React:如何将当前 props.children 与新的进行比较
React: how to compare current props.children with new one
嗨,
我正在构建组件,它仅充当其他一些生成内容的包装器并使用第三方库。该库与 props.children
组件一起使用。到目前为止一切顺利,但是这个第三方库在应用时有点滞后,或者在元素上刷新。因为刷新这个库的唯一原因是 props.children
改变了,所以我想弄清楚如何比较 shouldComponentUpdate
中的 this.props.children
和 nextProps.children
。我在想 PureRenderMixin 应该做这项工作,但对我来说它不起作用。即使我只更改 state.listName
组件也会重新渲染,如下面的示例所示。
<div>
List name '{this.state.listName}'
<br />
<MyComponent>
<ul>
{listOfLi}
</ul>
</MyComponent>
</div>
有什么办法,如何管理 props.children
或任何其他选项的比较,如何做类似的事情?
感谢您的帮助!
你可以利用 child key
prop,React 建议应该给出 children 的数组来唯一标识它们。因为每个 child 都有一个键,你可以可靠地判断 children 是否随着 prop 的变化而改变(这就是键的全部意义!)。如果密钥在新旧之间不匹配,则它们已更改。
React.render(<App><Child key='1'/><Child key='2'/></App>, document.body)
如果 children 发生变化
,则在每次更新前检查您要检查的 App 组件
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
}
请注意,这并不能告诉你每个 child 的内容是否发生了变化,如果你想知道 [=23] =]在这一点下面的整棵树中没有任何变化
作为更进一步的优化,我们知道 children 永远不会因状态变化而改变,因此我们实际上可以在 componentWillReceiveProps()
中进行比较并设置一些状态 属性 喜欢 childrenHaveChanged
正如 Matt S 指出的那样,接受的答案是一种脆弱的解决方法,并且取决于 key
的非标准使用。除了他列出的列表示例之外,如果您的 id
保持不变但在它们所代表的资源中修改了某些字段,即使使用 key={id}
之类的东西也会失败。
这个issue contains a good discussion on the topic and ends with a more stable workaround。本质上,您可以简化 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
})
)
}
然后你可以使用shouldComponentUpdate
或React.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
只是一种方法,the comment 中提到的方法类似,您还可以利用 lodash.isequal
等实用程序进行深度比较。不幸的是,我不知道有任何一两个衬垫可用于此比较,但如果您知道更简单稳定的方法,请发表评论!
您描述的设计和行为有些不对劲。您应该很少(如果有的话)必须关心执行 children
的手动差异。那应该留给 React。如果您使用的库在每次组件更新时都会阻塞,无论是因为 children
还是其他一些 state/prop 更改,它都不是被动编写的,您应该寻找一个不同的库反应性地写的。或者,您可以通过打开问题或提交拉取请求来为修复开源项目中的该行为做出贡献。
做你想做的事情并不容易,因为它不应该是必要的。 React 非常非常擅长处理协调,并且只会在实际发生更改时才会呈现,这将更改与其相关的 DOM 的状态。
嗨,
我正在构建组件,它仅充当其他一些生成内容的包装器并使用第三方库。该库与 props.children
组件一起使用。到目前为止一切顺利,但是这个第三方库在应用时有点滞后,或者在元素上刷新。因为刷新这个库的唯一原因是 props.children
改变了,所以我想弄清楚如何比较 shouldComponentUpdate
中的 this.props.children
和 nextProps.children
。我在想 PureRenderMixin 应该做这项工作,但对我来说它不起作用。即使我只更改 state.listName
组件也会重新渲染,如下面的示例所示。
<div>
List name '{this.state.listName}'
<br />
<MyComponent>
<ul>
{listOfLi}
</ul>
</MyComponent>
</div>
有什么办法,如何管理 props.children
或任何其他选项的比较,如何做类似的事情?
感谢您的帮助!
你可以利用 child key
prop,React 建议应该给出 children 的数组来唯一标识它们。因为每个 child 都有一个键,你可以可靠地判断 children 是否随着 prop 的变化而改变(这就是键的全部意义!)。如果密钥在新旧之间不匹配,则它们已更改。
React.render(<App><Child key='1'/><Child key='2'/></App>, document.body)
如果 children 发生变化
,则在每次更新前检查您要检查的 App 组件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
}
请注意,这并不能告诉你每个 child 的内容是否发生了变化,如果你想知道 [=23] =]在这一点下面的整棵树中没有任何变化
作为更进一步的优化,我们知道 children 永远不会因状态变化而改变,因此我们实际上可以在 componentWillReceiveProps()
中进行比较并设置一些状态 属性 喜欢 childrenHaveChanged
正如 Matt S 指出的那样,接受的答案是一种脆弱的解决方法,并且取决于 key
的非标准使用。除了他列出的列表示例之外,如果您的 id
保持不变但在它们所代表的资源中修改了某些字段,即使使用 key={id}
之类的东西也会失败。
这个issue contains a good discussion on the topic and ends with a more stable workaround。本质上,您可以简化 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
})
)
}
然后你可以使用shouldComponentUpdate
或React.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
只是一种方法,the comment 中提到的方法类似,您还可以利用 lodash.isequal
等实用程序进行深度比较。不幸的是,我不知道有任何一两个衬垫可用于此比较,但如果您知道更简单稳定的方法,请发表评论!
您描述的设计和行为有些不对劲。您应该很少(如果有的话)必须关心执行 children
的手动差异。那应该留给 React。如果您使用的库在每次组件更新时都会阻塞,无论是因为 children
还是其他一些 state/prop 更改,它都不是被动编写的,您应该寻找一个不同的库反应性地写的。或者,您可以通过打开问题或提交拉取请求来为修复开源项目中的该行为做出贡献。
做你想做的事情并不容易,因为它不应该是必要的。 React 非常非常擅长处理协调,并且只会在实际发生更改时才会呈现,这将更改与其相关的 DOM 的状态。