Попередження: Кожна дочка в масиві або ітераторі повинна мати унікальний підказ “ключ”. Перевірте метод візуалізації `ListView`


101

Я створив додаток з ReactNative як для iOS, так і для Android з ListView. Під час заповнення подання списку дійсним джерелом даних у нижній частині екрана друкується таке попередження:

Попередження: Кожна дочка в масиві або ітераторі повинна мати унікальний пропік "ключа". Перевірте метод візуалізації ListView.

Яка мета цього попередження? Після повідомлення вони посилаються на цю сторінку , де обговорюються різні речі, які не мають нічого спільного з рідною реакцією, а з веб-реакцією.

Мій 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}
            />

    );
}

Мій DataSource складається з приблизно такого:

    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 відображаються такими речами, як:

        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 не знатиме, що з ними робити, коли вони не мають унікального ідентифікатора.
Піт ТНТ

Що ви маєте на увазі під "ключами"? Властивість під назвою "ключ"?
видалити


Це не вирішує. Я додав ключову властивість до своєї структури даних та оновив вихідне запитання детальнішими даними. Перерахування простих даних, що призводять до DataSource, має ключ для кожного запису. Це попередження залишається.
видалити

Це може походити і від інших методів візуалізації (renderHeader, renderDetailItem, renderSeparator)
Піт ТНТ

Відповіді:


93

У мене вже деякий час точно така ж проблема, як і у вас, і, ознайомившись із деякими наведеними вище порадами, я нарешті вирішив проблему.

Виявляється (принаймні для мене в будь-якому випадку) мені потрібно було надати ключ (проп, який називається 'ключ') компоненту, який я повертаю з мого методу renderSeparator. Додавання ключа до мого renderRow або renderSectionHeader нічого не зробило, але додавши його до renderSeparator попередження зникло.

Сподіваюся, що це допомагає.


У моєму випадку я просто видалив renderSeparator і перемістив свій <Separator> в тіло значення renderRow, що повертається.
човновий кодер

Те саме, що і для SectionList, потрібно явно додати властивість із ключем імені для кожного, itemщоб зробити RN щасливим.
LeOn - Han Li

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

76

Вам потрібно надати ключ .

Спробуйте зробити це у рядках ListView, якщо у вас є властивість ключа:

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

Якщо ні, спробуйте просто додати елемент як ключ:

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

1
мені більше подобається ця відповідь, тому що в ній є код, щоб я міг скопіювати XD
Джовіл Бермудес

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

Додайте "ключовий ключ" до кореневого компонента візуалізації списку.

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

6

Це попередження надходить, коли ви не додаєте ключ до елементів списку. Відповідно до реакції 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 Component, код тут:

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

Ключові слова цього попередження - "унікальний", sectionID + rowID повертають унікальне значення в ListView.


4

Перевірка: ключ = 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)}/>

Заслуга холодного буфету та Надера Дабіта, який вказав мені цей шлях.


2

Тут базується на моєму розумінні. Сподіваюся, це корисно. Він повинен відображати список будь-яких компонентів як приклад. Кореневий тег кожного компонента повинен мати a 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>, настільки ж важливі для нього, як і елементи, схожі на справжні елементи DOM HTML <div>.

Чи має це сенс?


0

У моєму випадку я використовував подання Semantic UI React "Card". Як тільки я додав ключ до кожної побудованої картки, попередження зникло, наприклад:

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

-1

Якщо ви використовуєте <Fade in>елемент для програми для реагування, додайте key={}в нього також атрибут, інакше ви побачите помилку в консолі.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.