Понимание уникальных ключей для дочерних элементов массива в React.js

Я создаю компонент React, который принимает источник данных JSON и создает сортируемую таблицу.
Каждая из строк динамических данных имеет уникальный ключ, назначенный ему, но я все еще получаю сообщение об ошибке:

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

Мой метод рендеринга TableComponent возвращает:

<table>
  <thead key="thead">
    <TableHeader columns={columnNames}/>
  </thead>
  <tbody key="tbody">
    { rows }
  </tbody>
</table>

Компонент TableHeader - это одна строка, а также назначенный ему уникальный ключ.

Каждый row в rows построен из компонента с уникальным ключом:

<TableRowItem key={item.id} data={item} columns={columnNames}/>

И TableRowItem выглядит так:

var TableRowItem = React.createClass({
  render: function() {

    var td = function() {
        return this.props.columns.map(function(c) {
          return <td key={this.props.data[c]}>{this.props.data[c]}</td>;
        }, this);
      }.bind(this);

    return (
      <tr>{ td(this.props.item) }</tr>
    )
  }
});

Что вызывает уникальную ошибку подсказки ключа?

Ответ 1

Вы должны добавить ключ к каждому дочернему элементу, а также к каждому элементу внутри дочерних элементов.

Таким образом, React может справиться с минимальным изменением DOM.

В вашем коде каждый <TableRowItem key={item.id} data={item} columns={columnNames}/> пытается отобразить в них несколько дочерних <TableRowItem key={item.id} data={item} columns={columnNames}/> без ключа.

Проверьте этот пример.

Попробуйте удалить key={i} из элемента <b></b> внутри div (и проверьте консоль).

В этом примере, если мы не дадим ключ к элементу <b> и хотим обновить только object.city, React необходимо повторно отобразить всю строку, а не только элемент.

Вот код:

var data = [{name:'Jhon', age:28, city:'HO'},
            {name:'Onhj', age:82, city:'HN'},
            {name:'Nohj', age:41, city:'IT'}
           ];

var Hello = React.createClass({

    render: function() {

      var _data = this.props.info;
      console.log(_data);
      return(
        <div>
            {_data.map(function(object, i){
               return <div className={"row"} key={i}> 
                          {[ object.name ,
                             // remove the key
                             <b className="fosfo" key={i}> {object.city} </b> , 
                             object.age
                          ]}
                      </div>; 
             })}
        </div>
       );
    }
});

React.render(<Hello info={data} />, document.body);

Ответ, размещенный @Chris внизу, раскрывает гораздо больше деталей, чем этот ответ. Пожалуйста, посмотрите на fooobar.com/questions/34019/...

Реагировать на документацию о важности ключей в сверке: ключи

Ответ 2

Будьте осторожны при повторении массивов!

Это распространенное заблуждение, что использование индекса элемента в массиве является приемлемым способом подавления ошибки, с которой вы, вероятно, знакомы:

Each child in an array should have a unique "key" prop.

Однако во многих случаях это не так! Это анти-шаблон, который в некоторых ситуациях может привести к нежелательному поведению.


Понимание key опоры

React использует key подсказку для понимания отношения компонента к DOM, которое затем используется для процесса согласования. Поэтому очень важно, чтобы ключ всегда оставался уникальным, иначе есть хорошая вероятность. Реакт будет смешивать элементы и мутировать неправильный. Также важно, чтобы эти ключи оставались статичными во всех повторных документах, чтобы поддерживать лучшую производительность.

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

Разработчик React сказал в этом выпуске GitHub:

  • ключ не совсем о производительности, это больше об идентичности (что, в свою очередь, ведет к лучшей производительности). случайно присвоенные и изменяющиеся значения не являются идентичными
  • Мы не можем реалистично предоставлять ключи [автоматически], не зная, как моделируются ваши данные. Я бы предложил, возможно, используя какую-то функцию хеширования, если у вас нет идентификаторов
  • У нас уже есть внутренние ключи, когда мы используем массивы, но они являются индексом в массиве. Когда вы вставляете новый элемент, эти ключи ошибочны.

Короче говоря, key должен быть:

  • Уникальный. Ключ не может быть идентичным ключу компонента.
  • Статический - ключ не должен меняться между рендерами.


Использование key

В соответствии с приведенным выше объяснением внимательно изучите следующие образцы и попытайтесь реализовать, когда это возможно, рекомендуемый подход.


Плохо (потенциально)

<tbody>
    {rows.map((row, i) => {
        return <ObjectRow key={i} />;
    })}
