【问题标题】:Why React.memo doesn't work with `props.children` property?为什么 React.memo 不适用于 `props.children` 属性?
【发布时间】:2022-08-07 18:14:41
【问题描述】:

我试图在React.memo() 中包装一个子组件(\“Box\”),以避免在父组件从状态更改重新渲染时对其进行不必要的重新渲染。但这不适用于 props.children 属性。当我再次尝试不使用props.children 时,它确实有效!但为什么?如果不使用 memo ,我如何使用组合和 props.children 属性来实现这一点?

(1)传递组件示例(工作):<Box title=\"i am a box\" />

(2) 传递组件示例(没用):<Box> <h1>I am a box</h1> </Box>

///

工作示例(1):

导出默认备忘录 (function Box(props) { 返回 ( <div className=\'box\'><h1>{props.title}</h1></div> ) });

父组件:

不工作的例子(2):

导出默认备忘录 (function Box(props) { 返回 ( <div className=\'box\'>{props.children}</div> ) });

父组件:

  • 如果您将代码放在代码部分的图像中会更好(如果有人想重现该问题会更容易)

标签: reactjs react-hooks state


【解决方案1】:

组件的子组件实际上来自其父组件。当 parent 重新渲染时,它会重新计算所有内容并触发 return 语句。该 return 语句中的所有内容都被重新创建,并且所有的孩子都被要求重新渲染。

其中一些可能是记忆组件,这将根据道具决定是否应该重新渲染。但是,当您将子组件传递给该组件时,对于父组件的每次重新渲染,您都会将子组件的新实例发送到该记忆化组件。因此,它会导致它重新渲染。

【讨论】:

  • 那么有解决办法吗?
  • 我想这取决于您的用例以及您要实现的目标。您可以将该 Box 与孩子一起包装在一个单独的容器组件中并记忆它。你真的需要把孩子传给那个盒子吗?你不能用道具来达到同样的效果吗?
【解决方案2】:

我可以解释这一点。

  1. Memo 做了简单的比较,没有深入的比较。
  2. 当您在孩子中传递字符串时,备忘录将适用于孩子。
  3. 如果您传递一些元素或组件,props.childrens 将包含数组,并在传递的元素上具有引用。每个渲染器都会收到一个新数组。一个数组可能有相同的内容,但对数组的引用会不同。这些是不同的数组,备忘录不做深度比较。它不比较数组、对象的内容,而只比较它们的引用。

    基于上述内容,为什么这不适用于您的示例。您将“h1”元素作为子元素传递。在您的子组件中有一个带有一个“h1”元素的数组。并且对于父级的每次渲染,都会出现一个新数组。是的,它将是一个内容相同的数组,但它不是同一个数组,不是同一个引用。备忘录不做深度检查。

    下面两个例子,带有字符串和元素。

    // if you pass string as children, memo will be work
    
    const MemoizedChildComponent = memo(({children}) => {
      console.log('render', children) // only one time render, children: "string"
      return <div>children</div>
    })
    
    const App = () => {
      const [count, setCount] = useState(1)
    
      const handleCountIncrement = () => {
        setCount(count + 1)
      }
    
      return <div>
        <button onClick={handleCountIncrement}>increase</button>
        <MemoizedChildComponent>string</MemoizedChildComponent> // pass simple string
      </div>
    }
    

    但是,如果您不想放弃元素并想记住它们怎么办?

    我建议使用 useMemo 钩子来记忆传递的孩子。

    我不会说那很好。但是,如果您的目标是记住孩子,这将有所帮助。你不能用备忘录解决这个问题。没有深度比较,所以memo和children有问题。不要忘记在 useMemo 中指定依赖项,示例中没有,因此依赖项数组为空。

    const MemoizedChildComponent = memo(({children}) => {
      console.log('render', children) // only one time render, because it children memoized in parent component inside useMemo hook. children: [object, object, object]
      return <div>children</div>
    })
    
    const App = () => {
      const [count, setCount] = useState(1)
    
      const handleCountIncrement = () => {
        setCount(count + 1)
      }
    
      const memoizedChildren = useMemo(() => [
        <p key="1">asd</p>,
        <p key="2">asd</p>,
        <p key="3">asd</p>
      ], [])
    
      return <div>
        <button onClick={handleCountIncrement}>increase</button>
        <MemoizedChildComponent>{memoizedChildren}</MemoizedChildComponent>
      </div>
    }
    

【讨论】:

    【解决方案3】:

    不要直接导出你的备忘录对象,而是将它包装在一个功能组件中:

    const MemoizedBox = React.memo((props) => {
        return <div>Some text</div>
      })
    
    export default function Box(props) {
        return <MemoizedBox />;
    }
    

    【讨论】:

      猜你喜欢
      • 2020-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-05
      • 1970-01-01
      • 1970-01-01
      • 2022-08-19
      • 2010-09-30
      相关资源
      最近更新 更多