Using expressions as effect dependencies in React functional components
useEffect
hook, which binds together the behaviours of componentDidMount
, componentDidUpdate
, and componentWillUnmount
. It is used as such:useEffect(() => {
// This will run upon mounting or unmounting the component,
// as well as everytime it rerenders
});
Of course, this is very rarely what you want: most of the time, you want the effect to run when given props or state values change, which is done as follows:
const [bar, setBar] = useState("");
useEffect(() => {
// This will run when the component rerenders
// because one of the values listed below changed
}, [props.foo, bar]);
You can also pass
[]
as the second argument to limit the effect's scope to mounting and unmounting of the component.But you might also want to be nitpicking even more than that: in some situations, it could be ideal to have the effect run based on a computed value. Imagine a component A reading a value from a remote API, and supplying it to its child component B through props; B can then trigger a behaviour in A that would cause that value to change. It is a perfectly valid use case to want to process the first "change" (actually receiving the initial value with a delay) differently than the subsequent ones. Well, the following works just fine:
useEffect(() => {
// This will only run the first time props.foo receives a defined value
}, [props.foo !== undefined]);
This is a pretty helpful workaround to the fact we have no access to effect dependencies' previous values.
In my experience, if you find yourself writing a "complex" effect dependency such as this, half the time there is a simpler solution to your problem; but the other half, knowing this kind of thing is possible can come in handy (and it just makes sense: under the hood, React just matches the values against the previous ones to know whether it should run the effect again or not).
Last but not least, toying around with that dependency list is also useful if a value you depend on is an
Object
or an Array
, since there is a fair chance those are not mutated when their values change, and these therefore cannot be matched against the previous ones:useEffect(() => {
// This will only run if someArray's contents are actually different,
// even if the parent component overwrote it [thisWay, ...orSomething]
}, [JSON.stringify(someArray)]);
I would be interested to know if other people are using expressions in their effects' dependency lists, so you know what to do!