React JS - Props 在渲染的 HTML 中不显示。
React JS - Props 在渲染的 HTML 中不显示。
我正在构建一个React组件,它接受JSON数据源并创建一个可排序的表格。
每个动态数据行都有一个唯一的键分配给它,但我仍然会收到一个错误:
数组中的每个子项都应该有一个唯一的“key”属性。
检查TableComponent的渲染方法。
我的TableComponent
渲染方法返回:
{ rows }
TableHeader
组件是一行,并且也有一个唯一的键分配给它。
rows
中的每个row
都是使用具有唯一键的组件构建的:
而TableRowItem
看起来像这样:
var TableRowItem = React.createClass({ render: function() { var td = function() { return this.props.columns.map(function(c) { return{this.props.data[c]}
; }, this); }.bind(this); return (
{ td(this.props.item) }
) } });
是什么导致唯一键错误?
迭代数组时要小心!
很多人错误地认为,在数组中使用元素的索引值是一种可接受的方法,可以避免你最熟悉的错误:
Each child in an array should have a unique "key" prop.
然而,在许多情况下,这是不可行的!这是一种反模式,在某些情况下会导致不想要的行为。
了解key
属性
React使用key
属性来理解组件到DOM元素的关系,然后用于调和过程。因此,保持唯一的关键是非常重要的,否则React很有可能混淆元素并改变不正确的元素。同样重要的是,在所有重新呈现期间保持这些键不变,以保持最佳性能。
话虽如此,只要知道数组是完全静态的,就不必总是应用以上内容。然而,尽可能地应用最佳实践是值得鼓励的。
React开发人员在这个GitHub问题中说:
- key并不是真正关于性能的,它更多地是关于身份的(进而导致更好的性能)。随机分配和更改值并不等同于身份
- 如果我们不知道您的数据如何建模,就不可能[自动]提供密钥。如果您没有ID,我建议尝试使用某种哈希函数
- 当我们使用数组时,我们已经有了内部密钥,但它们是数组中的索引。当您插入新元素时,这些密钥是错误的。
简而言之,key
应该是:
- 唯一 - 一个密钥不能与 兄弟组件的密钥相同。
- 静态 - 一个密钥在重新呈现之间不应该发生变化。
使用key
属性
根据以上解释,仔细研究以下示例,并在可能的情况下尝试实现推荐的方法。
不好(可能)
{rows.map((row, i) => { return; })}
当在React中迭代数组时,这可能是最常见的错误。这种方法在技术上并不“错误”,只是如果不知道自己在做什么就会很“危险”。如果您正在迭代一个静态数组,则这是一个完全有效的方法(例如,导航菜单中的链接数组)。但是,如果您添加、删除、重新排序或筛选项目,则需要小心。请查看官方文档中的详细说明。
class MyApp extends React.Component { constructor() { super(); this.state = { arr: ["Item 1"] } } click = () => { this.setState({ arr: ['Item ' + (this.state.arr.length+1)].concat(this.state.arr), }); } render() { return(
-
{this.state.arr.map(
(item, i) =>
- {item + " "} )}
在此代码片段中,我们使用了一个非静态数组,并且不局限于将其用作堆栈。这是一种不安全的方法(您将看到其原因)。请注意,当我们将项添加到数组的开头(基本上是unshift)时,每个的值保持不变。为什么?因为该
key
不能唯一标识每个项。
换句话说,最初的时候,Item 1
具有key = {0}
。当我们添加第二项时,顶部项变为Item 2
,接着是第二项的Item 1
。但是,现在Item 1
的key = {1}
而不是key = {0}
了。相反,Item 2
现在具有key = {0}
!!
因此,React认为元素没有更改,因为键为
0
的Item
始终位于顶部!
那么,为什么这种方法只有时而坏呢?
如果数组在某种程度上被过滤、重新排列或添加/删除项,则此方法仅会存在风险。如果始终是静态的,那么它可以完全安全地使用。例如,像["Home", "Products", "Contact us"]
这样的导航菜单可以使用此方法安全地进行迭代,因为您可能永远不会添加新链接或重新排列它们。
简而言之,以下情况下您可以安全地
使用索引作为key
:
- 数组是静态的,永远不会更改。
- 数组从未被过滤(显示数组的子集)。
- 数组从未被重新排序。
- 数组被用作堆栈或LIFO(后进先出)。换句话说,只能在数组的末尾添加(即推),并且只有最后一项可以删除(即弹出)。
如果我们在上面的代码片段中改为将已添加的项推送到数组末尾,则每个现有项的顺序始终是正确的。
非常糟糕的
{rows.map((row) => { return; })}
虽然此方法可能保证键的唯一性,但它始终会迫使React在列表中重新渲染每个项,即使无需如此也会如此。这是一种非常不好的解决方案,因为它会极大地影响性能。更不用说无法排除Math.random()
可能产生相同数字的情况下键冲突的可能性。
不稳定的键(例如由
Math.random()
生成的键)将导致许多组件实例和DOM节点不必要地重新创建,这可能会导致性能下降并且在子组件中丢失状态。
非常好的
{rows.map((row) => { return; })}
这可能是最佳方法,因为它使用了数据集中每个项唯一的属性。例如,如果rows
包含从数据库中提取的数据,可以使用表的主键(通常是自动递增的数字)。
选择键的最佳方法是使用一个能唯一标识列表项在它的兄弟节点中的字符串。通常你会使用数据中的IDs作为键。
好的
componentWillMount() { let rows = this.props.rows.map(item => { return {uid: SomeLibrary.generateUniqueID(), value: item}; }); } ... {rows.map((row) => { return; })}
这也是一个好的方法。如果你的数据集不包含任何保证唯一性的数据(例如一个任意数字的数组),那么就有可能发生键冲突。在这种情况下,最好在迭代数据集之前为每个项目手动生成一个唯一标识符。最好在组件挂载或接收数据集(例如从 props
或异步 API 调用中),以便仅执行一次,而不是每次组件重新渲染都要生成。已经有一些库可以提供这样的键了。以下是一个例子:react-key-index。
你应该为每个子元素以及每个元素内部的子元素添加一个键。
这样React可以处理最小的DOM更改。
在你的代码中,每个
都试图在没有键的情况下渲染一些子元素。
查看这个示例。
尝试从
元素中删除key={i}
(并检查控制台)。
在示例中,如果我们不为元素提供键,并且我们希望仅更新
object.city
,React需要重新渲染整行而不仅仅是该元素。
这是代码:
const data = [ { name: "Nuri", age: 28, city: "HO" }, { name: "Talib", age: 82, city: "HN" }, { name: "Jenny", age: 41, city: "IT" }, ]; const ExampleComponent = React.createClass({ render: function () { const infoData = this.props.info; return ( {infoData.map((object, i) => { return ( {[ object.name, // remove the key {" "} {object.city}{" "} , object.age, ]} ); })} ); }, }); React.render(, document.body);
底部由@Chris发布的答案比此答案更详细。
React在协调中键的重要性的文档:Keys