DEV Community

loading...

Custom Hooks and Sockets

Afroze Kabeer Khan. M
Full stack Developer. Node Js, JavaScript & React
・4 min read

So, it's been a while since the hooks came in and I've been trying to find time to try and learn to create custom hooks. Really mind-boggling 🧠. Though the interesting part is that the concept may be simple but if you find the right use case to it.

The first thing i wanted to try out was to make network request and to see how we can implement using hooks. The usual way in react was obviously setState. Assuming that most of you have worked with setState. Now let's see how to write it in a functional component using Hooks.

For this let's use JSON placeholder to fetch posts.

import React,{ useState } from "react";

// URL to fetch posts
const postsUrl = "https://jsonplaceholder.typicode.com/posts"

// Our Component
const ListPosts = () => {
    // Using state hook to set if loading
    const [ loading, setLoading ] = useState(true);
    // Fetch and set list of posts
    const [ posts, setPosts ] = useState([]);

    //Here we fetch the posts and add it to posts state
    fetch(postsUrl)
        .then(res => res.json())
        .then(data => { setPosts(data); setLoading(false); })
        .catch(err => {
            alert('Error fetching posts');
            setLoading(false);
        });

    //Our Component which will lists posts
    return(
        <div>
            // Loop through the list of posts
            <h1>My Posts <span>{posts.length}</span></h1>
            {loading ? 
                <h1>Loading posts...</h2> 
                : posts.map(post => (
                    <div>
                        <h3>{post.title}</h3>
                        <hr/>
                        <p>{post.body}</p>
                    </div>
            })}
        </div>
    )
}
Enter fullscreen mode Exit fullscreen mode

In this component we used useState hook to set posts and loading state. But now let's see how we can rewrite this as a hook separating the logic from component.

First let's use the effect hook which will react to changes inside the component. Let's check it out.

React.useEffect(() => {
    // Function called whenever something updates in component
},[ /* Update based on variable */ ])
Enter fullscreen mode Exit fullscreen mode

Now let's see how to use it in our component

import React,{ useState } from "react";

// URL to fetch posts
const postsUrl = "https://jsonplaceholder.typicode.com/posts"

// Our Component
const ListPosts = () => {
    // Using state hook to set if loading
    const [ loading, setLoading ] = useState(true);
    // Fetch and set list of posts
    const [ posts, setPosts ] = useState([]);

    // Use effect to update component
    React.useEffect(() => {
        //Here we fetch the posts and add it to posts state
        fetch(postsUrl)
            .then(res => res.json())
            .then(data => { setPosts(data); setLoading(false); })
            .catch(err => {
                alert('Error fetching posts');
                setLoading(false);
            });
    },[ postsUrl ])

    //Our Component which will lists posts
    return(
        <div>
            // Loop through the list of posts
            <h1>My Posts <span>{posts.length}</span></h1>
            {loading ? 
                <h1>Loading posts...</h2> 
                : posts.map(post => (
                    <div>
                        <h3>{post.title}</h3>
                        <hr/>
                        <p>{post.body}</p>
                    </div>
            })}
        </div>
    )
}
Enter fullscreen mode Exit fullscreen mode

Now let's separate the logic from the application

// Here is our hook which makes request and returns the posts
const usePosts = (postUrl) => {
    const [ loading, setLoading ] = useState(true);
    const [ posts, setPosts ] = useState([]);

    React.useEffect(() => {
        //Here we fetch the posts and add it to posts state
        fetch(postsUrl)
            .then(res => res.json())
            .then(data => { setPosts(data); setLoading(false); })
            .catch(err => {
                alert('Error fetching posts');
                setLoading(false);
            });
    },[ postsUrl ])

    return { loading, posts }
}

// Adding it to our component
const postsUrl = "https://jsonplaceholder.typicode.com/posts";

const ListPosts = () => {
    const { loading, posts } = usePosts(postsUrl);

    return(  
        <div>  // Loop through the list of posts  
            <h1>My Posts <span>{posts.length}</span></h1>  
            {loading ?  
                <h1>Loading posts...</h2>
                : posts.map(post =>  (
                <div>
                    <h3>{post.title}</h3>  
                    <hr/>
                    <p>{post.body}</p>
                </div>
                })}  
        </div>)
}
Enter fullscreen mode Exit fullscreen mode

So there we go, we have created our first simple custom hook. This is okay if we are going to use static requests to fetch data. What if we are going to recieve a stream of data based on events.
Let's see how we can write the same component using a socket to fetch temperature from an IoT Device. Which will constantly send data.

For this I've created a sample code that streams a random temperature between 0 and 100 using express and socket-io. The code is as below.

// server.js
const app = require('express')();
const server = require('http').createServer(app);
const socket = require('socket.io');
const io = socket(server);

const port = 8080 || process.env.PORT;

io.on('connection', () => {
    console.info('SOME ONE IS HERE');
});

setInterval(() => {
    const temp = Math.floor(Math.random()* 100);
    const topic = 'temperature';
    console.info(`TEMP : ${temp}`);
    io.emit(topic,temp);
}, 3000);

const listenCb = () => console.table([['status', 'port'],['started',port]])
server.listen(port, listenCb);
Enter fullscreen mode Exit fullscreen mode

Install the dependencies and run the server using the follwoing code

npm i -S socket.io express

# Run the app using nodejs
node app.js
Enter fullscreen mode Exit fullscreen mode

This will constantly send the data every 3 seconds.

Now let's see how to use this in our React's component using custom hooks. First let's write our hook.

import React from 'react';
import PropTypes from 'prop-types';
import socket from 'socket.io-client';

// Use socket to fetch request to data 
// Socket server's url and topic in which data is sent
const useSocket = (serverUrl, topic) => {
    const [temp, setTemp] = React.useState(0);
    const [isConnected, setConnected] = React.useState(false);

    React.useEffect(() => {
        const client = socket.connect(serverUrl);
        client.on("connect", () => setConnected(true));
        client.on("disconnect", () => setConnected(false));
        client.on(topic, (data) => {
            setTemp(data);
        })
    }, [serverUrl, topic, isConnected]);

    return { temp, isConnected };
}

// Our component which sends the request through the topic
const Sockt = () => {
    const serverUrl='http://localhost:8080', topic='temperature';
    const { temp, isConnected } = useSocket(serverUrl, topic);

    return (
        <div>
            <h4>Temperature</h4>
            <h1>{temp}</h1>
            <h3>{`CONNECTED: ${isConnected}`}</h3>
        </div>
    )
}
Enter fullscreen mode Exit fullscreen mode

The component now will show the temperature which is received from the socket server. Every time data is sent through the socket.

I hope, you got to learn something new and interesting. Find the code sandbox for the implementation of what we went through this entire article.

Backend - Socket Server 🧦
https://codesandbox.io/s/express-socket-io-70t5x

React using hooks
https://codesandbox.io/s/priceless-greider-3b814

Discussion (0)