Search code examples
reactjsreact-memo

in React app profiling child re-rendering with memo


In the profiling result the component (Table) uses React.memo and it is shown that did not re-render

did not re-render

but underneath there is shown the same Table component and in Why did this render is mentioned that parent re-rendered

re-render

The Table uses memo this way and the querystring stays the same.

function areEqal(prevTable, nextTable) {
  return prevTable.queryString === nextTable.queryString;
}
const MemoTable = memo(Table, areEqal);
...
<MemoTable queryString={queryString} />

The question is, why the Table is shown twice (once not re-rendered, then yes) and why it re-renders when memoized to re-render just when querystring changes.


Solution

  • memo can only prevent renders related to props. Rendering may still happen if a context value that you're subscribed to changes. As discussed in the comments, you are apparently using a couple of contexts, and those are what's causing the render.

    While deleting the useContexts appears to have worked for your case, the fact that you weren't expecting this rerender may indidate that the DataContext.provider has a common mistake in which you're changing the context value on every render. If you're providing an object, you need to make sure to only create a new object when the properties of the object have actually changed. So for example, if your current code is this:

    const Example = ({ children }) => {
       const [userPreferences, setUserPreferences] = useState('something');
    
       return (
         <DataContext.Provider value={{ userPreferences, setUserPreferences }}>
           {children}
         </DataContext.Provider>
       );
    }
    

    ... then you should memoize the value like this:

    const Example = ({ children }) => {
       const [userPreferences, setUserPreferences] = useState('something');
       const value = useMemo(() => {
         return { userPreferences, setUserPreferences }
       }, [userPreferences]);
    
       return (
         <DataContext.Provider value={value}>
           {children}
         </DataContext.Provider>
       );
    }
    

    As for the two copies of Table in the dev tools, the first is the memoized component MemoTable. MemoTable then renders an unmemoized Table inside of it, and that's the second one. "Table (memo)" is the default name that memo gives to the component it returns. If you'd like to change that name so it shows up differently in the dev tools, you can:

    const MemoTable = memo(Table, areEqal); 
    MemoTable.displayName = "Hello world";