Предупреждение: каждый дочерний элемент в массиве или итераторе должен иметь уникальную «ключевую» опору. Проверьте метод рендеринга ListView


102

Я создал приложение с ReactNative как для iOS, так и для Android с расширениемListView . При заполнении представления списка допустимым источником данных в нижней части экрана выводится следующее предупреждение:

Предупреждение: каждый дочерний элемент в массиве или итераторе должен иметь уникальную "ключевую" опору. Проверьте метод рендеринга ListView.

Какова цель этого предупреждения? После сообщения они ссылаются на эту страницу , где обсуждаются совершенно разные вещи, которые не имеют ничего общего с react native, а с веб-реакциями.

Мой ListView построен с этими операторами:

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}
            />

    );
}

Мой источник данных состоит из чего-то вроде:

    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)
    });

И ListView-Rows отображаются с такими вещами, как:

        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>
        );

Все работает нормально, как и ожидалось, кроме предупреждения, которое мне кажется полной ерундой.

Добавление ключевого свойства в мой "DetailItem" -Class не решило проблему.

Вот что действительно будет передано в ListView в результате 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: 'xxxxxxxxx@hotmail.com',
I/ReactNativeJS( 1293):           headline: 'Email',
I/ReactNativeJS( 1293):           icon: 'fontawesome|envelope' },
I/ReactNativeJS( 1293):         { key: 6, type: 'moreInfo', text: '', headline: '', icon: '' } ] },

Как видно из одного ключа, каждая запись имеет ключевое свойство. Предупреждение все еще существует.


1
Скорее всего, вам DetailItemнужны ключи. Если у них уже есть уникальные ключи, вам нужно показать другие методы рендеринга ( renderHeader, renderDetailItem, renderSeparator). Они работают нормально и ожидаются до тех пор, пока источник данных каким-либо образом не будет изменен (например, строки будут удалены), после чего React не будет знать, что с ними делать, если у них нет уникального идентификатора.
Pete TNT

Что вы имеете в виду под «ключами»? Недвижимость под названием «ключ»?
удалить


Это не решает. Я добавил ключевое свойство в свою структуру данных и обновил исходный вопрос, добавив более подробные данные. Перечисление простых данных, которые приводят к источнику данных, имеют ключ для каждой записи. Это предупреждение остается.
удалить

Это может происходить и из других методов рендеринга (renderHeader, renderDetailItem, renderSeparator)
Пит TNT

Ответы:


93

Некоторое время у меня была точно такая же проблема, как и у вас, и, посмотрев на некоторые из приведенных выше предложений, я наконец решил проблему.

Оказывается (во всяком случае, для меня) мне нужно было предоставить ключ (свойство, называемое «ключ») компоненту, который я возвращаю из моего метода renderSeparator. Добавление ключа в мой renderRow или renderSectionHeader ничего не дало, но добавление его в renderSeparator заставило предупреждение исчезнуть.

Надеюсь, это поможет.


В моем случае я просто удалил renderSeparator и переместил свой <Separator> в тело возвращаемого значения renderRow.
boatcoder

То же самое и здесь SectionList: нужно явно добавить свойство с ключом имени для каждого, itemчтобы сделать RN счастливым.
Леон - Хан Ли

1
Прежде чем прочитать это, я потратил около 8 часов на отслеживание того, что, по моему мнению, было проблемой с моими данными JSON. Если бы произошло переполнение стека: taco: Я бы вам его дал!
hoekma 06

76

Вам нужно предоставить ключ .

Попробуйте сделать это в своих строках ListView, если у вас есть ключевое свойство:

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

Если нет, попробуйте просто добавить элемент в качестве ключа:

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

1
Мне

34

Вы также можете использовать счетчик итераций (i) как key:

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

2
Это может не сработать при изменении массива. Этот ответ объясняет это на примере: stackoverflow.com/a/43892905/960857
Крис

21

Измените свой код с:

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

Кому:

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

Потом решил.


13

