You have a misunderstanding in the concepts here. The default is for React to call render on all children, regardless of whether the props changed or not.
After that happened, React will compare that new Virtual DOM to the current Virtual DOM and then only update those parts of the real DOM that changed.
That's why the code in a render method should be quick to execute.
This behavior can be changed by using features like useMemo
, PureComponents
or shouldComponentUpdate
.
References:
https://reactjs.org/docs/rendering-elements.html (Bottom):
Even though we create an element describing the whole UI tree on every tick, only the text node whose contents have changed gets updated by React DOM.
https://reactjs.org/docs/optimizing-performance.html#avoid-reconciliation
Even though React only updates the changed DOM nodes, re-rendering still takes some time. In many cases it’s not a problem, but if the slowdown is noticeable, you can speed all of this up by overriding the lifecycle function shouldComponentUpdate, which is triggered before the re-rendering process starts.
...
In most cases, instead of writing shouldComponentUpdate() by hand, you can inherit from React.PureComponent. It is equivalent to implementing shouldComponentUpdate() with a shallow comparison of current and previous props and state.
Also, read this for some more background info: https://dev.to/teo_garcia/understanding-rendering-in-react-i5i
Some more detail:
Rendering in the broader sense in React means this (simplified):
- Update existing component instances with the new props where feasible (this is where the key for lists is important) or create a new instance.
- Calling
render
/ the function representing the component if shouldComponentUpdate
returns true
- Syncing the changes to the real DOM
This gives you these optimization possibilities:
- Ensure you are reusing instances instead of creating new ones, e.g. by using a proper key when rendering lists. Why? New instances always result in the old DOM node to be removed from the real DOM and a new one to be added. Even when unchanged. Reusing an instance will only update the real DOM if necessary. Please note: This has no effect on whether or not
render
is being called on your component.
- Make sure your render method doesn't do heavy lifting or if it does, memoize those results
- Use PureComponents or
shouldComponentUpdate
to prevent the call to render
altogether in scenarios where props didn't change
Answering your specific question:
To actually prevent your ShowName
component from being rendered - into the Virtual DOM - if their props changed, you need to perform the following changes:
- Use
React.memo
on your ShowName component:
function ShowName({ name, onClick }) {
return (
<button type="button" onClick={onClick}>{name}</button>
);
}
export default memo(ShowName);
- Make sure the props are actually unchanged by not passing a new callback to
onClick
on each render of the parent. onClick={() => onClick(index)}
creates a new anonymous function every time the parent is being rendered.
It's a bit tricky to prevent that, because you want to pass the index to this onClick function. A simple solution is to create another component that is passed the onClick with the parameter and the index and then internally uses useCallback
to construct a stable callback. This only makes sense though, when rendering your ShowName
is an expensive operation.