React parent pass children will trigger rerender without state/props change

Viewed 107

When I have some children(HTML dom) as props to a child component and control in parent, I found that will trigger rerender!

Why React parent pass children will trigger rerender without state/props change?

How to avoid it? Check following.


const InsideChild = React.memo(({children}) => {
  const countRef = useRef(0)
  countRef.current += 1

  return (
    <div>render count: {countRef.current} {children}</div>
  )
})
                        
const OutsideParent = () => {
  const [count, setCount] = useState(0)

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
      <div>
        Test1:
        <InsideChild />
      </div>
      <div>
         Test2:
         <InsideChild>
           <p>children as html dom will not trigger rerender.</p>
         </InsideChild>
      </div>
    </div>
  )
}

as sample code, Test1 will not trigger rerender, Test2 will. Is that possible to avoid it?

More detail and working sample here:

https://codepen.io/sky790312/pen/QWqygxQ

1 Answers

The React.memo is not working when you are sending html is becuase you the children is an object in that case, the only way you can compare it is with a deep compare or something.

You can add a comparer function to your memo, and compare the inner props of the objects in the case you send an html.

In the example i just check if the props are an object and then compare the object inside, hope it helps you

const comparisonFn = (prevProps, nextProps) => { 
  if(typeof prevProps?.children === 'object') {
    return prevProps?.children.props.children == 
      nextProps?.children.props.children
  } else {
    return prevProps?.children === nextProps?.children;
  }
}

const InsideChild = React.memo(({children}) => {
  const countRef = useRef(0)
  countRef.current += 1

  return (
    <div>render count: {countRef.current} {children}</div>
  )
}, comparisonFn)
Related