I'm getting data from server in top level component and when I do get some specific data I want to rerender App component, so it has these new data, that came from server, but I can't think of how, because I can't use f.e. hooks on top level component. I'm aware, that I could just move all my listeners to App component and just do all the things there, but I don't like that it would send 4 times more request from server, because it's rerendering at the every state.
So I wanted to ask if there is a possibility to have my listeners at the top level component and just rerender the child components.
Here is my top level component code
import React from 'react';
import ReactDOM from 'react-dom';
import { GlobalStyle } from './components/global_styles';
import App from './components/App';
const ws = new WebSocket("ws://localhost:3001");
var token = JSON.parse(localStorage.getItem('loginToken') || '{}');
ws.onmessage = ({ data }) => {
const parsedData = JSON.parse(data)
switch (parsedData.type) {
case 'login-success':
console.log('logged in');
token = parsedData.user;
break;
case 'login-fail':
console.log(parsedData.error);
break;
}
};
ReactDOM.render(
<React.StrictMode>
<GlobalStyle />
<App ws={ws} token={token} />
</React.StrictMode>,
document.getElementById('root')
);
Specifically I'm talking about the 'token' variable, whenever it changes, it should rerender App component.
CodePudding user response:
If you want changes to token
trigger a rerender, it must be made a state variable.
One pattern that might fit your needs is using a Context
that saves the token and using it in your App
component
I would imagine it would be something like that
const TokenContext = React.createContext()
export const TokenProvider = ({children}) => {
const [token, setToken] = useState(JSON.parse(localStorage.getItem('loginToken') || '{}'));
useEffect(() => {
const ws = new WebSocket("ws://localhost:3001");
ws.onmessage = ({ data }) => {
const parsedData = JSON.parse(data)
switch (parsedData.type) {
case 'login-success':
console.log('logged in');
setToken(parsedData.user);
break;
case 'login-fail':
console.log(parsedData.error);
break;
}
};
return () => {/*whatever you need to do for cleanup, e.g. closing the connection*/}
},[]);
return (<TokenContext.Provider value={token}>{children}</TokenContext.Provider>)
}
export const useToken = () => useContext(TokenContext)
In your top level code add (after importing of course)
ReactDOM.render(
<React.StrictMode>
<TokenProvider>
<GlobalStyle />
<App ws={ws} token={token} />
</TokenProvider>
</React.StrictMode>,
document.getElementById('root')
);
And now you can utilize the useToken
hook wherever you need the token
like const token = useToken()
Alternatively, you can use a global state manager like Redux or MobX or one of the thousand others that exist for React and I can no longer keep track of
CodePudding user response:
inside your App
component simply use an useEffect
hook with token as a dependency as
useEffect(()=>{ /* token changed do something */ },[token])