Eevee

Bad use case of useEffect

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:

Code Playground
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.

Code Playground
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;