question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Preventing rerenders with React.memo and useContext hook.

See original GitHub issue

Do 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:closed
  • Created 5 years ago
  • Reactions:130
  • Comments:48 (11 by maintainers)

github_iconTop GitHub Comments

2250reactions
gaearoncommented, Mar 20, 2019

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 a theme property, and you want to only re-render some ExpensiveTree on appContextValue.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 but appContextValue itself changes too often, we could split ThemeContext from AppContext.

function Button() {
  let theme = useContext(ThemeContext);
  // The rest of your rendering logic
  return <ExpensiveTree className={theme} />;
}

Now any change of AppContext won’t re-render ThemeContext consumers.

This is the preferred fix. Then you don’t need any special bailout.

Option 2: Split your component in two, put memo in between

If 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.

function Button() {
  let appContextValue = useContext(AppContext);
  let theme = appContextValue.theme; // Your "selector"
  return <ThemedButton theme={theme} />
}

const ThemedButton = memo(({ theme }) => {
  // The rest of your rendering logic
  return <ExpensiveTree className={theme} />;
});

Option 3: One component with useMemo inside

Finally, 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 all useMemo inputs are the same.

function Button() {
  let appContextValue = useContext(AppContext);
  let theme = appContextValue.theme; // Your "selector"

  return useMemo(() => {
    // The rest of your rendering logic
    return <ExpensiveTree className={theme} />;
  }, [theme])
}

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.

45reactions
pumanitrocommented, Mar 20, 2019

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.

Read more comments on GitHub >

github_iconTop Results From Across the Web

4 options to prevent extra rerenders with React context
4 options to prevent extra rerenders with React context ; state · dispatch · = useContext(PersonContext); ; InnerPersonFirstName = React · memo(({ ...
Read more >
React context with hooks prevent re render - Stack Overflow
Use React.memo to prevent a re-render of a comp, if its own props haven't actually changed. ... Important: React.memo only checks prop changes...
Read more >
Prevent unnecessary re-rendering when using Context API
In this post we are going to prevent an unnecessary re-rendering when using Context API. Context API is great but we need to...
Read more >
React re-renders guide: everything, all at once - Developer way
There is no way to prevent a component that uses a portion of Context value from re-rendering, even if the used piece of...
Read more >
useContext React.memo? I have a question : r/reactjs - Reddit
I'm using the useContext hook to gain access to the value from the ... in more detail and gives advice on avoiding re-renders...
Read more >

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Hashnode Post

No results found