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:
- You might have mismatching versions of React and the renderer (such as React DOM)
- You might be breaking the Rules of Hooks
- 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)?
Advertisement
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