How to store socket-io-client instance to access through out the application

Viewed 2023

I am using react hook to create application, and socket.io-client for communication between flask and react, trying to access socket.io-client instance in different components of the application,

Please check below code, inside socket-Instance.js, created instance of socketio after connection and stored in store.js using dispatch,

in device.js, trying to access stored socket instance, and emit data on toggle,

But in socket-instance, i am trying to print console.log(socketInstance); this is coming as null

i wanted to create socketinstance once, and trying to emit the data from different components, using connected socket

using https://www.npmjs.com/package/socket.io-client this library

Index.js

import { StateProvider } from './shared/store';
const app = (
    <StateProvider>
        <AppRouting />
        <SocketInstance />
    </StateProvider>
);

store.js

import React, { createContext, useReducer } from 'react';

const initialState = {
    deviceStatus: []
};
const store = createContext(initialState);
const { Provider } = store;

const StateProvider = ({ children }) => {
    const [state, dispatch] = useReducer((currentState, action) => {
        const newState = { ...currentState };
        console.log(action.type);
     switch (action.type) {
      case 'socket':
                newState.websocket = action.currentState
                return newState

            default:
                throw new Error();
        };
    }, initialState);

    return <Provider value={{ state, dispatch }}>{children}</Provider>;
};

export { store, StateProvider }

socket-instance.js

mport React, { useEffect, useContext, useState } from 'react';
import { store } from '../shared/store';
import socketio from 'socket.io-client';
const ENDPOINT = "http://localhost:5000/";

function SocketInstance() {
    const { dispatch } = useContext(store);
    const [socketInstance, setSocket] = useState(null);

    useEffect(() => {

        const currentSocket = socketio.connect(ENDPOINT, {
            reconnection: true
        })

        currentSocket.on('connect', function (msg) {
            console.log("Server connected...");
        });

        currentSocket.on("disconnect", function () {
            console.log("server disconnected from client");
        });
        setSocket(currentSocket)

        console.log("=======set socket instance==========");

        console.log(socketInstance);


        dispatch({ type: 'socket', socket: socketInstance })


    }, [])

    return (
        <div >

        </div>
    )

}

export default SocketInstance;

device.js


function DeviceStatus() {

    const { state, dispatch } = useContext(store);
    const { deviceStatus, websocket } = state; //deviceCommand should be same as setting new state in store
    const [isEnable, setIsEnabled] = useState(false)
    const toggleSwitch = (checked) => {
        setIsEnabled(checked)
        dispatch({
            type: "updateDeviceStatus",
            deviceId: 21,
            deviceState: checked,
        })

        console.log("******before emit***************");
        console.log(deviceStatus);
        console.log("******check websocket ***************");
        console.log(websocket);
        if (websocket) {
            console.log("websocket instance can accessible");

            // websocket.emit("datafromui", deviceStatusFromUI)
        }
    }
return (
      <div className="devicestatus-item" >
                        <span>{DeviceStatusConst.FRONT_LL}</span>
                        <Switch checked={isEnable} onChange={toggleSwitch} />
                    </div>


  )
}
export default DeviceStatus

Help will very much appreciated, "Thanks in advance"

1 Answers

The useEffect for SocketInstance has an empty dependency array, which means it will only run once after the first render. It's logging null because the state doesn't update until it the NEXT render.

As far as using the same instance across components, you can simply define currentSocket outside of your component (or in a separate file and export it) and pass it down as props. Even better, you could use context instead. Also, I recommend cleaning up your event listeners whenever the SocketInstance component unmounts.

CreateContext.js

import React from 'react'
import socketio from 'socket.io-client'

export const socket = socketio('http://localhost:5000/')
const SocketContext = React.createContext(socket)

export const SocketProvider = SocketContext.Provider
export const SocketConsumer = SocketContext.Consumer
export default SocketContext

Index.js

import { StateProvider } from './shared/store'
import { SocketProvider } from './shared/context/SocketContext'

const app = (
    <StateProvider>
      <SocketProvider>
        <AppRouting />
          <SocketInstance />
      </SocketProvider>
    </StateProvider>
)

socket-instance.js

import React, { useEffect, useContext, useState, useCallback } from 'react'
import SocketContext from './shared/context/SocketContext'

const SocketInstance = () => {
  const currentSocket = useContext(SocketContext)

  const listener = useCallback(message => {
    console.log(message)
  }, [])

  useEffect(() => {
    console.log(currentSocket)

    currentSocket.on('some-event', listener)
    return () => currentSocket.off('some-event', listener)
  }, [])

  return (
    <div></div>
  )
}
Related