什么是唯一标识?
相信大家在使用React时,都曾遇到过下面这个警告信息吧:
1 | Warning: Each child in a list should have a unique "key" prop. |
这个警告信息的意思是:在渲染列表数据时,列表中每个元素都应该具有唯一的key
属性。这个key
属性是React用来识别每个子元素的唯一标识。React使用这个唯一标识来高效地更新和渲染组件。
这种警告经常发生在渲染多个相同元素时,比如使用js中的map进行循环渲染,下面的代码就是一个典型的例子,我们使用一个数组保存了四个按钮,然后使用map进行循环渲染这些按钮。
1 | const actions = ['View', 'New', 'Edit', 'Delete']; |
由于我们没有为Button
组件指定唯一标识,所以上面的代码会发出警告信息。我们可以为每个按钮添加一个唯一的key
属性来解决这个问题。
1 | actions.map((action) => ( |
因为这四个按钮的名字都不相同,属于唯一标识,我们可以用它来做Button的key
属性。这样就可以消除警告信息了。
Table组件中的唯一标识
Ant Design中的Table
组件也是列表数据,当然也需要唯一标识,Table组件中最重要的两个属性,dataSource
和columns
,都需要指定唯一标识。
为Columns指定唯一标识
在之前的文章中,我们定义Columns时,已经为每一列都指定了key
属性。由下面的代码可知,key
属性和dataIndex
属性的值是相同的,因为dataIndex
属性基本就是数据库中的表字段,可以确保唯一性,所以使用这个值作为key
属性的值是一个不错的选择。
1 | const columns = [ |
为dataSource指定唯一标识
与columns
不同,dataSource
有两种方式来指定唯一标识:
- 在
dataSource
中的每一行数据中添加一个唯一的key
属性。 - 在
<Table>
组件中指定rowKey
属性。
第一种方式不是很常用,因为数据都是后端API返回的,我们无法要求后端API返回的数据中每一行都添加一个key
属性。所以实际开发中,经常使用第二种。
1 | <Table |
注意,这里的id
不是随便取的名字,要求返回的列表数据中每一项都要有id属性(一般来说,后端返回的列表数据都会有id属性,因为在数据库层面,也需要id来区分不同的记录),也可以是其他属性,比如name
, email
等,只要确保它是唯一的就可以。
如果你的数据源中没有唯一标识,那么你可以自定义一个函数来生成唯一标识。
1 | <Table |
这里的generateUniqueKey
是一个自定义函数,用于生成唯一标识。这个函数接收每一行数据(record
)作为参数,返回一个字符串类型的唯一标识。我们可以使用多列组合的方式返回唯一标识,比如使用name
和email
的组合。
1 | function generateUniqueKey(record) { |
也可以使用第三方库来生成唯一标识,比如nanoid
。
1 | // 使用nanoid生成唯一key |
注意事项
需要注意的是:不能使用数组下标作rowKey
,因为在数据发生变化时,数组下标会发生变化,导致React无法正确识别哪些数据发生了变化,从而引发渲染问题。
1 | rowKey={(record, index) => index} // 不要使用数组元素下标做rowKey |
总结
React要求列表数据中每个元素都要有一个唯标识(默认是key
属性),其底层原因是为了Diff算法能够高效地比较和更新组件。Ant Design的Table组件也遵循了这个原则,我们可以通过如下几种方式来指定每一行数据的唯一标识。
React通过对比新旧虚拟DOM树的key值,快速识别增删改的组件。若未提供key或使用不稳定的key(如数组下标),会导致不必要的重新渲染甚至状态丢失。
为dataSource指定唯一标识
- 在
dataSource
中的每一行数据中添加一个唯一的key
属性。 - 在Table组件中指定
rowKey
属性,可以是一个字符串(表示数据源中每一行数据的唯一标识)或者一个返回字符串的函数(用于生成每一行数据的唯一标识)。
为columns指定唯一标识
对于columns
来说,必须在定义时指定一个唯一的key
属性,因为Table组件的rowKey
只作用于dataSource
,而不作用于columns
。
今天下午的飞机回大连,上次回去还是端午节,转眼一个半月过去了,How time flies!