I use React to render a list of data, but each item in the data have not been assigned id or uuid or something like identify property. Can I use the item index as the key? like:
data.map((item, index) => {
<Item key={index}></Item>
})
What I concerned is if some other list on the page also use the order index as the child component key, would it matter? Should the key be a unique identify?
The key only needs to be unique to that list.
I also had that worry initially.
From the official docs:
Remember that the key only has to be unique among its siblings, not
globally unique.
Keys should be stable, predictable, and unique. Unstable keys (like
those produced by Math.random()) will cause many nodes to be
unnecessarily re-created, which can cause performance degradation and
lost state in child components.
Read more here: Reconciliation - Keys
You can do this if you not going to move your elements within list. Your elements will have different indices each time you move them, so react can't track which elements a moved and which just changed their data.
indices must be unique whithin their lists, they may intersect with other lists.
Related
I understand when using arrays of Components the key property is assumed to be the index of the array, and should be explicitly set. Are the children of those children recommended to be explicitly set?
{arr.map(item, i) => {
<Parent
key={item.ID}
>
<Child
key={`child${item.ID`} //required to ensure correct reconciliation?
/>
</Parent>
}
Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity (source)
The general purpose of a key is to optimize React's rendering performance. If you have a list of items, giving a key tells React how to remember this item for subsequent renders. If you use an array index, that can defeat the purpose of the key (if the order of those elements change). Its better to use a unique ID or something more specific to the entity being rendered.
With that context, The parent element is what needs the key, so React can do its optimizations. The children of that "dynamic" element are attached to that parent / its key so there's no need to apply a key on the children`. Just the parents that are rendered in a loop :)
only the outermost items you iterate you need to set a key on. As far as I see there's a single child component per Parent, so no need to worry abot key in this case
You do not need to explicitly set keys on the children.
The follow is a good article on using keys: https://reactjs.org/docs/lists-and-keys.html
It explicitly recommends not using indexes as keys for an array:
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. Check out Robin Pokorny’s article for an in-depth explanation on the negative impacts of using an index as a key. If you choose not to assign an explicit key to list items then React will default to using indexes as keys.
No, you only need it for the outer component (Parent). Documentation
All the information I could find highlight using keys when rendering lists, for example:
<ul>
{array.map((item, index) => <li key={index}>{item}</li>)}
</ul>
Are there situations other than lists where it's also helpful to provide keys?
Are there downsides to simply providing keys for every non-static element on the page?
You may use keys to reset a component state. See this article for more information : https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#recommendation-fully-uncontrolled-component-with-a-key
A key is recommended for lists in react because react uses this to identify items that are added, changed or removed.
Keys
Keys help React identify which items have changed, are added, or are
removed. Keys should be given to the elements inside the array to give
the elements a stable identity.
An example with a function component:
function MyListComponent (props) {
const list = props.list;
const items= list.map((item) =>
<li key={item.toString()}>
{item}
</li>
);
return (
<ul>{items}</ul>
);
}
const array = [1, 2, 3, 4, 5];
ReactDOM.render(
<MyListComponent list={array} />,
document.getElementById('root')
);
The only rule is that it must be unique among its siblings.
If you don't use a key - if you don't use a key you'll get a warning.
An id (identifier of each item in your list) is best for this purposes - if you don't have any identifier that you can use, an index of each item in the list can be used, but its not recommended because this can cause problems in cases where the order of items change.
Keys only give a performance benefit when used in lists. When one value in a list is changed, React knows to only update that value and not the whole list. Keys are helpful because they help keep track of elements when the index changes (note: only use index as key for last resort because if the index changes (i.e. an element is added to the front/middle of a list) React will have to re render the whole list). You can read more about it here: https://reactjs.org/docs/lists-and-keys.html. As for the downsides, I'm going to take an educated guess and say including an extra key attribute that is not used anywhere will slow down your compilation time an unnoticeable amount
I have a stateful component which maintains 2 lists of different types. As an example, think in terms of cars. Each car has a make(and lets say it has unique makeId) and model(again with a unique modelId). Now my component first displays a list of makes, each having a key attribute whose value is makeId. Now upon click on a make, My stateful component updates the list with all the models(each having modelId as key attribute) of the selected make. Now it might happen that some make had the same numeric value of makeId as the numeric value of modelId now being rendered.(because makes and models go in different Relational tables). Will react face some issues while updating the list in this case?
Example:
Below is the comparison of old and new list.
<ul>
<li key=1> Some Make X</li>
<li key=2> Some Make Y</li>
</ul>
<ul>
<li key=34> Some Model X</li>
<li key=2> Some Model Y</li>
</ul>
React identifies elements with unique key prop. In a Component, if you think the list of elements will have overlapping ids you can always pass a unique key prop.
If the element you are rendering is relevant to make, pass in the key as
<div key={`make_${makeId}`} />
If the element you are rendering is relevant to model, pass in the key as
<div key={`model_${modelId}`} />
Now it might happen that some make had the same numeric value of
makeId as the numeric value of modelId now being rendered.
From the question, it seems safe to assume that makes and models are two different arrays being rendered in different enclosures. Using these variables modelId and makeId as ID attributes in HTML, would need you to prepend a string to it, such as model-${modelId} or make-${makeId} because they have to be unique across the page.
However incase of React Keys, they have do not have to be global across the page, but only inside an Array to distinguish their siblings. So you can safely use modelId and makeId as keys without any modification.
From the docs:
Keys used within arrays should be unique among their siblings. However
they don’t need to be globally unique.
According to the docs https://reactjs.org/docs/lists-and-keys.html#keys Keys help React identify which items have changed, are added, or are removed. It means that key will be used when your previous and new components are the same Component class and have the same props. As you list item components are completely changed (from model to make and vice versa), they will be re-rendered always. So there will be no render issue in this case
I have an array of number that I wish to render in a tabular form. The array is returned from an API call, not generated by my app.
The data may change but is unlikely to do so, and in any case there are only twenty odd values, so re-rendering the whole table is not really a problem.
A simple data.map(value => <td>{value}</td> should do it.
But I keep getting an Each child in an array or iterator should have a unique "key" prop. warning. Is there any way that I can tell React that there is no key and that I wish it to re-render the whole table if anything changes.
Alternatively, is there any way that I can generate a unique key for each entry? The data items are not guaranteed to be unique.
I should add that I understand what keys are for and why they are useful, but in this instance I do not have any and the easiest thing would be not to use them, since there is unlikely to be a re-render.
You can use the index as the key. I think its worth reiterating that using the index as the key only works fine in the very specific scenario that the OP is facing.
This is particularly annoying when requirements change and all of sudden the list is being modified. This shows up as items not being updated during the render because the item updated has the same key (the index), its value is different, but react only cares about the key.
In cases where your data has no unique key. You should use some function that generates a unique id for each item. A simple version of that function just increments a global counter:
// Declared globally (as in attached to window object or equivalent)
var myuniqueidcounter = 0;
function uniqueId() {
myuniqueidcounter += 1
return myuniqueidcounter;
}
// Do this in the props change or whereever your data gets passed in
let keyedData = data.map(value => Object.assign(value, { Id: uniqueId() });
// In render
data.map(value => <td key={value.Id}>{value}</td>
That way, on multiple render calls, the ids returned are always unique. We assign the key when we get the data to avoid having to re-render the entire list on each call to render().
However, this case is actually pretty rare as you can usually find some combination of the backing data that will produce a unique key for each entry.
If you do go index-as-key
This article lists 3 conditions that should be met when choosing index-as-key approach that I think is a good check list:
The list and items are static–they are not computed and do not change;
The items in the list have no ids;
The list is never reordered or filtered.
data.map((value,index) =>{
<td key={index}>{value}</td>}
)
or
data.map((value,index) =>{
let i = Math.floor(Math.random() * 1000+1)
<td key={i}>{value}</td>}
)
You can use index as your key as it is unique each time
Based on the question asked, it might be worth saying that there is an another solution to this that doesn't use keys:
e.g. The following will complain about not having unique keys:
React.createElement('div', {}, [<span>1</span>, <span>2</span>]);
However, the following renders all children with no problems (This is what JSX transformed to JS looks like for nodes with multiple children):
React.createElement('div', {}, <span>1</span>, <span>2</span>);
So if you have e.g. a smallish list of generated react element fragments and unique keys don't offer and advantage in your situation, you can do:
React.createElement.apply(null, ['div', {}, ...elementList])
Notes:
elementList is passed as arguments to React.createElement which might be an issue if the list is huge.
It will re-render all the children with each render.
Using unique keys is generally the recommended approach, and is more performant for re-rendering.
However there are occasions where you just want to render in a single shot and don't care about re-rendering, or the data is not structured in a way that you can make good use of unique keys. You can use this as a work-around if you really need to.
I want to understand what happens if I don't use keys in dynamically added components. I removed keys and it renders without any issue and just gave warning messages regarding key usage. Would someone please give some example of what the consequences are if we don't use keys?
Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity:
Example:
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
TL;DR Use unique and constant keys when rendering dynamic children, or expect strange things to happen.
One of the tricky aspects I've found during the few weeks I've been using React.js is to understand the key property you're expected to pass to a component when it's part of an array of children. It's not that you have to specify this property, things will work most of the time apart from getting this warning on the console:
Each child in an array should have a unique "key" prop. Check the render method of undefined.
By reading the linked documentation it can be easy to not see the implications of this affirmation:
When React reconciles the keyed children, it will ensure that any child with key will be reordered (instead of clobbered) or destroyed (instead of reused).
At first it looked to me it was all about performance but, as Paul O’Shannessy pointed, it's actually about identity.
The key here is to understand not everything in the DOM has a representation in React "Virtual DOM" and, because direct manipulations of the DOM (like a user changing an value or a jQuery plugin listening an element) are unnoticed by React, not using unique and constant keys will end up with React recreating the DOM node of a component when the key is not constant (and losing any untracked state in the node) or reusing a DOM node to render another component when the key is not unique (and tying its state to this other component).
Here you have a live demo showing how awful the results are:
http://jsfiddle.net/frosas/S4Dju/
Just add an item, change it, add more items and see what happens.
Also see
Source
Another useful usage of React keys other than creating dynamic elements is reseting elements when their keys change, for example in a project I had an <input/> element of type file and I wanted the element to be initialized to its initial value (no file chosen) each time the component renders, so I did the following:
Parent constructor:
this.state = {
fileInputKey: Date.now()
// other properties
};
The state object also had other properties, I just added this one for the sake of this example
Each time I wanted the input element in the child component be reset I did:
this.setState({fileInputKey: Date.now()});
Parent render:
<Child fileInputKey={this.state.fileInputKey}/>
Child render:
<input key={this.props.fileInputKey} type="file" onChange={this.onSelectFile}/>
Also see this example from React blog:
https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#recommendation-fully-uncontrolled-component-with-a-key