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
It’s mostly a question of the desired encapsulation of
Child
. If it is a reusable thing that should be agnostic of whyparentVal
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.
Like @adsy said, I will add that in order to get rid of the
useEffect
hook, you may useuseMemo
for a better approach in my opinion. (https://react.dev/reference/react/useMemo)You can use it similar like this:
Of course, if you have other functions implemented that need to change the
childVal
value, like a button or something else within thechild component
, it is a better approach to useuseEffect
and useuseState
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.