Sreya Rajendran
Sreya Rajendran

Reputation: 23

useContext value changes on rerenders

So I have a component that looks something like

const App = () => {
  const someContextValue = useSomeContext();  //custom hook that calls useContext

  useEffect(() => {
    someContextValue()
  }, [someContextValue]);

  return <div />
}

Whenever the component rerenders, the useEffect is triggered even though someContextValue hasn't really changed.

I got around this by using useMemo like

const someContextValue = useMemo(useSomeContext, [useSomeContext])

Now someContextValue doesn't change on rerenders. But I feel like this isn't quite right. What would be the right way to do this?

Upvotes: 1

Views: 2509

Answers (1)

JMadelaine
JMadelaine

Reputation: 2964

If you're returning an object {} or array [] from the context, then the context value HAS changed.

The someContextValue variable is defined inside the component.

The component is a function, and when a function runs, the values that are defined inside it get defined. If for example, your context returns an object containing state values, then that object is a different instance to the one from the previous render, and your useEffect runs because of that.

useEffect will compare reference-types by their instance and not the values inside them.

When you call useSomeContext(), you're creating a new object, and this happens every time the component renders.

This is not the same as a state value from useState, where the state value does not get redefined. The useState hook maintains the same instance of the value, therefore it is not recreated every time, and the useEffect sees that the state value is the same instance.

This is why, when using context, you should destructure the context object and refererence the values inside the object, which are either state values passed from a useState hook inside the context, or a value defined inside the context that does not get redefined when your consuming component re-renders (because the context does not re-render in that case):

const { someStateValue } = useSomeContext()

useEffect(() => {
  someStateValue()
}, [someStateValue]);

Upvotes: 9

Related Questions