The useEffect
hook lets us watch the values of states and props and do something according to their values.
Sometimes we may only want to run the useEffect
callback when a state value has been updated.
In this article, we’ll look at how to only run the useEffect
hook callback only when a state value is updated.
Run the useEffect React Hook Callback Only on State Update
To run the useEffect
hook callback only when a state is updated, we can create a ref to keep track of when a state is updated.
For instance, we can write:
import { useEffect, useRef, useState } from "react";
export default function App() {
const isInitialMount = useRef(true);
const [count, setCount] = useState(0);
useEffect(() => {
if (isInitialMount.current) {
isInitialMount.current = false;
} else {
console.log(count);
}
});
return (
<form>
<button onClick={() => setCount((c) => c + 1)}>increment</button>
</form>
);
}
We create the isInitialMount
ref with the useRef
hook which is set to true
initially.
Then we create the count
state which we want to log only when count
is updated.
In the useEffect
callback, we check is isInitialMount.current
is true
.
If it’s true
, then we set initialMount.current
to false
.
Refs are non-reactive, so it won’t cause a re-render of the component.
However, the current
value persists across renders.
When isIninitialMount.current
is false
, we log the value of count
.
Below that, we have a button that calls setCount
to update the count
value when we click it.
Now when we click the increment button, we see count
is logged starting when it’s 1, which means it’s updated beyond its initial value.
We can also extract the logic into its own hook.
For instance, we dcan write:
import { useEffect, useRef, useState } from "react";
const useUpdateEffect = (effect, dependencies = []) => {
const isInitialMount = useRef(true);
useEffect(() => {
if (isInitialMount.current) {
isInitialMount.current = false;
} else {
effect();
}
}, dependencies);
};
export default function App() {
const [count, setCount] = useState(0);
useUpdateEffect(() => {
console.log(count);
}, [count]);
return (
<form>
<button onClick={() => setCount((c) => c + 1)}>increment</button>
</form>
);
}
We have the useUpdateEffect
hook that takes the effect
function, which we want to run when anything in the dependencies
array change.
We move the isInitialMount
ref to the hook.
And we call useEffect
in useUpdateEffect
hook instead of the App
component.
This lets us reuse the logic anywhere with ease.
Conclusion
We can add a ref to keep track of when a component is updated when running the useEffect
callback code we want accordingly when it does.
This lets run the useEffect
callback only when the component is updated.