一篇文章告訴你React裡為什麼不能用index作為key

2014?發表於2017-12-14

文章首發於github部落格

之前在寫react的時候,當我們做map迴圈的時候,當我們沒有一個唯一id來標識每一項item的時候,我們可能會選擇使用index

data.map((item, index) => {
    return <li key={index}>{item}</li> 
})
複製程式碼

但是其實當你使用index來作為唯一key的時候,其實是由一個大坑的,什麼坑呢?必須坑了你才知道,來看下面的這種情況:

class App extends React.Component{
	constructor(props) {
		super(props)
		this.state = {
			list: [{id: 1,val: 'aa'}, {id: 2, val: 'bb'}, {id: 3, val: 'cc'}]
		}
	}

	click() {
		this.state.list.reverse()
		this.setState({})
	}
	splice() {
		this.state.list.splice(1,1)
		this.setState({})
	}

	render() {
		return (
            <ul>
                <div onClick={this.splice.bind(this)}>delete</div>
                <div onClick={this.click.bind(this)}>reverse</div>
                {
                	this.state.list.map(function(item, index) {
                		return (
                            <Li key={index} val={item.val}></Li>
                		)
                	}.bind(this))
                }
            </ul>
		)
	}
}

class Li extends React.Component{
	constructor(props) {
		super(props)
	}
	componentDidMount() {
		console.log('===mount===')
	}
	componentWillUpdate(nextProps, nextState) {
		console.log('===update====')
	}
	render() {
		return (
            <li>
                {this.props.val}
                <input type="text"></input>
            </li>
		)
	}
}
複製程式碼

頁面渲染好了之後,3個input輸入框依次輸入1,2,3: 當我們用index作為key的時候,點選reverse會發現,input輸入框還是1,2,3順序顯示,但是這並不符合我們的預期,控制檯中此時列印的也是update; 當我們用物件中的id作為key的時候,點選reverse,此時神奇的事情發生了,input輸入框變成了3,2,1,符合我們的預期,控制檯此時列印的也是update;

為什麼會這樣呢?

當我們傳入index作為key時,此時的key為0,1,2, 當我們點選reverse重新排序後,index傳進去的key還是0,1,2,此時react比較key=0時,發現只需要更新子節點的值就可以,於是只把item替換成了cc,而input則相反, 當我們傳入id作為index的時候,,點選reverse後,此時的key變成了3,2,1,根據react的diff演算法,react還是能分辨出只需要移動子節點即可完成更新,因此input也隨之變化。

那說了這麼多,其實對於index作為key我們是不推薦的,除非你能夠保證他們不會發生變化。

參考文獻 index as a key is an anti-pattern, 有問題可以在部落格下方留言,感謝star

相關文章