</tbody>

Это, возможно, самая распространенная ошибка, возникающая при повторении массива в React. Этот подход не является технически "неправильным", он просто... "опасен", если вы не знаете, что делаете. Если вы выполняете итерирование через статический массив, то это абсолютно правильный подход (например, массив ссылок в вашем навигационном меню). Однако, если вы добавляете, удаляете, переупорядочиваете или фильтруете элементы, тогда вам нужно быть осторожными. Взгляните на это подробное объяснение в официальной документации.

class MyApp extends React.Component {
  constructor() {
    super();
    this.state = {
      arr: ["Item 1"]
    }
  }
  
  click = () => {
    this.setState({
      arr: ['Item ' + (this.state.arr.length+1)].concat(this.state.arr),
    });
  }
  
  render() {
    return(
      <div>
        <button onClick={this.click}>Add</button>
        <ul>
          {this.state.arr.map(
            (item, i) => <Item key={i} text={"Item " + i}>{item + " "}</Item>
          )}
        </ul>
      </div>
    );
  }
}

const Item = (props) => {
  return (
    <li>
      <label>{props.children}</label>
      <input value={props.text} />
    </li>
  );
}

ReactDOM.render(<MyApp />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

Ответ 3

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

Это предупреждение, поскольку для элементов массива, которые мы собираемся перебирать, потребуется уникальное сходство.

React обрабатывает обработку итерации компонентов в виде массивов.

Лучший способ разрешить это - предоставить индекс для элементов массива, которые вы собираетесь перебирать. Например:

class UsersState extends Component
    {
        state = {
            users: [
                {name:"shashank", age:20},
                {name:"vardan", age:30},
                {name:"somya", age:40}
            ]
        }
    render()
        {
            return(
                    <div>
                        {
                            this.state.users.map((user, index)=>{
                                return <UserState key={index} age={user.age}>{user.name}</UserState>
                            })
                        }
                    </div>
                )
        }

index - Реагировать встроенные реквизиты.

Ответ 4

Просто добавьте уникальный ключ к своим компонентам

data.map((marker)=>{
    return(
        <YourComponents 
            key={data.id}     // <----- unique key
        />
    );
})

Ответ 5

Проверьте: ключ = undef !!!

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

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

если ваш код заполнен правильно, но если на

<ObjectRow key={someValue} />

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

Ответ 6

Пример: правильное использование ключа

function ListItem(props) {
  // Correct! There is no need to specify the key here:
  return <li>{props.value}</li>;
}

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    // Correct! Key should be specified inside the array.
    <ListItem key={number.toString()}
              value={number} />

  );
  return (
    <ul>
      {listItems}
    </ul>
  );
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);

Ответ 7

Я исправил это с помощью Guid для каждого ключа следующим образом: Generating Guid:

guid() {
    return this.s4() + this.s4() + '-' + this.s4() + '-' + this.s4() + '-' +
        this.s4() + '-' + this.s4() + this.s4() + this.s4();
}

s4() {
    return Math.floor((1 + Math.random()) * 0x10000)
        .toString(16)
        .substring(1);
}

А затем присвоение этого значения маркерам:

{this.state.markers.map(marker => (
              <MapView.Marker
                  key={this.guid()}
                  coordinate={marker.coordinates}
                  title={marker.title}
              />
          ))}

Ответ 8

Самое простое решение, которое я нашел, - это создать уникальный идентификатор для каждого элемента в списке. Есть 2 библиотеки, uniqid и uuid.

npm install uniqid

Тогда в вашем коде:

import uniqueid from 'uniqid'
- OR if you use require:   var uniqid = require('uniqid');

Затем просто добавьте ключ к нужному элементу HTML. В приведенном ниже примере обратите внимание на ключ = {uniqueid()}

{this.state.someList.map((item) => <li>{item}</li>)}
I changed to this:
{this.state.someList.map((item) => <li key={uniqueid()}>{item}</li>)}

Ответ 9

Это предупреждение, но решение этой проблемы сделает рендеринг намного быстрее,

Это потому, что React должен однозначно идентифицировать каждый элемент в списке. Допустим, если состояние элемента этого списка изменяется в Reacts Virtual DOM то React должен выяснить, какой элемент был изменен и где в DOM его нужно изменить, чтобы DOM браузера синхронизировался с Reacts Virtual DOM.

В качестве решения просто введите key атрибут для каждого тега li. Этот key должен иметь уникальное значение для каждого элемента.