简体   繁体   中英

how to use React.memo with a Component contains children

need some help ~

I have two Components, and I wrapped Parent with React.memo:

Child

const Child = ()=> <div>I'm Child</div

export default Child

Parent

const Parent = (props)=> <div>{props.children}</div>

export default React.memo(Parent)

Use in App:

const App = () => {
  const [count, setCount] = useState(0)

  return(
    <div>
      <button onClick={()=>setCount(count+1)}></button>

      <Parent>
        <Child></Child>
      </Parent>
    </div>
  )
}

Click the button, result:

The Parent Component will rerender, so the memo not working because it's children is a function component

So, how can I prevent rerender?

I know a way to solve by useMemo, but it's ugly and not friendly, do you have better ideas?

const App = () => {
  const [count, setCount] = useState(0)

  const children = useMemo(()=><Child></Child>,[])

  return(
    <div>
      <button onClick={()=>setCount(count+1)}></button>

      <Parent>
        {children}
      </Parent>
    </div>
  )
}

Wrap your <Child /> with React.memo :

const Child = ()=> {
  console.log('render') // fires only once - on initial render
  return <div>I'm Child</div>
}

const MChild = React.memo(Child);
const Parent = (props)=> <div>{props.children}</div>
const MParent = React.memo(Parent)

const App = () => {
  const [count, setCount] = useState(0);

  return(
    <div>
      <button onClick={()=>setCount(count+1)}>increment {count}</button>
      <MParent>
        <MChild></MChild>
      </MParent>
    </div>
  )
}

render(<App />, document.getElementById('root'));
const children = useMemo(()=><Child></Child>,[])

Is the easiest way to go. Using memo(Child) wont work since jsx in fact returns a new object whenever you call <Child /> . React.memo by default just use simple shallow comparison so there really is no other direct way to solve it. You can create your own function that would eventually support children and pass it to React.memo(MyComp, myChildrenAwareEqual) .

Move <Parent><Child/></Parent> into a separate component, and memoize that component instead:

const Family = memo(() => <Parent><Child/></Parent>);

const App = () => {
  const [count, setCount] = useState(0);

  return (
    <>
      <button onClick={() => setCount(count => count + 1)}>{count}</button>
      <Family />
    </>
  )
}

demo

const Memo = React.memo(({ children, value }) => {
  return children
}, (prev, next) => prev.value === next.value)

And then to use it

function Title() {
  const [count, setCount] = useState(0)
  const onClick = () => {
    setCount(c => c + 1)
  }

  const a = ''

  return (
    <>
      <div onClick={onClick}>{count}</div>
      <Memo value={a}>
        <Child>
          <Nothing2 a={a} />
        </Child>
      </Memo>
    </>
  )
}

When a changes, Child renders, otherwise it bails out with previous render.

I outline the rational behind it in my blog, https://javascript.plainenglish.io/can-usememo-skip-a-child-render-94e61f5ad981

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM