как использовать React.memo с компонентом, содержащим дочерние элементы

Нужна помощь ~

У меня есть два компонента, и я обернул родительский элемент React.memo:

Ребенок

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

export default Child

Родитель

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

export default React.memo(Parent)

Использование в приложении:

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

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

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

Нажмите кнопку, результат:

Родительский компонент будет повторно отображен, поэтому памятка не работает, потому что ее дочерние элементы являются функциональным компонентом.

Итак, как я могу предотвратить повторную визуализацию?

Я знаю способ решить с помощью useMemo, но он уродлив и недружелюбен, у вас есть идеи получше?

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

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

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

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

4 ответа

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

Это самый простой способ. С помощьюmemo(Child) не будет работать, поскольку jsx фактически возвращает новый объект всякий раз, когда вы вызываете <Child />. React.memoпо умолчанию просто используйте простое неглубокое сравнение, так что другого прямого способа решения нет. Вы можете создать свою собственную функцию, которая в конечном итоге будет поддерживать детей, и передать ееReact.memo(MyComp, myChildrenAwareEqual).

Оберните свой <Child /> с участием 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'));

Двигаться <Parent><Child/></Parent>в отдельный компонент и вместо этого запомните этот компонент:

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

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

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

демонстрация

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

А потом использовать это

      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>
    </>
  )
}

Когда a изменения, дочерний рендеринг, в противном случае он выйдет из строя с предыдущим рендерингом.

Я обрисовываю рациональное решение в своем блоге https://javascript.plainenglish.io/can-usememo-skip-a-child-render-94e61f5ad981

Другие вопросы по тегам