From 485f1493be6e8a1c1a72eb66c4935bcc3a35a56f Mon Sep 17 00:00:00 2001 From: ajcumine Date: Tue, 10 Oct 2017 09:17:33 +0100 Subject: [PATCH 1/6] Added details and codepen examples for index usage as keys in lists --- content/docs/lists-and-keys.md | 2 +- content/docs/reconciliation.md | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/content/docs/lists-and-keys.md b/content/docs/lists-and-keys.md index 0d1b8c04..2adad35b 100644 --- a/content/docs/lists-and-keys.md +++ b/content/docs/lists-and-keys.md @@ -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 items can reorder as that would impact performance, and may cause issues with the reordering of components and their respective states. 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](/react/docs/reconciliation.html#recursing-on-children) if you're interested in more information. ### Extracting Components with Keys diff --git a/content/docs/reconciliation.md b/content/docs/reconciliation.md index 6a23282c..eaf085f5 100644 --- a/content/docs/reconciliation.md +++ b/content/docs/reconciliation.md @@ -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 item's index in the array as a key. This can work well if the items are never reordered. However, there will be a performance impact with reordering as React will naively update components. + +There can also be issues with the state of a component in a list if indexes are used as keys. The state of an item in a list (or any deep state inside of it) will stay attached to the original position of the item, even if the item has “moved” in the data source. This is particularly noticeable with inputs retaining their values in the original positions even when their parent components reorder or are prepended to. + +[Here](http://codepen.io/ajcumine/pen/KmVWmQ?editors=0010) is an example of the issues that can be caused by using indexes as keys on CodePen, and [here](https://codepen.io/ajcumine/pen/ZKQeJM?editors=0010) is a updated version of the same example showing how not using indexes as keys will fix these reordering, sorting, and prepending issues. ## Tradeoffs From 6f0dfce69e1a6c58c362bd4d0a420600785f3209 Mon Sep 17 00:00:00 2001 From: ajcumine Date: Tue, 10 Oct 2017 19:25:26 +0100 Subject: [PATCH 2/6] fix link to reconciliation docs --- content/docs/lists-and-keys.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/lists-and-keys.md b/content/docs/lists-and-keys.md index 2adad35b..42f82551 100644 --- a/content/docs/lists-and-keys.md +++ b/content/docs/lists-and-keys.md @@ -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 impact performance, and may cause issues with the reordering of components and their respective states. 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](/react/docs/reconciliation.html#recursing-on-children) if you're interested in more information. +We don't recommend using indexes for keys if the items can reorder as that would impact performance, and may cause issues with the reordering of components and their respective states. 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 From ae716058c2f6244f1c84d7d585f321542328bd8f Mon Sep 17 00:00:00 2001 From: ajcumine Date: Tue, 10 Oct 2017 19:28:32 +0100 Subject: [PATCH 3/6] revert update to usage of index as key wording --- content/docs/reconciliation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/reconciliation.md b/content/docs/reconciliation.md index eaf085f5..e209ff2e 100644 --- a/content/docs/reconciliation.md +++ b/content/docs/reconciliation.md @@ -138,7 +138,7 @@ 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. However, there will be a performance impact with reordering as React will naively update components. +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. There can also be issues with the state of a component in a list if indexes are used as keys. The state of an item in a list (or any deep state inside of it) will stay attached to the original position of the item, even if the item has “moved” in the data source. This is particularly noticeable with inputs retaining their values in the original positions even when their parent components reorder or are prepended to. From ee6f7925aa7e1a703650c3857345982ea8e50332 Mon Sep 17 00:00:00 2001 From: ajcumine Date: Fri, 17 Nov 2017 16:27:05 +0000 Subject: [PATCH 4/6] move reconciliation index as keys examples from codepen into ./examples --- content/docs/reconciliation.md | 2 +- examples/reconciliation/index-used-as-key.js | 103 ++++++++++++++++++ .../reconciliation/no-index-used-as-key.js | 103 ++++++++++++++++++ 3 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 examples/reconciliation/index-used-as-key.js create mode 100644 examples/reconciliation/no-index-used-as-key.js diff --git a/content/docs/reconciliation.md b/content/docs/reconciliation.md index e209ff2e..245b5c71 100644 --- a/content/docs/reconciliation.md +++ b/content/docs/reconciliation.md @@ -142,7 +142,7 @@ As a last resort, you can pass item's index in the array as a key. This can work There can also be issues with the state of a component in a list if indexes are used as keys. The state of an item in a list (or any deep state inside of it) will stay attached to the original position of the item, even if the item has “moved” in the data source. This is particularly noticeable with inputs retaining their values in the original positions even when their parent components reorder or are prepended to. -[Here](http://codepen.io/ajcumine/pen/KmVWmQ?editors=0010) is an example of the issues that can be caused by using indexes as keys on CodePen, and [here](https://codepen.io/ajcumine/pen/ZKQeJM?editors=0010) is a updated version of the same example showing how not using indexes as keys will fix these reordering, sorting, and prepending issues. +[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 diff --git a/examples/reconciliation/index-used-as-key.js b/examples/reconciliation/index-used-as-key.js new file mode 100644 index 00000000..9d7ff13c --- /dev/null +++ b/examples/reconciliation/index-used-as-key.js @@ -0,0 +1,103 @@ +const ToDo = (props) => ( + + + + + +); + +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( +
+ key=index
+ + + + + + + + + { + this.state.list.map((todo, index) => ( + + )) + } +
IDcreated at
+
+ ) + } +} + +ReactDOM.render( + , + document.getElementById('root') +); diff --git a/examples/reconciliation/no-index-used-as-key.js b/examples/reconciliation/no-index-used-as-key.js new file mode 100644 index 00000000..b083118d --- /dev/null +++ b/examples/reconciliation/no-index-used-as-key.js @@ -0,0 +1,103 @@ +const ToDo = (props) => ( + + + + + +); + +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( +
+ key=id
+ + + + + + + + + { + this.state.list.map((todo, index) => ( + + )) + } +
IDcreated at
+
+ ) + } +} + +ReactDOM.render( + , + document.getElementById('root') +); From 211963bfd4672a3d731e0172899b9fcb95c19cae Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Fri, 17 Nov 2017 10:09:21 -0800 Subject: [PATCH 5/6] Wordsmithing --- content/docs/lists-and-keys.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/lists-and-keys.md b/content/docs/lists-and-keys.md index 42f82551..4c3f3cf6 100644 --- a/content/docs/lists-and-keys.md +++ b/content/docs/lists-and-keys.md @@ -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 impact performance, and may cause issues with the reordering of components and their respective states. 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. +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 From 383a7cbb834fd9ab3e2a0bf03ac730b2ef23ce15 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Fri, 17 Nov 2017 10:16:33 -0800 Subject: [PATCH 6/6] Wordsmithing --- content/docs/reconciliation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/docs/reconciliation.md b/content/docs/reconciliation.md index 245b5c71..f76e83f1 100644 --- a/content/docs/reconciliation.md +++ b/content/docs/reconciliation.md @@ -138,9 +138,9 @@ 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. -There can also be issues with the state of a component in a list if indexes are used as keys. The state of an item in a list (or any deep state inside of it) will stay attached to the original position of the item, even if the item has “moved” in the data source. This is particularly noticeable with inputs retaining their values in the original positions even when their parent components reorder or are prepended to. +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.