Добавьте опору 'key' к корневому компоненту рендеринга списка.

<ScrollView>
      <List>
          {this.state.nationalities.map((prop, key) => {
             return (
               <ListItem key={key}>
                  <Text>{prop.name}</Text>
               </ListItem>
             );
          })}
      </List>
</ScrollView>

6

Это предупреждение появляется, когда вы не добавляете ключ к своим элементам списка. Согласно react js Docs -

Ключи помогают React определить, какие элементы были изменены, добавлены или удалены. Ключи должны быть предоставлены элементам внутри массива, чтобы придать элементам стабильную идентичность:

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

Лучший способ выбрать ключ - использовать строку, которая однозначно идентифицирует элемент списка среди его братьев и сестер. Чаще всего в качестве ключей вы будете использовать идентификаторы из ваших данных:

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

Если у вас нет стабильных идентификаторов для отображаемых элементов, вы можете использовать индекс элемента в качестве ключа в крайнем случае.

const todoItems = todos.map((todo, index) =>
  // Only do this if items have no stable IDs
  <li key={index}>
    {todo.text}
  </li>
);

4

Я исправил это, добавив свойство в компонент renderSeparator, код здесь:

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

Ключевые слова этого предупреждения - «уникальный», sectionID + rowID возвращают уникальное значение в ListView.


4

Проверяем: key = undef !!!

Вы также получили предупреждающее сообщение:

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

если ваш код правильный, но если включен

<MyComponent key={someValue} />

someValue не определено !!! Пожалуйста, сначала проверьте это. Вы можете сэкономить часы.



3

Конкретный код, который я использовал, чтобы исправить это, был:

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

Я включаю конкретный код, потому что вам нужно, чтобы ключи были уникальными - даже для разделителей. Если вы сделаете что-то подобное, например, если вы установите для него константу, вы просто получите еще одну досадную ошибку о повторном использовании ключей. Если вы не знаете JSX, создание обратного вызова JS для выполнения различных частей может быть довольно сложной задачей.

И в ListView, очевидно, прикрепив это:

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

Благодарим coldbuffet и Надера Дабита, которые указали мне на этот путь.


2

Здесь основано на моем понимании. Надеюсь, это поможет. Он должен отображать список любых компонентов в качестве примера. Корневой тег каждого компонента должен иметь расширение key. Он не обязательно должен быть уникальным. Это не может быть key=0, key='0'и т.д. Это выглядит ключ бесполезно.

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>),
    ];
}

0

Похоже, что оба условия соблюдены, возможно, проблема в ключевом («контакте»)

 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') );
}

нет, контакт не является ключевым. Между тем я добавил недвижимость под названием «ключ» в свою структуру данных и обновил свой вопрос, передав более подробные данные. Ничего не помогает. Предупреждение остается.
удалить

0

Это невозможно переоценить:

Ключи имеют смысл только в контексте окружающего массива .

«Например, если вы извлекаете компонент ListItem, вы должны хранить ключ в элементах <ListItem /> в массиве, а не в элементе <li> в самом ListItem». - https://reactjs.org/docs/lists-and-keys.html#extracting-components-with-keys


0

Что меня сбило с толку в этой проблеме, так это то, что я подумал, что необходимость в ключе применяется к тому, что выглядит как «реальные» или DOM HTML-элементы, в отличие от JSX-элементов, которые я определил.

Конечно, с React мы работаем с виртуальной DOM, поэтому определяемые нами элементы React JSX <MyElement>так же важны для нее, как и элементы, которые выглядят как настоящие HTML-элементы DOM <div>.

Имеет ли это смысл?


0

В моем случае я использовал представление «Карточка» Semantic UI React. Как только я добавил ключ к каждой построенной мной карте, предупреждение исчезло, например:

return (
        <Card fluid key={'message-results-card'}>
          ...
        </Card>
)

-1

Если вы используете <Fade in>элемент для приложения реакции, добавьте key={}в него также атрибут, иначе вы увидите ошибку в консоли.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.