const Parent = () => {
let [hitCounter,setHitCounter] = React.useState(0);
let [clickCounter,setClickCounter] = React.useState(0);
const data= React.useMemo(() => {
console.log("in useMemo")
return hitCounter
},[hitCounter]);
return(
<>
<Child hitCounter={data}/>
<input type="button" onClick={() => setHitCounter(hitCounter + 1)} value="Hit me"/>
{hitCounter}
<div><input type="button" onClick={() => setClickCounter(clickCounter + 1)} value="Click me"/>
{clickCounter}</div>
</>
)
}
export default Parent;
const Child = (props) => {
let data = 0;
useEffect(() => {
console.log("in useeffect");
data = props.counter;
},[props.counter])
console.log("in Child");
return(
<div>
The Hit counter value is {data}
</div>
)
}
export default Child
In the Parent component, I have 2 buttons Hit me and Click me. On click of each of them the respective counter gets incremented. So when I am clicking on ‘Click me’ the clickCounter gets incremented but hitCounter remains same. I am sending the memoized ‘Hit me’ count(hitCounter) to the child.
My confusion is why the child component is still getting re-rendered when clickCounter changes even though I am not passing it to child and have used useMemo for hitcounter(this is not getting changed).
I am using useMemo for hitcounter to make sure the child does not gets re-rendered when hitcounter is not changing.
2
Answers
In response to a change in state, React will re-render that component with the changed state, and all children of that component. The props you pass to those children do not matter.
Consider this example:
Here every time you click the button,
'foo'
will get logged to the console. Here theApp
component has updated state, so all it’s children render, too.If you don’t want that, you can use React.memo on the component, not the state.
This is not a hook, it’s a wrapper around component declarations.
With that change, the
Child
component will only render once for it’s lifetime since it props could never change.See Stackblitz
That said, you rarely need to do this. You should only be looking at this if yo are having actual performance problems in your app. And even if you are, in my experience it’s usually more due to your business logic than react’s rendering performance.
If you want to skip rendering a child component, you need to memoize that component itself. For example:
React.memo will skip rendering the child if none of its props have changed.
The other option is to memoize the element in the parent:
As long as
hitCounter
does not change, this will reuse the same element, and thus react knows it has already rendered that element.P.S, your existing code has some things that don’t do anything of note. For example:
This code says that if
hitCounter
changes, it should run the memo function. That function simply returnshitCounter
and assigns it todata. The same could be done with the code
const data = hitCounter`.This effect does nothing of note. Effects run after rendering is complete, so nothing is going to use
data
. You’ve already finished rendering the component using0
for data. You probably want: