react: Reasons React would be calling render() when memo returns false
Do you want to request a feature or report a bug?
More of a good place to document some behavior.
What is the current behavior?
It’s hard to isolate why this happens because it’s something inside React that’s making this decision. Perhaps context is changing. But my biggest frustrations lately have been due to renders happening when they shouldn’t, or seem like they shouldn’t.
For example I can have a component like so:
const Component = React.memo(() => <div />, (a, b) => true)
And I’ll find it calling render quite often in a performance sensitive area.
It would be helpful to have docs somewhere that really go into why a component render is being called.
Further, it would be even more helpful to have a sort of “debug stream” you could hook into to see React logging reasons why things are updated in a clean fashion. That this doesn’t exist already is a bit of a mystery. It would have saved a ridiculous amount of time for myself over the last years, and I’m sure many others. It would look something like:
(render) ComponentName [props changed]
(render) OtherComponentName [props changed] [state changed]
Even better would be to show some sort of tree structure. And show the results of memo/shouldComponentUpdate, and why they may have been ignored:
(render) ComponentName [memo: true | ignored due to context change in: SomeContext]
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 10
- Comments: 24 (6 by maintainers)
Hey, my comment might not help you but I hope it does a little. I was just battling the same issue.
I have nested routing in my app and I was trying to get my
<Header />component not to refresh when its only prop wasn’t changing, yet it kept re-rendering. Mymemo()always returned true.I put a console log at every route/layout wrapper level of my App (I’m a debugging master right 💪) and found out that my wrapping layout component is re-rendering all the time because it has
historyprop (although I’m not using it at all), simply because the component is a<Route />in a<Switch />so thehistoryprop kept changing and my Layout was re-rendered all over - and it contained the poor<Header />.From my experience, I can say that
memo()indeed is working right. However, if your parent components re-render, there’s not much your poor memoized child can do about it.Have you solved this issue? Debugging every level of my app helped a lot, I saw many unnecessary renders in my wrapping layouts when they didn’t need to change at all, because I wasn’t even going to the different parts of the app that use different wrappers.
Hey! I just debugged my app when it re-rendered
React.memoed components, and found out I just made a stupid mistake. So here’s a heads-up if anyone else runs into the same problem.When I declare a function like this:
const Cmp = () => { return <div />; };, then the component will be named “Cmp” (the variable name) in the React DevTools “⚛️ Components” tab. However, when I wrap it in aReact.memocall like this:const Cmp = React.memo(() => { return <div />; });, then the component will be named “AnonymousMemo” instead. As a workaround, I started declaring my components like this:const Cmp = React.memo(function Cmp() { return <div />; });, which restored the component name in the DevTools.However, this will not work for recursive components! In the following component, the whole recursive tree will re-render whenever the root component re-renders:
The reason is that inside of the component, the name
TreeItembinds to the function name, not to the result of theReact.memocall.@gaearon The tool looks great. It will take some time until I learn to use it efficiently, but I’m definitely giving it a go.
@natew Sorry, I guess I couldn’t form my thoughts into correct sentences lol. English is my second language so sometimes I have troubles expressing the things I want to say correctly.
I want to say that the component I wrapped with memo() didn’t only keep re-rendering, it kept re-mounting. So memo() didn’t work because it was being mounted again and again, therefore memo() was no help. Maybe your component is acting the same way and that’s why memo() doesn’t help. If it mounts, it doesn’t care about memo(), that’s why it refreshes although you always return true as the second argument. Could this be your case?
Hey guys, I read through this thread. I’m having the same issues with React. How exactly are we supposed to garantee a component doesn’t rerender if the below doesn’t even work?
I notice that even with useCallback(() => {}, []) still runs on every time the component re-renders.
Is this all normal? If so what’s the point of passing in
[]to useCallback or even using memo?Ok well this is good news for my sanity then. I thought I was going insane given it’s a simple component and fully returns true for memo. No legacy context and it wasn’t changing I don’t think. I’ll try and extract an example then.