My <App />
component has 2 pieces of state. The first piece of state updates on user interaction and then triggers an update in the second piece of state using the useEffect()
hook. This causes 2 re-renders anytime that the user clicks on the button. Can someone help me figure out how to only rerender <App />
after the second piece of state updates? Is this a bad react pattern?
Here is the codesandbox: https://codesandbox.io/s/wispy-butterfly-nm5fsr
You can view the console to see the re-renders.
The reason I need to fix this is because I have a real project where re-renders are happening and very large props are being passed between components. The re-renders are causing visible lag. I would share the entire project, but it's way too much for a stack overflow question.
It's rendering twice because you're updating state once per render, instead of updating both states at the same time. Consider what you're doing here:
const handleClick = () => {
if (state1) setState1((prev) => prev + 1);
else setState1(1);
};
useEffect(() => {
if (state1) setState2(state1 + 10);
}, [state1]);
This basically means:
Update
state1
and re-render. On that next render, updatestate2
and re-render.
The calculation to derive the state updates isn't terribly complicated. Add 1
to a number, and add 10
to a number. You don't need to wait for the component to re-render to know what state1
will be. It will be the current value plus 1, which is easily calculated. You can use that calculation to update both states:
const handleClick = () => {
if (state1) {
const newState1 = state1 + 1;
setState1(newState1);
setState2(newState1 + 10);
} else {
setState1(1);
setState2(10);
}
};
Or, even better, you don't need two state values in the first place. In this example, state2
is derived from state1
(and doing so is not a difficult or expensive calculation). Which means you're duplicating state.
Don't duplicate state. Just derive the second value from state1
:
const [state1, setState1] = useState(null);
const handleClick = () => {
if (state1) setState1((prev) => prev + 1);
else setState1(1);
};
return (
<div className="App">
{console.log("Rerendering children...")}
<Child1 state1={state1} />
<Child2 state2={state1 ? state1 + 10 : null} />
<button type="button" onClick={handleClick}>
Change State 1!
</button>
</div>
);