Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of `ListView`
Asked Answered
K

19

133

I built an app with ReactNative both for iOS and android with a ListView. When populating the listview with a valid datasource, the following warning is printed at the bottom of the screen:

Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of ListView.

What is the purpose of this warning? After the message they link to this page, where complete different things are discussed which have nothing to do with react native, but with web based reactjs.

My ListView is built with those statements:

render() {
    var store = this.props.store;

    return (

        <ListView
            dataSource={this.state.dataSource}
            renderHeader={this.renderHeader.bind(this)}
            renderRow={this.renderDetailItem.bind(this)}
            renderSeparator={this.renderSeparator.bind(this)}
            style={styles.listView}
            />

    );
}

My DataSource consists of something like:

    var detailItems = [];

    detailItems.push( new DetailItem('plain', store.address) );
    detailItems.push( new DetailItem('map', '') );

    if(store.telefon) {
        detailItems.push( new DetailItem('contact', store.telefon, 'Anrufen', 'fontawesome|phone') );
    }
    if(store.email) {
        detailItems.push( new DetailItem('contact', store.email, 'Email', 'fontawesome|envelope') );
    }
    detailItems.push( new DetailItem('moreInfo', '') );

    this.setState({
        dataSource: this.state.dataSource.cloneWithRows(detailItems)
    });

And the ListView-Rows are rendered with stuff like:

        return (
            <TouchableHighlight underlayColor='#dddddd'>
                <View style={styles.infoRow}>
                    <Icon
                                name={item.icon}
                                size={30}
                                color='gray'
                                style={styles.contactIcon}
                                />
                    <View style={{ flex: 1}}>
                        <Text style={styles.headline}>{item.headline}</Text>
                        <Text style={styles.details}>{item.text}</Text>
                    </View>
                    <View style={styles.separator}/>
                </View>
            </TouchableHighlight>
        );

Everything works fine and as expected, except the warning which seems to be complete nonsense to me.

Adding a key-property to my "DetailItem"-Class didn't solve the issue.

This is, what really will be passed to the ListView as a result of "cloneWithRows":

_dataBlob: 
I/ReactNativeJS( 1293):    { s1: 
I/ReactNativeJS( 1293):       [ { key: 2,
I/ReactNativeJS( 1293):           type: 'plain',
I/ReactNativeJS( 1293):           text: 'xxxxxxxxxx',
I/ReactNativeJS( 1293):           headline: '',
I/ReactNativeJS( 1293):           icon: '' },
I/ReactNativeJS( 1293):         { key: 3, type: 'map', text: '', headline: '', icon: '' },
I/ReactNativeJS( 1293):         { key: 4,
I/ReactNativeJS( 1293):           type: 'contact',
I/ReactNativeJS( 1293):           text: '(xxxx) yyyyyy',
I/ReactNativeJS( 1293):           headline: 'Anrufen',
I/ReactNativeJS( 1293):           icon: 'fontawesome|phone' },
I/ReactNativeJS( 1293):         { key: 5,
I/ReactNativeJS( 1293):           type: 'contact',
I/ReactNativeJS( 1293):           text: '[email protected]',
I/ReactNativeJS( 1293):           headline: 'Email',
I/ReactNativeJS( 1293):           icon: 'fontawesome|envelope' },
I/ReactNativeJS( 1293):         { key: 6, type: 'moreInfo', text: '', headline: '', icon: '' } ] },

As one key see, each record has a key property. The warning still exists.

Knavery answered 3/1, 2016 at 12:1 Comment(6)
Most likely your DetailItems need to have keys. If they already have unique keys, you need show the other render methods (renderHeader, renderDetailItem, renderSeparator). They are working fine and expected until the data source is modified in someway (rows are removed, for example) at which point React won't know what to do with them when they don't have an unique identifier.Pastypat
What do you mean with "keys"? A Property called "key"?Knavery
Yes. See facebook.github.io/react/docs/…Pastypat
It doesnt solve it. I added a key property to my data structure and updated the original question with more detailled data. Listing plain data, which results to the DataSource, have a key per each record. This warning remains.Knavery
It might come from the other render methods too (renderHeader, renderDetailItem, renderSeparator)Pastypat
This can help you #28329882Plimsoll
P
119

I've had exactly the same problem as you for a while now, and after looking at some of the suggestions above, I finally solved the problem.

It turns out (at least for me anyway), I needed to supply a key (a prop called 'key') to the component I am returning from my renderSeparator method. Adding a key to my renderRow or renderSectionHeader didn't do anything, but adding it to renderSeparator made the warning go away.

