Merge pull request #111 from ajcumine/docs-no-keys-in-lists

[Documentation] Details/Example for Indexes as keys
This commit is contained in:
Brian Vaughn
2017-11-17 10:16:58 -08:00
committed by GitHub
4 changed files with 212 additions and 2 deletions

View File

@@ -130,7 +130,7 @@ const todoItems = todos.map((todo, index) =>
);
```
We don't recommend using indexes for keys if the items can reorder, as that would be slow. You may read an [in-depth explanation about why keys are necessary](/docs/reconciliation.html#recursing-on-children) if you're interested.
We don't recommend using indexes for keys if the order of items may change. This can negatively impact performance and may cause issues with component state. If you choose not to assign a key to your list items then React will use indexes as keys. You may read an [in-depth explanation about why keys are necessary](/docs/reconciliation.html#recursing-on-children) if you're interested in more information.
### Extracting Components with Keys

View File

@@ -138,7 +138,11 @@ In practice, finding a key is usually not hard. The element you are going to dis
When that's not the case, you can add a new ID property to your model or hash some parts of the content to generate a key. The key only has to be unique among its siblings, not globally unique.
As a last resort, you can pass item's index in the array as a key. This can work well if the items are never reordered, but reorders will be slow.
As a last resort, you can pass an item's index in the array as a key. This can work well if the items are never reordered, but reorders will be slow.
Reorders can also cause issues with component state when indexes are used as keys. Component instances are updated and reused based on their key. If the key is an index, moving an item changes it. As a result, component state for things like controlled inputs can get mixed up and updated in unexpected ways.
[Here](codepen://reconciliation/index-used-as-key) is an example of the issues that can be caused by using indexes as keys on CodePen, and [here](codepen://reconciliation/no-index-used-as-key) is a updated version of the same example showing how not using indexes as keys will fix these reordering, sorting, and prepending issues.
## Tradeoffs

View File

@@ -0,0 +1,103 @@
const ToDo = (props) => (
<tr>
<td><label>{props.id}</label></td>
<td><input/></td>
<td><label>{props.createdAt.toTimeString()}</label></td>
</tr>
);
class ToDoList extends React.Component {
constructor() {
super();
const date = new Date();
const todoCounter = 1;
this.state = {
todoCounter: todoCounter,
list: [
{ id: todoCounter, createdAt: date },
]
}
}
sortByEarliest() {
const sortedList = this.state.list.sort((a, b) => {
return a.createdAt - b.createdAt;
});
this.setState({
list: [...sortedList]
})
}
sortByLatest() {
const sortedList = this.state.list.sort((a, b) => {
return b.createdAt - a.createdAt;
});
this.setState({
list: [...sortedList]
})
}
addToEnd() {
const date = new Date();
const nextId = this.state.todoCounter + 1;
const newList = [
...this.state.list,
{ id: nextId, createdAt: date }
];
this.setState({
list: newList,
todoCounter: nextId
});
}
addToStart() {
const date = new Date();
const nextId = this.state.todoCounter + 1;
const newList = [
{ id: nextId, createdAt: date },
...this.state.list
];
this.setState({
list: newList,
todoCounter: nextId
});
}
render() {
return(
<div>
<code>key=index</code><br/>
<button onClick={this.addToStart.bind(this)}>
Add New to Start
</button>
<button onClick={this.addToEnd.bind(this)}>
Add New to End
</button>
<button onClick={this.sortByEarliest.bind(this)}>
Sort by Earliest
</button>
<button onClick={this.sortByLatest.bind(this)}>
Sort by Latest
</button>
<table>
<tr>
<th>ID</th><th></th><th>created at</th>
</tr>
{
this.state.list.map((todo, index) => (
<ToDo
key={index}
{...todo}
/>
))
}
</table>
</div>
)
}
}
ReactDOM.render(
<ToDoList />,
document.getElementById('root')
);

View File

@@ -0,0 +1,103 @@
const ToDo = (props) => (
<tr>
<td><label>{props.id}</label></td>
<td><input/></td>
<td><label>{props.createdAt.toTimeString()}</label></td>
</tr>
);
class ToDoList extends React.Component {
constructor() {
super();
const date = new Date();
const toDoCounter = 1;
this.state = {
list: [
{ id: toDoCounter, createdAt: date },
],
toDoCounter: toDoCounter
}
}
sortByEarliest() {
const sortedList = this.state.list.sort((a, b) => {
return a.createdAt - b.createdAt;
});
this.setState({
list: [...sortedList]
})
}
sortByLatest() {
const sortedList = this.state.list.sort((a, b) => {
return b.createdAt - a.createdAt;
});
this.setState({
list: [...sortedList]
})
}
addToEnd() {
const date = new Date();
const nextId = this.state.toDoCounter + 1;
const newList = [
...this.state.list,
{ id: nextId, createdAt: date }
];
this.setState({
list: newList,
toDoCounter: nextId
});
}
addToStart() {
const date = new Date();
const nextId = this.state.toDoCounter + 1;
const newList = [
{ id: nextId, createdAt: date },
...this.state.list
];
this.setState({
list: newList,
toDoCounter: nextId
});
}
render() {
return(
<div>
<code>key=id</code><br/>
<button onClick={this.addToStart.bind(this)}>
Add New to Start
</button>
<button onClick={this.addToEnd.bind(this)}>
Add New to End
</button>
<button onClick={this.sortByEarliest.bind(this)}>
Sort by Earliest
</button>
<button onClick={this.sortByLatest.bind(this)}>
Sort by Latest
</button>
<table>
<tr>
<th>ID</th><th></th><th>created at</th>
</tr>
{
this.state.list.map((todo, index) => (
<ToDo
key={todo.id}
{...todo}
/>
))
}
</table>
</div>
)
}
}
ReactDOM.render(
<ToDoList />,
document.getElementById('root')
);