反应:如何将当前的道具儿童与新道具儿童进行比较

React: how to compare current props.children with new one

本文关键字:新道具 比较 反应      更新时间:2024-06-29

嗨,

我正在构建一个组件,它只充当其他生成内容的包装器,并使用第三方库。该库与组件的props.children配合使用。到目前为止还不错,但这个第三方库在应用或刷新元素时有点滞后。因为刷新这个库的唯一原因是当props.children发生变化时,我正在尝试如何比较shouldComponentUpdate中的this.props.childrennextProps.children。我想PureRenderMixin应该做这项工作,但对我来说,它不起作用。即使我只更改state.listName,也会重新绘制组件,如下例所示。

<div>
  List name '{this.state.listName}'
  <br />
  <MyComponent>
    <ul>
      {listOfLi}
    </ul>
  </MyComponent>
</div>

有没有任何方法,如何管理props.children的比较或任何其他选项,如何做类似的事情?谢谢你的帮助!

正如Matt S所指出的,可接受的答案是一种脆弱的解决方法,取决于key的非标准使用。除了他列出的列表示例之外,如果您的id保持不变,但它们所代表的资源中的某些字段被修改,那么即使使用类似key={id}的东西也会失败。

本期包含了关于该主题的良好讨论,并以更稳定的解决方法结束。从本质上讲,您可以简化children道具,使其能够运行深度比较。您可以使用React.Children实用程序来编写简化方法:

// Flattens all child elements into a single list
const flatten = (children, flat = []) => {
    flat = [ ...flat, ...React.Children.toArray(children) ]
    if (children.props && children.props.children) {
        return flatten(children.props.children, flat)
    }
    return flat
}
// Strips all circular references and internal fields
const simplify = children => {
    const flat = flatten(children)
    return flat.map(
        ({
            key,
            ref,
            type,
            props: {
                children,
                ...props
            }
        }) => ({
            key, ref, type, props
        })
    )
}

然后可以使用shouldComponentUpdateReact.memo来防止重新渲染:

const MyComponent = ({ children }) => (
    <div>{ children }</div>
)
export default React.memo(MyComponent, (prev, next) => (
    JSON.stringify(simplify(prev.children)) ===
    JSON.stringify(simplify(next.children))
))

这些实用程序+JSON.stringify只是一种方法,注释中提到的方法类似,您也可以利用lodash.isequal等实用程序进行深入比较。不幸的是,我不知道有哪一两句话可以进行比较,但如果你知道一种更简单稳定的方法,请评论!

您可以使用子key道具,React建议应该给子数组以唯一标识它们。因为每个子对象都有一个密钥,所以可以可靠地判断子对象是否在道具更改中发生了更改(这就是密钥的全部意义!)。如果新钥匙和旧钥匙不匹配,那么它们就变了。

React.render(<App><Child key='1'/><Child key='2'/></App>, document.body)

如果孩子们更改了,请在每次更新前检查你想要检查的应用程序组件

shouldComponentUpdate(nextProps){
   var oldKeys = this.props.children.map( child => child.key);
   var newKeys = nextProps.children.map( child => child.key);
   //compare new and old keys to make sure they are the same
}

注意,这并不能告诉你每个孩子的内容是否发生了变化,如果你想知道这一点下的整棵树中是否没有任何东西改变,你必须根据一些标准对每个孩子进行比较(例如深入比较道具)

作为进一步的优化,我们知道孩子永远不会因为状态变化而改变,所以我们实际上可以在componentWillReceiveProps()中进行比较,只需设置一些状态属性,如childrenHaveChanged

您所描述的设计和行为有些问题。您应该很少(如果有的话)关心执行children的手动diff。这应该留给React。如果每次组件更新时,无论是因为children还是其他状态/属性更改,您使用的库都会阻塞,那么它不是被动编写的,您应该寻找一个不同的被动编写的库。或者,您可以通过打开问题或提交拉取请求来帮助修复开源项目中的这种行为。

做你想做的事情并不容易,因为这不应该是必要的。React非常非常擅长处理对账,并且只有在实际发生变化时才会呈现,这将改变与其相关的DOM的状态。