Search code examples
reactjstypescriptreact-hooksuse-effectreact-context

React real-time chart with react.context and socket.io


I'm trying to build a website for displaying real-time charts in typescript with react for my learning. But I can not get values from a server properly before displaying the chart.

What I want to do is ...

  1. Communication protocol is websocket using socket.io.
  2. Using socket.io and storing data are inside React.Context(useSocket.tsx) so as to access the data from any react components easily.
  3. Displaying the data is in Home.tsx.
  4. The socket events are initial_data and new_data.
  5. The initial_data event is received at the time the accessing the website at first.
  6. The new_data event is received at regularly.
  7. The time getting both events above, update values inside Home.tsx automatically.

I researched some articles on the web, for example, explaining a way that using socket.io inside a useEffect() function that returning socket.disconnect().
So, the code I built is below.

useSocket.tsx

import {useContext, createContext, useState, useEffect} from "react";
import {io, Socket} from "socket.io-client";
import {chartDataType} from "../types/chartDataType";

type Context = {
    chartData: Array<chartDataType>;
}

const SocketContext = createContext<Context>({
    chartData: [],
});

const SocketsProvider = (props: any) => {

    const [chartData, setChartData] = useState();

    useEffect( () => {
        const socket: Socket = io("http://***.***.***.***");

        socket.on('initial_data', (data) => {
            console.log(data);
            setChartData(data);
        });

        socket.on('new_data', (data) => {
            console.log(data);
        });

        return () => { socket.disconnect() };
    },[]);

    return (
        <SocketContext.Provider value={{chartData}} {...props} />
    );
}

const useSocket = () => useContext(SocketContext);

export { SocketsProvider, useSocket };

Home.tsx

import {memo, VFC} from "react";
import { useSocket } from "../../context/useSocket";
import {Heading} from "@chakra-ui/react";

export const Home: VFC = memo(() => {
    const { chartData } = useSocket();

    return (
        <>
            <Heading as="h1">{`${chartData}`}</Heading>
        </>
    )
})

The above code caused an error Uncaught TypeError: Cannot read properties of undefined (reading '0') occurred in the browser console. But when the comment out the <Heading>...</Heading> line in Home.tsx, the console.log in useSocket.tsx can display the value from the server in the browser console.

I can not come up with the idea for the correct implementation. Is the definition of the type of the chartData wrong? or other reasons? The definition of the chartDataType has nothing wrong.
What is the way for the correct implementation?


Solution

  • What's happening is you are trying to render an empty array, the data hasn't loaded yet.

    You need to check if charData exists, or if it's undefined first.

    Like this:

    return ( 
       {CharData ? <Heading /> ... : null }
    )