Различные способы добавления ключа к элементу JSX в цикл in React

Я работаю над react уже более года. Я в основном играл в переборе массива .карта. ,инструкция foreach. ,фильтровать или использовать объект.ключи и предмет.значения, если это объект.

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

использование уникального идентификатора из данных в качестве ключа к ключевой опоре:

const data= [{"id": "01", "name": "abc"}, {"id": "02", "name": "xyz"}];

render(){
  const items = data.map(item => {
    return <span key={item.id}>{item.name}</span>;
  }
  return(
     <div>
        {items}
     </div>
  )
}

используя индекс как ключ к ключ проп:

const data= [{"id": "01", "name": "abc"}, {"id": "02", "name": "xyz"}];

render(){
  const items = data.map((item, i) => {
    let keyValue = i+1;
    return <span key={keyValue}>{item.name}</span>;
  }
  return(
     <div>
        {items}
     </div>
  )
}

есть ли другие способы добавления уникального ключа к элементу jsx помимо того, что я упомянул выше, и что наиболее эффективно и рекомендуется?

4 ответов


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

есть много способов, чтобы написать ключи, и некоторые из них будут работать лучше, чем другие.

чтобы понять, как выбранные нами ключи влияют на производительность, необходимо понять алгоритм согласования React.

https://reactjs.org/docs/reconciliation.html

tl; dr вводит эвристику для сравнения виртуальных деревьев DOM, чтобы сделать это сравнение O (n), с N узлами этого дерева VDOM. Эту эвристику можно разбить на следующие пункты:

  • компоненты разного типа создадут новое дерево: это означает, что при сравнении старого дерева с новым, если примиритель обнаруживает, что узел изменил свой тип (например,<Button /> to <NotButton />), причинит к нашей кнопке быть unmounted со своими детьми также, и NotButton быть установленным со своими детьми, как что ж.
  • мы можем намекнуть реагировать на то, как экземпляры сохраняются на VDOM, избегая их воссоздания. Эти подсказки предоставляются нами с ключами.: после принятия решения о том, следует ли сохранить экземпляр в узле (потому что его тип остается тем же), примиритель будет выполнять итерацию на дочерних элементах этого узла для их сравнения.

теперь, когда у нас есть это:

<div>
  <Button title="One" />
  <Button title="Two" />
</div>

и мы хотели бы добавить кнопку в DOM при следующем рендеринге, скажи

<div>
  <Button title="Zero" />
  <Button title="One" />
  <Button title="Two" />
</div>

алгоритм будет выглядеть следующим образом:

  • сравнивает <divs> в обоих VDOMs. Поскольку они имеют один и тот же тип, нам не нужно воссоздавать новое дерево. Реквизит одинаковый, поэтому на данный момент нет никаких изменений для применения к DOM.
  • One сравнивает против Zero. Reconciler обнаруживает, что здесь было изменение реквизита, а затем обновляет DOM с этим заголовком. Two сравнивает отношении One. Reconcilier также обнаруживает здесь изменение реквизита и использует DOM для записи этого изменения.
  • обнаруживает, что новый Button добавляется как последний ребенок, поэтому создает новый Button экземпляр в VDOM и напишите это изменение в DOM.

обратите внимание, что они имеют много операций на DOM, потому что он сравнивал компоненты по их индексу.

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

<div>
  <Button title="One" key="One" />
  <Button title="Two" key="Two" />
</div>

и мы хотели бы добавить кнопку в DOM при следующем рендеринге, скажем

<div>
  <Button title="Zero" key="Zero" />
  <Button title="One" key="One" />
  <Button title="Two" key"Two" />
</div>

алгоритм будет выглядеть следующим образом:

  • сравнивает <divs> в обоих VDOMs. Поскольку они имеют один и тот же тип, нам не нужно воссоздавать новое дерево. Реквизит одинаковый, поэтому на данный момент нет никаких изменений для применения к DOM.
  • принимает первого ребенка детей. - Это Button', - говорит посредник. 'И имеет ключ '('One'). Затем ищет детей, чей ключ тот же в новом списке детей. - О, я столкнулся с ним!- но примиритель понимает это!--36-->нет никаких изменений на его реквизит. Тогда для этого не потребуется никаких операций DOM.
  • тот же сценарий происходит со вторым Button, он будет сравнивать по keys вместо index. Понимает, что это тот же экземпляр, и никакие реквизиты не были изменены, поэтому React решает не применять изменения на DOM.
  • для the Button С ключом "ноль", так как не существует ребенка с тем же ключом, понимает, что экземпляр должен быть создан в VDOM, и это изменение должно быть записано на DOM.

таким образом, использование ключей предсказуемым содержимым помогает выверителю выполнять меньше операций над DOM. Здоровые ключи-это те, которые можно вывести из сопоставляемого объекта, например name или id или даже url если мы трансформируем urls to <imgs />.

насчет key=index? Не будет иметь никакого эффекта, так как по умолчанию reconciler сравнивает по позиции, т. е. его индекс.

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

как насчет случайных ключей? Их следует избегать любой ценой. Если ключ изменяется на каждом рендеринге, это будет поддерживать React destroying и creating экземпляры на VDOM (и, следовательно, создание дополнительных записей на DOM), так как компонент с ключом не был найден среди новых детей, но новый с тем же типом.

если вывод рендеринга похож на

<div>
  <Button key={randomGenerator()} />
</div>

тогда, каждый раз render выполняется (например, из-за изменения реквизита / состояния, или даже если это родитель перерисовывается и наш shouldComponentUpdate возвращает true), новый randomGenerator() будет сгенерирован ключ. Это будет выглядеть так:

- Эй! Я нашел Button С F67BMkd== ключ, но в следующем не было найдено ни одного. Я удалю его.' - О! Я столкнулся с Button С SHDSA++5 ключ! Давайте создадим новый.

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

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

надеюсь, что это помогает.


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

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

обратите внимание:

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

однако реальный ответ на ваш вопрос живет здесь: https://medium.com/@robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318

есть много библиотек, которые генерируют случайные уникальные идентификаторы, такие как shortid или uuid (который является самым популярным, просто посмотрите на количество загрузок) или просто создайте свою собственную функцию, которая генерирует случайные строки.

вы можете добавьте их непосредственно в объект в array

const todos = [
  { 
    id: uuid(),
    text: 'foo',
  }
]

и повторите так:

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

можно использовать дата.сейчас() С индекс, ваш код будет как бывший.

const data= [{"id": "01", "name": "abc"}, {"id": "02", "name": "xyz"}];

render(){
  const items = data.map((item, i) => {
    let keyValue = Date.now()+i;
    return <span key={keyValue}>{item.name}</span>;
  }
  return(
     <div>
        {items}
     </div>
  )
}

md5 sha1 или даже sha256 на содержимом.