React:如何防止在`map`中重新渲染子组件?
Posted
技术标签:
【中文标题】React:如何防止在`map`中重新渲染子组件?【英文标题】:React: How to prevent re-rendering child components in `map`? 【发布时间】:2020-06-17 02:48:14 【问题描述】:我试图将问题归结为一个尽可能简单的例子:
我们有一个子组件列表,每个子组件都称为NumChoice
,每个子组件代表一个数字。 NumChoice
包裹在 React.memo
中。在父组件中,我们有一个布尔数组choices
,每个都对应一个子组件NumChoice
。起初,choices
的所有元素都是false
。为了渲染子组件,我们遍历choices
,并为每个选择生成对应的子组件NumChoice
。我们在父组件中定义一个函数chooseDivisibles
,使用从每个子组件NumChoice
调用的useCallback
。 chooseDivisibles
获取调用它的NumChoice
的索引,并将choices
的对应元素更改为true
。每个NumChoice
如果choices
中对应的元素为true
,则其背景颜色为“红色”,否则,其背景颜色为“白色”。
完整的代码可在以下位置获得: https://codesandbox.io/s/react-rerender-l4e3c?fontsize=14&hidenavigation=1&theme=dark
将NumChoice
包装在React.memo
和chooseDivisibles
在useCallback
中,我们希望只重新渲染NumChoice
的组件,其对应的choices
元素发生变化,但React 会重新渲染它们。 chooseDivisibles
被包裹在 useCallback
中,除了 setChoices
之外没有列出任何依赖项。此外,NumChoice
被包裹在 React.memo
中,它应该只在指定的 props 发生变化时重新渲染,但它们不会,并且更改 choices
不应该对重新渲染 NumChoice
产生任何影响。如果我们排除在上一个和下一个道具中检查chooseDivisibles
的相等性,它会按预期工作,但我认为上一个和下一个chooseDivisibles
的比较不应该影响重新渲染NumChoice
,因为它包含在useCallback
and不依赖于choices
。我们如何防止重新渲染 props
未更改的 NumChoice
组件?
【问题讨论】:
在您添加prevProps.chooseDivisibles === nextProps.chooseDivisiles
之前,一切似乎都运行良好,分析器没有显示其 props 保持相同重新渲染的组件。
我认为您的建议并不能解决问题,因为如果我们将等式更改为 (prevProps, nextProps) => prevProps === nextProps
,它会重新渲染所有组件。考虑到chooseDivisibles
被包裹在useCallback
中,它列出了除setChoices
之外的任何依赖项,因此更改状态不应更改chooseDivisibles
并且不应重新渲染NumChoice
如果您尝试相同的事情而不使用map
through一个数组,这个问题不会发生。请看一下我刚刚保存的新代码。
据我所知,React 已经(浅)将以前的 props 与新的 props 对象进行比较,所以这样做 prevProps === newProps
是多余的,实际上我认为它是错误的,因为结果将永远是false 不管你是否这样做,因为对象是通过引用传递的。
@eMontielG 你的回答是对的。谢谢你。请发布它,我会接受它作为正确答案。为了解决您的问题,我将chooseDivisibles=event => chooseDivisibles(idx)
更改为chooseDivisibles=chooseDivisibles
,并将NumChoice
中的函数调用替换为onClick=event => props.chooseDivisibles(props.num)
。然后,我比较了prevProps.num === nextProps.num && prevProps.choice === nextProps.choice && prevProps.chooseDivisibles === nextProps.chooseDivisibles
,它按预期工作。
顺便说一句,我想我发现了你的问题,你也应该记住循环选择的组件。
【参考方案1】:
啊,我看到在NumChoice.js
中我们还断言prevProps.chooseDivisibles === nextProps.chooseDivisibles
,它始终是false
,因为chooseDivisibles=event => chooseDivisibles(idx)
每次都会生成一个新函数
如果你删除prevProps.chooseDivisibles === nextProps.chooseDivisibles
,它只会重新渲染受影响的!
【讨论】:
我认为您的建议并不能解决问题,因为如果我们将等式更改为(prevProps, nextProps) => prevProps === nextProps
,它会重新渲染所有组件。考虑到chooseDivisibles
被包裹在useCallback
中,它列出了除setChoices
之外的任何依赖项,因此更改状态不应更改chooseDivisibles
并且不应重新渲染NumChoice
如果您尝试相同的事情而不使用map
through一个数组,这个问题不会发生。
为了解决您的问题,我将chooseDivisibles=event => chooseDivisibles(idx)
更改为chooseDivisibles=chooseDivisibles
并将NumChoice
中的函数调用替换为onClick=event => props.chooseDivisibles(props.num)
,但它仍然会重新渲染所有NumChoice
组件。
嘿@1man 对不起,我的意思是我们可以注释掉断言函数。像这样:(prevProps, nextProps) => prevProps.num === nextProps.num && prevProps.choice === nextProps.choice // prevProps.chooseDivisibles === nextProps.chooseDivisibles <-- comment this out
分叉示例:codesandbox.io/s/…
我们找到了解决方案,但我们不应该评论我们的prevProps.chooseDivisibles === nextProps.chooseDivisibles
,因为chooseDivisibles
被包裹在useCallback
中,这样可以防止在choices
更改时更改函数对象。所以,我们不需要注释掉这个比较。但是,如果我们将其注释掉并且由于某些其他原因 chooseDivisibles
更改,我们确实希望重新渲染 NumChoice
。
啊,很高兴听到这个消息!感谢更新?我刚刚更新了示例,将<App />
中的一些关注点分开,所以我们可以简化<NumChoice />
的界面,使其备忘录功能更直接codesandbox.io/s/…请告诉我你的想法!跨度>
以上是关于React:如何防止在`map`中重新渲染子组件?的主要内容,如果未能解决你的问题,请参考以下文章
如何防止我的功能组件使用 React 备忘录或 React 钩子重新渲染?
Array.map 中的 React 功能组件在将函数作为道具传递时总是重新渲染
使用 setInterval 时如何防止 React 重新渲染整个组件