Reputation: 23
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
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