Skip to content

Using use-State hook in react function always throws an error + How to share a variable with another component

I’m a beginner to programming in Javascript/Typescript and just about to develop my very first app in react. To get some data from the backend (FastAPI) I created the function “GetData” that is executed whenever the button is clicked.

Component 1: Button import { GetData } from “./socket”

export default function Button() {
    return (
        <button onClick={GetData}>
            Run
        </button>
    )
}

Component 2 (named socket): websocket and data logic

import {useState, createContext} from 'react';

let socket: any;

export async function ConnectWebsocket() {
    socket = new WebSocket("ws://127.0.0.1:8000/");

    socket.onopen = () => {
        console.log('connected')
    }

    socket.onclose = () => {
        console.log('closed')
    }

    socket.onerror = () => {
        console.log('error')
    }
}

export async function GetData() {
    const [data, setData] = useState({});
    
    socket.send("get Data");
    
    socket.onmessage = (event: any) => {
        const newData = JSON.parse(event.data);
        console.log(`Data from server: ${newData}`);
        setData((data) => ({ ...data, ...newData }));
    }

    console.log(`Overall data: ${data}`);
}

The problem I’m facing is the useState hook. Whenever I try to stream the data via the websocket by clicking the Run-Button I always get the following error:

Uncaught (in promise) Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:

  1. You might have mismatching versions of React and the renderer (such as React DOM)
  2. You might be breaking the Rules of Hooks
  3. You might have more than one copy of React in the same app See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.

I also created a small example using useState-hook and this one worked. Can you spot what I messed up in my code above?

Plus I have another beginner question. How would you make the “data” variable available to a third component (e.g. a table)?

Answer

You can only use react hooks inside the actually react component. In your case your Button component. So I would do something like this instead:

   class SocketHelper {
    socket = new WebSocket("ws://127.0.0.1:8000/");

    constructor() {
        
        socket.onopen = () => {
            console.log('connected')
        }

        socket.onclose = () => {
            console.log('closed')
        }

        socket.onerror = () => {
            console.log('error')
        }
    }
}

export const socketHelper = new SocketHelper();


export default function Button() {
    const [data, setData] = useState({});

    useEffect(() => {

        socketHelper.socket.onmessage = (event: any) => {
            const newData = JSON.parse(event.data);
            console.log(`Data from server: ${newData}`);
            setData((data) => ({ ...data, ...newData }));
        }
    }, []);

    const getData = () => {
        socketHelper.socket.emit("getdata");
    }

    return (
        <div>
            <button onClick={getData}>
                Run
            </button>
            <pre>{JSON.stringify(data, null, 2)}</pre>
        </div>
    )
}

Also you are using socket.send but it doesn’t seems like your are using the returned socket. So instead i would use the emit function: https://socket.io/docs/v4/client-api/#socketemiteventname-args