Piling answered 19/1, 2016 at 4:48 Comment(2)
In my case, I just deleted renderSeparator and moved my <Separator> into the body of the renderRow return value.Sarto
Before reading this I wasted about 8 hours tracking down what I thought was a problem with my JSON data. If there was a stack overflow :taco: I'd give you one!Tryparsamide
T
100

You need to provide a key.

Try doing this in your ListView Rows if you have a key property:

<TouchableHighlight key={item.key} underlayColor='#dddddd'>

If not, try just adding the item as the key:

<TouchableHighlight key={item} underlayColor='#dddddd'>
Tamaru answered 3/1, 2016 at 16:29 Comment(1)
This answer sort of changed my understanding of the language. I specifically thought that I could not do this so I was unsure how to proceed. Now I understand that the HTML looking code is just another syntax, it's not what is going to rendered it's just easier to read that way.Nephron
V
39

You can also use the iteration count (i) as the key:

render() {
    return (
      <ol>
        {this.props.results.map((result, i) => (
          <li key={i}>{result.text}</li>
        ))}
      </ol>
    );
}
Virginia answered 4/5, 2017 at 19:25 Comment(1)
This may not work when the array changes. This answer explains it with an example: https://mcmap.net/q/53124/-understanding-unique-keys-for-array-children-in-react-jsArise
C
24

You are getting the same error if you have an empty tag <> as the top level of your structure inside a loop:

