Raghav Sharma
Raghav Sharma

Reputation: 140

How to execute useEffect hook callback only when a single value from the hook callback changes?

It is being mentioned in the react documentation that every thing that is supposed to be changed needs to be present in the dependency array of the useEffect hook.

I could make use of // eslint-disable-next-line react-hooks/exhaustive-deps but this is not the ideal way to do it I think.

But what would you do if you want to trigger a Side Effect only when a certain state changes? Not the other things being used to it?

I have a workaround but that doesn't works if you have multiple side effects listening to unique states.

const [state1, setState1] = useState(1);
const [state2, setState2] = useState(2);

const fetchDataWithState = useCallback(() => {
  action.fetchData({
    state1,
    state2,
  })
}, [state1, state2])

// Effect to listen to the changes of state1
useEffect(() => {
  // Some random work related to state1
  fetchDataWithState()
}), [fetchDataWithState, state1])

// Effect to listen to the changes of state2
useEffect(() => {
  // Some random work related to state2
  fetchDataWithState()
}), [fetchDataWithState, state2])

The above code doesn't work if there are multiple side effects, each specific for a particular state.

If state1 gets changed, the fetchDataWithState will have a different reference, so it will lead to execute the callback in the second useEffect which was supposed to triggered only when the state2 changes.

Or should I use // eslint-disable-next-line react-hooks/exhaustive-deps by not passing fetchDataWithState it in the dependency array.

Upvotes: 1

Views: 1189

Answers (1)

jsejcksn
jsejcksn

Reputation: 33701

Store the previous value of state1 in a ref, and only invoke the function if state1 actually changes:

const [state1, setState1] = useState(1);
const [state2, setState2] = useState(2);
const state1PreviousRef = useRef(0); // Any initial value that's the same type as state1, but not the same value

// only invoke action.fetchData if state1 changes:
useEffect(() => {
  // state1 changed from last time
  if (state1 !== state1PreviousRef.current) {
    action.fetchData({state1, state2});
  }

  // update previous to current
  state1PreviousRef.current = state1;
}, [
  state1,
  state2,
  action, // might or might not be necessary, depending on where this is defined (you don't show this in your example)
  state1PreviousRef, // not necessary, but **actually** exhaustive
]);

Upvotes: 2

Related Questions