0

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?

  • 1
    The context value HAS changed. Because the value is defined inside the component and the component is a function. When a function runs, the values that are defined inside it get defined. 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, 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. – JMadelaine May 11 '20 at 20:16
  • 1
    I've added my comment as a more verbose answer. – JMadelaine May 11 '20 at 20:29

1 Answers1

6

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]);
JMadelaine
  • 2,859
  • 1
  • 12
  • 17