【发布时间】:2020-12-14 18:30:18
【问题描述】:
更新: 问题没有解决,但结果与我第一次发布时的样子不同。我修改了标题以更好地描述它的真正含义。更新了代码中的一些 cmets 以节省一些读取时间。 React 会在应该重用元素的时候重用元素,当两个数组长度相同时,结果会很奇怪。
Codesandbox 快速查看。
我遇到了这个令人沮丧但有趣的问题,并且已经为此苦苦挣扎了很长一段时间,但没有运气。不确定它是否真的是 React 的错误。
根据官方文档here,如果我理解这一点,通过为元素提供正确的键(在同级中唯一),React 应该尝试更新和重用它,而不是在重新渲染。
查看codepen 上的此示例,我已将其简化为足以显示问题。原始代码也包含在下面以供快速参考。
如果应用程序像现在这样简单,它就可以正常工作,因此增加/减少状态cur(通过单击两个按钮)来回滚动数字。
如果您在浏览器开发工具中查看它,以查看每次单击按钮时会破坏和重新创建哪些元素,很明显每次单击 + 都会破坏第一个项目并创建最后一个项目,每次单击 @987654327 @ 销毁最后一项并创建第一项,中间的其他 <li>s 只是更新和重用。这是正确的,也是意料之中的。
问题:
如果 num 和 loop 恰好长度相同,例如将num 更改为[3, 4, 5, 6](删除任何两个项目都可以)所以它有4 个项目与loop 相同,我希望看到所有项目在按钮单击时被重用,并且不应该销毁和重新创建任何项目,因为所有项目都在同一个 DOM 树中,并且设置了易于识别的键。
但是,如果您再次检查浏览器开发工具,它的工作方式确实完全不同,尽管渲染结果似乎没有任何问题。单击+ 会破坏第一个项目并将其重新创建为最后一个项目,同时重用所有其他项目。点击- 会重用第一个项目(上次渲染的最后一个项目)并销毁/重新创建所有其他项目。
虽然应用程序仍然可以正常运行(因为它很简单,只显示一些数字),但是所有类驱动的转换都会搞砸,<li> 下的所有子元素都会重新初始化,例如<img> 重新加载源图片。
问题
-
在这种情况下,当两个数组长度相同时,有什么特别之处?这里到底发生了什么?
-
可以或应该做些什么来解决它?意思是尝试尽可能多地重用现有项目。我能想到的一种快速肮脏的方法是将一些虚拟项目插入
num并在 map() 迭代中添加一些逻辑以跳过虚拟项目。这行得通,但我不认为这是一种“正确”的方式。
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
const num = [1, 2, 3, 4, 5, 6]; // array to serve content to list items
const loop = [1, 2, 3, 4]; // array to be mapped to generate the list
const RollingNums = () => {
const [cur, setCur] = React.useState(0); // control number rolling
return (
<div className="problem">
<ul>
{loop.map((value, index) => { // list items count is fixed
// simple CS to get continued content from array num
let idx = index + cur;
idx =
idx >= 0
? idx % num.length
: ((idx % num.length) + num.length) % num.length;
const target = num[idx].toString(); // content from array num for current element
return (
<li key={target} className={target}>
{target}
</li>
);
})}
</ul>
<div className="control">
<button onClick={() => setCur((prev) => prev - 1)}>-</button>
<span>{cur}</span>
<button onClick={() => setCur((prev) => prev + 1)}>+</button>
</div>
</div>
);
};
ReactDOM.render(<RollingNums />, document.querySelector('#root'));
【问题讨论】:
-
您的密钥在每次重新渲染时都会发生变化。你希望 React 如何使用相同的最后一个 React 元素?尽管如此,请check this out
-
@PrãtéékThápá 感谢您的提示。在我开始阅读您建议的文章之前,请允许我稍微评论一下:是的,我在每次渲染时都分配了键,但不是新键,而是选择并分配了现有的键。事实上,只要 num 比 loop 有更多的项目,一切都会按预期工作。 React 确实重用/更新了上次渲染中的相同元素,我已经尽可能清楚地描述了这一点。仅当 num 和 loop 长度相同时才会出现此问题。
-
据我所知,您正在分配新键。例如,第一个 li 有一个不同的键,并且会与其他 li 重新渲染。不确定,不过我明白你的意思。
-
我相信 React 是通过键而不是命令来工作的。在每个渲染上分配键是指导 React 在某些子元素上做什么的自然方式。观察 num 的长度大于 loop 的情况完全反映了这一点,只是当两个数组长度相同时,事情开始出乎意料...
标签: javascript reactjs list key reusability