Here I record the misuse of the useEffect I found in real-world projects.
Case 1: watch value via useEffect
This is the most common incorrect usage I have seen, mostly due to an incorrect understanding of why React renders and useEffect
itself.
Watch props
This is a component that receives a data
prop and sets the prop to state info
const Component = ({
data,
}) => {
const [info, setInfo] = useState(data);
useEffect(() => {
setInfo(data);
}, [data])
return <div>{info.name}</div>
}
This is pointless because React will re-render the component when its props change, and in any case, the props should not be changed in the child component.
besides, there will be at least 2 risks by doing this: Unpredictable behavior and Unexpected render
Here is the correct way to use props:
const Component = ({
data,
}) => {
- const [info, setInfo] = useState(data);
- useEffect(() => {
- setInfo(data);
- }, [data])
+ return <div>{data.name}</div>
}
Watch state
Sometimes you may want to watch a state and compute another value from it. Here is a wrong way to do so:
import { useState, useEffect } from 'react'; const App = () => { const [count, setCount] = useState(1); const [doubleCount, setDoubleCount] = useState(count * 2); useEffect(() => { setDoubleCount(count * 2); }, [count]); console.info('render'); return ( <div> <ul> <li>count: {count}</li> <li>doubleCount: {doubleCount}</li> </ul> <button onClick={() => setCount(count + 1)}>count + 1</button> </div> ); }; export default App;
This component defined two states, count
and doubleCount
, and makes count
as the dependence of the useEffect
. When the count
state changes, it will trigger a setter to set doubleCount
to count * 2
. I put an output to the console during the render stage to see how many times the component was rendered.
This seems to work fine, but if you check the console out you can see the component rendered twice every time you change the value of count. This is because when you mutate the count
state, the component will render, then the effect will be triggered and it will set doubleCount
state, and then the component will be triggered to re-render another time.
This is what I call the unnecessary render. To solve the problem, we can just define the double-count
as a variable during the render since it doesn’t have to be a state, and its change only depends on count
’s change.
import { useState, useEffect } from 'react'; const App = () => { const [count, setCount] = useState(1); const doubleCount = count * 2; console.info('render'); return ( <div> <ul> <li>count: {count}</li> <li>doubleCount: {doubleCount}</li> </ul> <button onClick={() => setCount(count + 1)}>count + 1</button> </div> ); }; export default App;