return <select>
    {Object.values(countries).map(c => {
        return (<>           {/*   <== EMPTY TAG!   */}
            <option value={c.id}>{c.name}</option>
            <States countryId={c.id} />
        </>)
    }
</select>

You can use full syntax of <React.Fragment> instead of short <> and add your key to the full tag:

import {Fragment} from 'react';

return <select>
    {Object.values(countries).map(c => {
        return (<Fragment key={c.id}>   {/* You can also use <React.Fragment> without import */}
            <option value={c.id}>{c.name}</option>
            <States countryId={c.id} />
        </Fragment>)
    }
</select>
Cyclonite answered 5/6, 2021 at 6:51 Comment(1)
works like a dream in react.native, much appreciated!Hypotaxis
P
22

Change your code from:

render() {
    return (
      <ol>
        {this.props.results.map((result) => (
          <li>{result.text}</li>
        ))}
      </ol>
    );
}

To:

render() {
    return (
      <ol>
        {this.props.results.map((result) => (
          <li key={result.id}>{result.text}</li>
        ))}
      </ol>
    );
}

Then solved.

Pettway answered 9/10, 2016 at 13:13 Comment(0)
G
13

Add a prop 'key' to the rendering root component of the list.

<ScrollView>
      <List>
          {this.state.nationalities.map((prop, key) => {
             return (
               <ListItem key={key}>
                  <Text>{prop.name}</Text>
               </ListItem>
             );
          })}
      </List>
</ScrollView>
Gadhelic answered 11/10, 2018 at 10:47 Comment(0)
D
7

This warning comes when you don't add a key to your list items.As per react js Docs -

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:

const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
  <li key={number.toString()}>
    {number}
  </li>
);

The best way to pick a key is to use a string that uniquely identifies a list item among its siblings. Most often you would use IDs from your data as keys:

const todoItems = todos.map((todo) =>
  <li key={todo.id}>
    {todo.text}
  </li>
);

When you don’t have stable IDs for rendered items, you may use the item index as a key as a last resort

const todoItems = todos.map((todo, index) =>
  // Only do this if items have no stable IDs
  <li key={index}>
    {todo.text}
  </li>
);
Disparity answered 3/11, 2017 at 8:10 Comment(0)
C
7

Check: key == undef !!!

You got also the warn message:

Each child in a list should have a unique "key" prop.

if your code is complete right, but if on

<MyComponent key={someValue} />

someValue is undefined.

Please check this first! You can save hours!

Crosslink answered 12/4, 2019 at 14:40 Comment(0)
B
4

I fixed it by add a property to renderSeparator Component,the code is here:

_renderSeparator(sectionID,rowID){
    return (
        <View style={styles.separatorLine} key={"sectionID_"+sectionID+"_rowID_"+rowID}></View>
    );
}

The key words of this warning is "unique", sectionID + rowID return a unique value in ListView.

Braun answered 8/7, 2016 at 6:17 Comment(0)
P
3

Assuming the renderDetailItem method has the following signature...

(rowData, sectionID, rowID, highlightRow) 

Try doing this...

<TouchableHighlight key={rowID} underlayColor='#dddddd'>
Peyter answered 4/1, 2016 at 8:48 Comment(0)
D
3

The specific code I used to fix this was:

  renderSeparator(sectionID, rowID, adjacentRowHighlighted) {
    return (
      <View style={styles.separator} key={`${sectionID}-${rowID}`}/>
    )
  }

I'm including the specific code because you need the keys to be unique--even for separators. If you do something similar e.g., if you set this to a constant, you will just get another annoying error about reuse of keys. If you don't know JSX, constructing the callback to JS to execute the various parts can be quite a pain.

And on the ListView, obviously attaching this:

<ListView
  style={styles.listview}
  dataSource={this.state.dataSource}
  renderRow={this.renderRow.bind(this)}
  renderSeparator={this.renderSeparator.bind(this)}
  renderSectionHeader={this.renderSectionHeader.bind(this)}/>

Credit to coldbuffet and Nader Dabit who pointed me down this path.

Dated answered 2/7, 2016 at 0:49 Comment(0)
W
2

Here is based on my understanding. Hopefully it's helpful. It's supposed to render a list of any components as the example behind. The root tag of each component needs to have a key. It doesn't have to be unique. It cannot be key=0, key='0', etc. It looks the key is useless.

render() {
    return [
        (<div key={0}> div 0</div>),
        (<div key={1}> div 2</div>),
        (<table key={2}><tbody><tr><td> table </td></tr></tbody></table>),
        (<form key={3}> form </form>),
    ];
}
Whitehorse answered 19/6, 2019 at 19:29 Comment(0)
C
2

This worked for me.

<View>
    {
        array.map((element, index) => {
            return(
                <React.Fragment key= {`arrayElement${index}`}>
                    {element}
                </React.Fragment>
            );
        })
    }
</View>
Consecration answered 29/7, 2021 at 5:4 Comment(4)
This suppresses the warning but an array index is not a good choice of key.Reproduction
@Reproduction Could you please tell me why that is the case and what would be a better choice of key?Consecration
If the list changes then you want to minimize the number of key changes. If you have a logical ID, such as a data-base ID, that is better.Reproduction
That makes a lot of sense. Thanks for clarifying.Consecration
E
1

In my case, I was using the Semantic UI React "Card" view. Once I added a key to each card I constructed, the warning went away, for example:

return (
        <Card fluid key={'message-results-card'}>
          ...
        </Card>
)
Eba answered 13/4, 2020 at 14:46 Comment(0)
I
0

Seems like both the conditions are met, perhaps key('contact') is the issue

 if(store.telefon) {
    detailItems.push( new DetailItem('contact', store.telefon, 'Anrufen', 'fontawesome|phone') );
}
if(store.email) {
    detailItems.push( new DetailItem('contact', store.email, 'Email', 'fontawesome|envelope') );
}
Italianate answered 3/1, 2016 at 12:34 Comment(1)
no, contact is not a key. In between i added a real property called "key" to my data structure and updated my question passing more detailed data. Nothing helpes. The warning remains.Knavery
P
0

This cannot be emphasized enough:

Keys only make sense in the context of the surrounding array.

"For example, if you extract a ListItem component, you should keep the key on the <ListItem /> elements in the array rather than on the <li> element in the ListItem itself." -- https://reactjs.org/docs/lists-and-keys.html#extracting-components-with-keys

Polloch answered 4/6, 2019 at 21:2 Comment(0)
G
0

The thing that tripped me up on this problem was that I thought that the need for a key applied to what looks like 'real' or DOM HTML elements as opposed to JSX elements that I have defined.

Of course with React we are working with a virtual DOM so the React JSX elements we define <MyElement> are just as important to it as the elements that look like real DOM HTML elements like <div>.

Does that make sense?

Glengarry answered 26/3, 2020 at 6:45 Comment(1)
It makes sense, but isn't the whole answer. Just because you add a key to the element that React is complaining about doesn't mean the error will go away. I'm about ready to give up and replace my WIP Web dashboard with a CLI tool.Felon
G
0

For me, when I trying to insert ID into the .maps method, the error still kept showing:

<iconsList.map(icon =>{
  switch(icon) {
    case 'css':
      return <img key={icon.ID}....

However I resolved that by directly specifying the iterable item as key.

return iconsList.map(icon =>{
  switch(icon) {
    case 'css':
      return <img key={icon} className="icon" src={require('../resources/css.png')} alt={id} ></img>;
    case 'js':
      return <img key={icon} className="icon" src={require('../resources/js.png')} alt={id} ></img>;
    default:
      return <h5 key={icon} className="icon">*</h5>;
  }
})
Gaudy answered 6/3, 2023 at 21:1 Comment(0)
P
-1

If you're using the <Fade in> element for a react application please add key={} attribute in it as well or you'll see an error in the console.

Panlogism answered 16/10, 2018 at 9:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.