skip to Main Content

Is it appropriate to use useEffect to fetch new state when a prop changes? For example, like this:

const Parent = () => {
  const [parentVal, setParentVal] = useState(0);
  const updateParentVal = () => {
    setParentVal((v) => v + 1);
  };

  return (
    <div style={{ border: "1px solid red", padding: 10 }}>
      <span style={{ marginRight: 10 }}>parentVal: {parentVal}</span>
      <button onClick={updateParentVal}>increment</button>
      <Child parentVal={parentVal} />
    </div>
  );
};

const Child = ({ parentVal }) => {
  const [childVal, setChildVal] = useState(-2);
  const fetchNextChildVal = (currChildVal) => Promise.resolve(currChildVal + 2);

  useEffect(() => {
    (async () => {
      const nextChildVal = await fetchNextChildVal(childVal);

      setChildVal(nextChildVal);
    })();
  }, [parentVal]);

  return (
    <div style={{ border: "1px solid blue", margin: 30, padding: 10 }}>
      <span>childVal: {childVal}</span>
    </div>
  );
};

export default function App() {
  return <Parent />;
}

Even after reading through You Might Not Need an Effect from the React docs, it’s not clear to me. Various sections discourage using useEffect to update state, which makes me think that the proper way to handle this situation would be to fetch the data in updateParentVal and pass it down into Child.

In particular, this excerpt from the "Sharing logic between event handlers" section:

When you’re not sure whether some code should be in an Effect or in an event handler, ask yourself why this code needs to run. Use Effects only for code that should run because the component was displayed to the user. In this example, the notification should appear because the user pressed the button, not because the page was displayed! Delete the Effect and put the shared logic into a function called from both event handlers:

I know the section is talking about shared logic between more than one event handler, but it feels to me like it’s making a broader point about it being more appropriate to fetch in the event handler.

However, the "Fetching data" section at the end sounds like it’s saying that it is appropriate to fetch data in response to a prop changing. So then, I’m not actually sure what the best practice is.

2

Answers


  1. It’s mostly a question of the desired encapsulation of Child. If it is a reusable thing that should be agnostic of why parentVal changed, then yes it might be appropriate.

    That tends to be the case if the component is going to be used in unknown situations, such as a lib, or something that’s used a lot in your app to the point where you need the decoupling.

    But if you do know about how that changes (button click) ahead of time, then yes arguably you can utilise that to remove the effects which is likely to lead to (pretty minor) perf improvements.

    Login or Signup to reply.
  2. Like @adsy said, I will add that in order to get rid of the useEffect hook, you may use useMemo for a better approach in my opinion. (https://react.dev/reference/react/useMemo)

    You can use it similar like this:

    const childVal = useMemo(() => parentVal * 2, [parentVal]);
    

    Of course, if you have other functions implemented that need to change the childVal value, like a button or something else within the child component, it is a better approach to use useEffect and use useState for that, but for something simple, that you do not change directly and only changes after some of the props changes, is a good approach.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search