Я хотел бы дать простой совет коллегам-разработчикам React. При рендеринге динамических массивов дочерних элементов в компоненте React…

Не забывайте свои ключи!

tl;dr Убедитесь, что вы предоставляете уникальный ключ для каждого визуализируемого дочернего элемента. Если вы используете магистральную модель, хорошим выбором будет model.cid, или вы можете использовать идентификатор базы данных, если он есть.

Предоставление ключей дочерним элементам необходимо из-за метода согласования React (то есть того, как он вычисляет разницу DOM для каждого рендеринга). По умолчанию React согласовывает список дочерних элементов в порядке их отображения. Однако с динамическими дочерними элементами обычно необходимо поддерживать личность и состояние каждого дочернего элемента при каждом рендеринге, даже если порядок может измениться. В этом случае вам рекомендуется передавать key prop каждому дочернему элементу. Поступая таким образом, вы сообщаете React, что каждый дочерний элемент должен быть переупорядочен (а не уничтожен) или уничтожен (вместо повторного использования).

Допустим, у вас есть компонент ResultsList, который отображает список результатов поиска:

var ResultsList = React.createClass({ render: function() { return ( <ul> {this.props.results.map(function(result) { return <ResultItem result={result}/>; })} </ul> ); } }); var ResultItem = React.createClass({ render: function() { return ( <li>return {this.props.result};</li> ); } });

Это выглядит правильно… верно?

Неправильно. Вы заметите, что если вы откроете консоль JavaScript, вы, вероятно, увидите предупреждение, которое выглядит примерно так: Каждый дочерний элемент в массиве должен иметь уникальный ключ. Проверьте вызов renderComponent с помощью <ResultItem>. См. Ключи предупреждений React для получения дополнительной информации».

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

var ResultList = React.createClass({ render: function() { return ( <ul> {this.props.results.map(function(result) { return <ResultItem key={result.id} result={result}/>; })} </ul> ); } }); var ResultItem = React.createClass({ render: function() { return ( <li>return {this.props.result};</li> ); } });

Единственное изменение заключается в том, что теперь мы передаем свойство key каждому ResultItem для рендеринга. key должен быть уникальным, поскольку именно так React различает каждого дочернего элемента, чтобы сделать повторный рендеринг более эффективным. Если у вас есть повторяющиеся ключи, будет отображаться только один из этих компонентов. В приведенном выше примере уникальность достигается за счет использования атрибута id результата. Так что это было легко. Проблема решена!

Неверные способы решения этой проблемы

Я не раз видел, как люди (включая меня самого) пытались избавиться от этого предупреждения неправильным путем. Вот несколько неправильных способов сделать это:

1. Добавление ключа внутри HTML самого дочернего компонента вместо передачи его каждому дочернему компоненту напрямую в качестве реквизита.

// This won't work. var ResultItem = React.createClass({ render: function() { return ( <li key={this.props.result.id}>return {this.props.result};</li> ); } });

Непосредственно из React docs: Ключ всегда должен передаваться непосредственно компонентам в массиве, а не дочернему HTML-контейнеру каждого компонента в массиве.

2. Использование индекса цикла для уникального ключа

// This won't work either. var ResultList = React.createClass({ render: function() { return ( <ul> {this.props.results.map(function(result, index) { return <ResultItem key={index} result={result}/>; })} </ul> ); } });

Это избавит от предупреждения, но вызовет проблемы. С этим шаблоном, если сопоставленные результаты изменятся, React не будет повторно отображать существующие дочерние компоненты с новыми результатами. Это связано с тем, что ключ не изменился, несмотря на изменение props.result, поскольку рассматриваемый дочерний элемент все еще находится в том же индексе в массиве. React не знает, что ребенок должен получать новые реквизиты, потому что ключ остался прежним.

3. Использование чего-то вроде символа подчеркивания _.uniqueId()

// React's rendering is blazing fast... // ...until you do this. var ResultList = React.createClass({ render: function() { return ( <ul> {this.props.results.map(function(result) { return <ResultItem key={_.uniqueId()} result={result}/>; })} </ul> ); } });

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

Не пренебрегайте предупреждениями о ключах. Не используйте глупые ярлыки, чтобы заставить их исчезнуть. Это причинит вам (или следующему бедняге после вас) много боли и страданий. Инженеры, стоящие за разработкой программного обеспечения на заказ Revelry, используют React и часто пишут об этом. Прочтите этот пост на тему Тестирование React с помощью Jasmine, ознакомьтесь с разделом React и ECMAScript 6 или узнайте больше об использовании Карт Google с React.

Первоначально опубликовано на сайте revelry.co 27 августа 2015 г.