Я создаю компонент 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>
)
}
});
Что вызывает уникальную ошибку подсказки ключа?
Вы должны добавить ключ к каждому дочернему элементу, а также к каждому элементу внутри дочерних элементов.
Таким образом, 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 внизу, раскрывает гораздо больше деталей, чем этот ответ. Пожалуйста, посмотрите на https://stackoverflow.com/questions/28329382/understanding-unique-keys-for-array-children-in-react-js
Реагировать на документацию о важности ключей в сверке: ключи
key
к элементу <tr>
?
Будьте осторожны при повторении массивов!
Это распространенное заблуждение, что использование индекса элемента в массиве является приемлемым способом подавления ошибки, с которой вы, вероятно, знакомы:
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>
В этом фрагменте мы используем нестатический массив, и мы не ограничиваемся его использованием в качестве стека. Это небезопасный подход (вы поймете, почему). Обратите внимание, как мы добавляем элементы в начало массива (в основном, несимметрично), значение для каждого <input>
остается на месте. Зачем? Поскольку key
не однозначно идентифицирует каждый элемент.
Другими словами, сначала Item 1
имеет key={0}
. Когда мы добавляем второй элемент, верхний элемент становится Item 2
, за которым следует Item 1
как второй элемент. Однако теперь Item 1
имеет key={1}
а не key={0}
. Вместо этого у Item 2
теперь есть key={0}
!!
Таким образом, React считает, что элементы <input>
не изменились, потому что Item
с ключом 0
всегда наверху!
Так почему же этот подход иногда бывает плохим?
Этот подход является только рискованным, если массив каким-то образом фильтруется, переупорядочивается или элементы добавляются/удаляются. Если он всегда статичен, то он совершенно безопасен в использовании. Например, навигационное меню, например ["Home", "Products", "Contact us"]
можно безопасно повторить с помощью этого метода, потому что вы, вероятно, никогда не добавите новые ссылки или не переупорядочите их.
Короче говоря, здесь, когда вы можете безопасно использовать индекс как key
:
Если бы мы вместо этого, в приведенном выше фрагменте кода, нажал добавленный элемент в конец массива, порядок для каждого существующего объекта всегда будет правильным.
<tbody>
{rows.map((row) => {
return <ObjectRow key={Math.random()} />;
})}
</tbody>
Хотя этот подход, вероятно, гарантирует уникальность ключей, он всегда будет реагировать на повторное отображение каждого элемента в списке, даже если это не требуется. Это очень плохое решение, так как оно сильно влияет на производительность. Не говоря уже о том, что нельзя исключать возможность столкновения с ключом в случае, если Math.random()
производит одинаковый номер дважды.
Нестабильные ключи (например, созданные
Math.random()
) приведут кMath.random()
многих экземпляров компонентов и узлов DOM, что может привести к ухудшению производительности иMath.random()
в дочерних компонентах.
<tbody>
{rows.map((row) => {
return <ObjectRow key={row.uniqueId} />;
})}
</tbody>
Это, пожалуй, лучший подход, поскольку он использует свойство, уникальное для каждого элемента в наборе данных. Например, если rows
содержат данные, извлеченные из базы данных, можно использовать основной ключ таблицы (обычно это число с автоматическим увеличением).
Лучший способ выбрать ключ - использовать строку, которая однозначно идентифицирует элемент списка среди своих братьев и сестер. Чаще всего вы будете использовать идентификаторы из ваших данных в качестве ключей
componentWillMount() {
let rows = this.props.rows.map(item => {
return {uid: SomeLibrary.generateUniqueID(), value: item};
});
}
...
<tbody>
{rows.map((row) => {
return <ObjectRow key={row.uid} />;
})}
</tbody>
Это также хороший подход. Если ваш набор данных не содержит каких-либо данных, которые гарантируют уникальность (например, массив произвольных чисел), существует вероятность столкновения ключей. В таких случаях лучше всего вручную генерировать уникальный идентификатор для каждого элемента в наборе данных перед его итерацией по нему. Предпочтительно при монтаже компонента или при получении набора данных (например, из props
или вызова асинхронного API), чтобы сделать это только один раз, а не каждый раз, когда компонент повторно отображает. Есть уже несколько библиотек, которые могут предоставить вам такие ключи. Вот один пример: index-key-index.
toString()
для преобразования в строку, а не в качестве числа. Это важно помнить?
key
. Не уверен, почему они конвертируют это.
Пример: правильное использование ключа
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')
);
Я исправил это с помощью 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}
/>
))}
Просто добавьте уникальный ключ к своим компонентам
data.map((marker)=>{
return(
<YourComponents
key={data.id} // <----- unique key
/>
);
})
Предупреждение: каждый дочерний элемент в массиве или итераторе должен иметь уникальную "ключевую" опору.
Это предупреждение, поскольку для элементов массива, которые мы собираемся перебирать, потребуется уникальное сходство.
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 - Реагировать встроенные реквизиты.
Самое простое решение, которое я нашел, - это создать уникальный идентификатор для каждого элемента в списке. Есть 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>)}
Это предупреждение, но решение этой проблемы сделает рендеринг намного быстрее,
Это потому, что React
должен однозначно идентифицировать каждый элемент в списке. Допустим, если состояние элемента этого списка изменяется в Reacts Virtual DOM
то React должен выяснить, какой элемент был изменен и где в DOM его нужно изменить, чтобы DOM браузера синхронизировался с Reacts Virtual DOM.
В качестве решения просто введите key
атрибут для каждого тега li
. Этот key
должен иметь уникальное значение для каждого элемента.
key
опору. Если вы его не предоставите, React назначит его автоматически (текущий индекс итерации).
key
. Это поможет ReactJS найти ссылки на соответствующие узлы DOM и обновить только содержимое внутри разметки, но не перерисовывать всю таблицу / строку.rows
или, более предпочтительно, jsfiddle? Кстати, вам не нужноkey
свойство дляthead
иtbody
.