DEV Community

Adam Smolenski
Adam Smolenski

Posted on

Time to time

So last time I was returning socket information through a custom hook... Not even telling you what a custom hook was or why I was using it... silly me. So here we will cover that.
So why a hook? Well These are functions that I was going to need across components that do entirely different things. As mentioned there were different levels of Time Control, but they all needed to be monitored from different components. By creating a custom hook I can have those functions available to me in all sorts of places. Think of it as a reverse Higher Order Component. I'm not imprinting a component on it, but taking a function from below.

Warning.. same rules apply to regular hooks, you need to be using them in functional component, they can be part of any conditional logic and they have to start with "use". Useful rules aren't they?

Everyone needed a baselevel connection but needed different functionality from this component as well. Stage Managers, assistants, audience members as well as the production team all needed to be able to view the time but only certain people can manipulate time, whether or not for production or test. So the whole thing looked like:

import { useEffect, useRef, useState } from "react";
import socketIOClient from "socket.io-client";

const START_TIMER = "startTimer"
const PAUSE_TIMER = "pauseTimer"
const SET_TIMER = "setTimer"
const RESET_TIMER = "resetTimer"
const GET_TIMER = "getTimer"
const RESET_STORAGE = "resetStorage"
const RESET_TRACK = "resetTrack"
const SOCKET_SERVER_URL = `/`;

const useTimer = () => {
    let [browserTimer, setBrowserTimer] = useState({})
    const socketRef = useRef();

    useEffect(() => {
        socketRef.current = socketIOClient(SOCKET_SERVER_URL, {
            query: {roomId:"Info"}
        });

        socketRef.current.on("startBrowserTimer", (data) => {
            setBrowserTimer(data)
        });
        socketRef.current.on("resetBrowserTimer", (data) => {
            setBrowserTimer(data)
        });
        socketRef.current.on("pauseBrowserTimer", (data) => {
            setBrowserTimer(data)
        });
        socketRef.current.on("setBrowserTimer", (data) => {
            setBrowserTimer(data)
        });

        socketRef.current.on("getBrowserTimer", (data) => {
            setBrowserTimer(data)
        });

        return () => {
            setBrowserTimer({ })
            socketRef.current.disconnect();

        };
    }, []);

    const startTimer = () => {
        socketRef.current.emit(START_TIMER)
    }

    const setTimer = (minutes, seconds) => {
        let intoSeconds = (parseInt(minutes)  * 60) + parseInt(seconds)
        socketRef.current.emit(SET_TIMER, {body:{newTimer: intoSeconds }})
    }

    const getTimer = () =>{
        socketRef.current.emit(GET_TIMER)
    }
    const pauseTimer = () => {
        socketRef.current.emit(PAUSE_TIMER)
    }

    const resetTimer = () => {
        socketRef.current.emit(RESET_TIMER)
    }



    return ({ startTimer, setTimer, resetTimer, pauseTimer, browserTimer, getTimer });
};


export { useTimer };
Enter fullscreen mode Exit fullscreen mode

Boy that's a lot of info. Let's start from the top... you are probably wondering why I'm in a room called info... Well I wanted to know who was connected to the timer, so that is a different component that gets the userlist to make sure our audience is in sync. Also why am I adding the accuracy check? Due to not re-rendering the hook unless new information is set, in the React component I set it to false after the Timer is read and set. That way, if the component re-renders it doesn't reset to false info.

All the socket.current.on are on the incoming messages, so this is all information generated from the backend which we will get to momentarily. The browser timer information is what we will be processing. It's based on the timer on the server with some extra information.

So let's look at the returns now since those house most of our functions. Everyone will have access to browserTimer and getTimer, that's we return these as a hash so we can destructure in react.

On the backend we add on from last time.

const startTimer = () => {
            clearInterval(timerInterval)
            delete (timerInterval)
            timerInterval = setInterval(() => {
                timer = timer + 1
            }, 1000)
            io.sockets.emit("startBrowserTimer", { timer, rolling: true, accurate: true })
        }
        const getTimer = () => {
            let rolling
            if (timerInterval && timerInterval._destroyed){
                rolling = false
            }else if(!timerInterval){
                rolling = false
            }else{
                rolling = true
            }
            io.sockets.emit("getBrowserTimer", { timer, rolling, accurate: true })
        }

        const resetTimer = () => {
            clearInterval(timerInterval)
            delete (timerInterval)
            timer = -1
            io.sockets.emit("resetBrowserTimer", { timer, rolling: false, accurate: true  })
        }

        const pauseTimer = () => {
            clearInterval(timerInterval)
            delete(timerInterval)
            io.sockets.emit("pauseBrowserTimer", { timer, rolling: false, accurate: true })
        }

        const setTimer = (newTime) => {
            timer = newTime
            if (timerInterval && timerInterval._destroyed) {
                rolling = false
            } else if (!timerInterval) {
                rolling = false
            } else {
                rolling = true
            }
            io.sockets.emit("setBrowserTimer", { timer, rolling, accurate: true })
        }

        socket.on("getTimer", (data)=>{

            getTimer()
        })

        socket.on("startTimer", (data) => {
            startTimer()
        })
        socket.on("pauseTimer", (data) => {
            pauseTimer()

        })

        socket.on("setTimer", (data) => {
            setTimer(data.body.newTimer)

        })

        if (timer > 7800) {
            resetTimer()
        }

        socket.on("resetTimer", data => {
           resetTimer()
        })
Enter fullscreen mode Exit fullscreen mode

So here we have a bunch more info to go through, startTimer is the best place to.... start. We are clearing on the beginning to ensure there is not a double Go. Otherwise it is setting our server timer and sending that information back through sockets for our users to then process further in React once it is passed back from the hook. But you can now see that the information sent back mainly consists or telling us if the clock is rolling where we are at, and that this information is up to date (accurate). Get timer just receives the data, reset sets the timer to -1 (this was done because that is outside of the cuing which goes up to 2 hours and 10 minutes, or 7800 seconds), you can also see that is called if the time is over that 7800 second mark, that way our server isn't randomly counting once the show is over. Set timer on the frontend (the hook) you can see is converted to seconds, SMs are too busy to think in seconds while processing show info. However, our server handles seconds best so we make that conversion in the Frontend and just change the timer in the backend.

So these are all the basic commands that I used to control time through sockets in order to sync up the audience. We will continue putting it all together next time.

Top comments (0)