阅读 397

在React中index作为key是反模式的

很多时候,一些开发者在呈现列表的时候喜欢用index(索引)作为其关键字(key)

我们都知道在React中,在渲染相邻同级元素(siblings)时需要给每一个item指定相应的key作为唯一标识符,如一组li,为了方便在页面发生变化是,即render树进行diff时,没有发生变化的element就不做更改。

但是在使用key的时候,很多人都习惯用列表的索引作为关键字,看起来很优雅,简洁。可不曾想到他暗藏着潜在的漏洞。

list.map((item,index)=>{
    return <li key={index}>{...item}</li>
})
复制代码
Note:它可能会破坏你的应用程序并显示错误的数据

因为key是用来唯一标识一个元素的。当你向列表中添加一条数据或者移除某条数据时。如果该键与之前的相同,React会认为DOM元素表示的组件和以前是一样的。然而我们想要的并非如此。

举一个小栗子,使用 ==index== 作为key

class Home extends React.Component {
	constructor() {
		super();
		this.state = {
			list: [{name:'zs',id:1},
			        {name:'ls',id:2},
			        {name:'ww',id:3}]
		};
	}
	addAheadItem() {
		this.setState({
			list: [{name:'zq',id:4}, ...this.state.list]
		});
	}
	addBehindItem(){
	   this.setState({
			list: [...this.state.list,{name:'zq',id:4}]
		}); 
	}
	render() {
		return (
			<div>
				<button	onClick={() => {this.addAheadItem();}}>
					添加到头部
				</button>
				<button	onClick={() => {this.addBehindItem();}}>
					添加到尾部
				</button>
				//使用index作为索引
				<div>
					{this.state.list.map((item, index) => {
						return (
							<li key={index}>
								{item.name}
								<input type="text"  />
							</li>
						);
					})}
				</div>
				//使用id作为索引
				<div>
					{this.state.list.map((item, index) => {
						return (
							<li key={item.id}>
								{item.name}
								<input type="text"  />
							</li>
						);
					})}
				</div>
			</div>
		);
	}
}
render(<Home/>,document.getElementById('root'))

复制代码

事实证明,当使用index作为索引的时候,添加到头部的数据的input输入框会显示以前的内容。

Better

列表里面的每一项都应该有一个永久并且唯一的属性,理想情况下应该在创建列表的时候分配下去. 当然我指的是id. 我们可以像下面这样使用它:

{todos.map((todo) => 
    <Todo {...todo}
        key={todo.id} />
)}
复制代码

另外的实现方式是把编号递增添加到抽象方法中,使用一个全局的index来确保任何两个列表项的id不同

todoCounter = 1;
function createNewTodo(text) {
    return {
        completed: false,
        id: todoCounter++,
        text
    }
}
复制代码
much Better

一个产品化的解决方案是它应该更加健壮,能够用来创建分散的列表项. 因此我强烈推荐一个npm包shortid, 它可以快速的生成一系列‘短的 无序的 对url友好的 唯一的’ id,下面是示例代码:

var shortid = require('shortid');

function createNewTodo(text) {
    return {
        completed: false,
        id: shortid.generate(),
        text
    }
}
复制代码

或许你会很疑惑,我是否总是要生成ID,很多人认为使用index作为key也不错。的确,每次都生成Id会显得冗余。那么总结一下,在以下几种情况下可以安全的使用index作为索引。

  1. 列表不被计算和改变。
  2. 列表项没有ID属性
  3. 列表不会执行重排或筛选操作
关注下面的标签,发现更多相似文章
评论