Reputation: 103
import React from 'react'
import ReactDOM from 'react-dom'
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({})
}
render() {
return (
<ul>
<div onClick={this.click.bind(this)}>reverse</div>
{
this.state.list.map(function(item, index) {
return (
<Li key={item.id} val={item.val}></Li>
)
}.bind(this))
}
</ul>
)
}
}
class Li extends React.Component{
constructor(props) {
super(props)
}
componentDidMount() {
console.log('===did===')
}
componentWillUpdate(nextProps, nextState) {
console.log('===mount====')
}
render() {
return (
<li>
{this.props.val}
<input type="text"></input>
</li>
)
}
}
ReactDOM.render(<App/>, document.getElementById('app'))
when I set key as item.id
, and I set three input tags a
, b
, c
;
when I click reverse, the component Li will mount, input will reverse
when I change key as index
, when I click reverse, the component Li update, and the input tag will not change,
I want to know how does it happen? Does Anyone have found out how key works?
Upvotes: 1
Views: 2680
Reputation: 52133
As @DuncanThacker explained the key
is used to identify unique elements, so that between 2 render passes React knows if an element is a new thing or an updated thing. Remember that React diffs each render to determine what actually changed in the DOM.
Now to explain the behavior you're seeing:
when I set key as
item.id
, and I set three input tagsa, b, c
;when I click reverse, the component Li will mount, input will reverse
When you use id
as key
you can re-order the array but React will only create and mount the node the first time. You are outputting ===mount===
inside componentWillUpdate
, which is why you see that misleading output, but React is only updating the nodes (moving them as needed). The state of the inner input
also follows each <Li>
component to its new position, since React correctly understands that the <Li>
moved, not simply re-drawn with different content.
when I change key as
index
, when I click reverse, the component Li update, and the input tag will not change
When you use index
as key
React effectively sees each render pass as rendering the array in the same order but with different content, since each element's key
is the same index
no matter what order the array content is in. This is also why the inner input
stays in the same place, even though the val
label is rendered in a different position. And this is exactly why you should not use index
as key
.
You could illustrate it like this:
|-----------------------|---------------------|--------------------------|
| Before | After | Change |
|-----------------------|---------------------|--------------------------|
Array:
|-----------------------|---------------------|--------------------------|
| { id: 1, val: "A" } | { id: 3, val: "C" } | Moved from last to first |
| { id: 2, val: "B" } | { id: 2, val: "B" } | None |
| { id: 3, val: "C" } | { id: 1, val: "A" } | Moved from first to last |
|-----------------------|---------------------|--------------------------|
Render key
from index
:
|-----------------------|---------------------|--------------------------|
| <Li key=0 val="A"> | <Li key=0 val="C"> | Val changed "A" to "C" |
| <Li key=1 val="B"> | <Li key=1 val="B"> | None |
| <Li key=2 val="C"> | <Li key=2 val="A"> | Val changed "C" to "A" |
|-----------------------|---------------------|--------------------------|
Render key
from item.id
:
|-----------------------|---------------------|--------------------------|
| <Li key=1 val="A"> | <Li key=3 val="C"> | Moved from bottom to top |
| <Li key=2 val="B"> | <Li key=2 val="B"> | None |
| <Li key=3 val="C"> | <Li key=1 val="A"> | Moved from top to bottom |
|-----------------------|---------------------|--------------------------|
Summary: you should always use id
or some other unique element identifier as key
.
See also: https://medium.com/@robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318
Upvotes: 6
Reputation: 5188
The "key" attribute is used by React to determine whether to render a brand new instance of the component, or whether the update an existing one. So using the item id as the key will mean that the component for that item will not be destroyed and re-created. If you add a new item to the list it will create a new component, and if you remove an item it will destroy the old one, but if you update an item without change its ID, the component will just update.
It's useful for dynamic lists because it reduces weird cases where a mounted component switches from rendering one list item to rendering another list item, when really it should be a brand new component instance.
Upvotes: 0