Preventing rerenders with React.memo and useContext hook.
See original GitHub issueDo you want to request a feature or report a bug?
bug
What is the current behavior?
I can’t rely on data from context API by using (useContext hook) to prevent unnecessary rerenders with React.memo
If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem. Your bug will get fixed much faster if we can run your code and it doesn’t have dependencies other than React. Paste the link to your JSFiddle (https://jsfiddle.net/Luktwrdm/) or CodeSandbox (https://codesandbox.io/s/new) example below:
React.memo(() => {
const [globalState] = useContext(SomeContext);
render ...
}, (prevProps, nextProps) => {
// How to rely on context in here?
// I need to rerender component only if globalState contains nextProps.value
});
What is the expected behavior?
I should have somehow access to the context in React.memo second argument callback to prevent rendering Or I should have the possibility to return an old instance of the react component in the function body.
Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React? 16.8.4
Issue Analytics
- State:
- Created 5 years ago
- Reactions:130
- Comments:48 (11 by maintainers)
This is working as designed. There is a longer discussion about this in https://github.com/facebook/react/issues/14110 if you’re curious.
Let’s say for some reason you have
AppContext
whose value has atheme
property, and you want to only re-render someExpensiveTree
onappContextValue.theme
changes.TLDR is that for now, you have three options:
Option 1 (Preferred): Split contexts that don’t change together
If we just need
appContextValue.theme
in many components butappContextValue
itself changes too often, we could splitThemeContext
fromAppContext
.Now any change of
AppContext
won’t re-renderThemeContext
consumers.This is the preferred fix. Then you don’t need any special bailout.
Option 2: Split your component in two, put
memo
in betweenIf for some reason you can’t split out contexts, you can still optimize rendering by splitting a component in two, and passing more specific props to the inner one. You’d still render the outer one, but it should be cheap since it doesn’t do anything.
Option 3: One component with
useMemo
insideFinally, we could make our code a bit more verbose but keep it in a single component by wrapping return value in
useMemo
and specifying its dependencies. Our component would still re-execute, but React wouldn’t re-render the child tree if alluseMemo
inputs are the same.There might be more solutions in the future but this is what we have now.
Still, note that option 1 is preferable — if some context changes too often, consider splitting it out.
Maybe a good solution for that would be to have the possibility of “taking” the context and rerender component only if given callback return true e.g:
useContext(ThemeContext, (contextData => contextData.someArray.length !== 0 ));
The main problem with hooks that I actually met is that we can’t manage from inside of a hook what is returned by a component - to prevent rendering, return memoized value etc.