为组件提供唯一键时,是否可以使用 Math.random() 生成这些键?

IT技术 reactjs
2021-04-18 06:17:19

问题如下:

我有几千个元素列表形式的数据。其中一些是重复的,然后也可能有重复的键。因为我没有真正的“ID”或任何可以让我有机会将所有元素的 id 作为唯一键的东西,所以可以Math.random()改用吗?

据我了解,key主要是react用来区分组件的。我认为就我的代码中的键而言,我真的没有任何关系,这应该没问题吗?为了确保不会有重复的数字,我不妨将两个数学随机数相互除以得到一个几乎可以肯定的唯一键。

这是一个好习惯吗?我可以使用它而不必担心任何事情吗?

6个回答

每次组件的键更改时,React 都会创建一个新的组件实例而不是更新当前的组件实例,因此出于性能考虑,使用 Math.random() 至少可以说是次优的。

此外,如果您要以任何方式重新排列组件列表,使用索引作为键无济于事,因为 React 协调器将无法移动与组件关联的现有 DOM 节点,而是必须重新-为每个列表项创建 DOM 节点,这将再次具有次优性能。

但重申一下,这只会在您对列表重新排序时出现问题,因此在您的情况下,如果您确定不会以任何方式对列表重新排序,则可以安全地使用索引作为键。

但是,如果您确实打算重新排序列表(或只是为了安全),那么我将为您的实体生成唯一 ID - 如果您没有可以使用的预先存在的唯一标识符。

添加 id 的一种快速方法是在您第一次接收(或创建) list 时映射列表并为每个项目分配索引

const myItemsWithIds = myItems.map((item, index) => { ...item, myId: index });

这样每个项目都会获得一个唯一的静态 ID。

tl;dr 如何为找到此答案的新人选择一把钥匙

  1. 如果您的列表项具有唯一 id(或其他唯一属性),请将其用作键

  2. 如果您可以组合列表项中的属性以创建唯一值,请使用该组合作为键

  3. 如果上述方法均无效,但您可以保证不会以任何方式对列表重新排序,则可以使用数组索引作为键,但最好将自己的 id 添加到列表项中首先收到或创建(见上文)

同意@fraxture 对此的看法。分配索引是一种不好的做法
2021-05-25 06:17:19
谢谢。我什至不知道一个组件是否被重新渲染的决定是基于它的键。
2021-06-05 06:17:19
根据 react eslint 的说法,不建议使用索引:“使用数组索引是一个坏主意,因为它不能唯一标识您的元素。在数组已排序或元素添加到数组开头的情况下,即使表示该索引的元素可能相同,该索引也会更改。这会导致不必要的渲染。” 无数组索引键
2021-06-06 06:17:19
@thisguyheisaguy 如果您无论如何都要重新排序您的列表,那么使用索引作为键绝对是一个坏主意,但是如果它是某种静态列表,那么它根本没有问题,只是您可能会看到一个烦人的当然是 eslint 警告 :) 我会编辑我的帖子以使其更清晰
2021-06-08 06:17:19
我也同意@fraxture。事实上,我在实践中看到了将新添加的行添加到表格顶部的要求。我最终得到了索引 0 的重复键。
2021-06-20 06:17:19

只需在您的react组件中实现以下代码...

constructor( props ) {
    super( props );

    this.keyCount = 0;
    this.getKey = this.getKey.bind(this);
}

getKey(){
    return this.keyCount++;
}

...并在this.getKey()每次需要新密钥时调用,例如:

key={this.getKey()}
它可能无法回答问题,但它肯定是一个解决方案。
2021-06-04 06:17:19
但这根本不能回答问题
2021-06-19 06:17:19

react文档

密钥应该是稳定的、可预测的和唯一的。不稳定的键(如 Math.random() 生成的键)将导致不必要地重新创建许多组件实例和 DOM 节点,这可能导致子组件的性能下降和状态丢失。

密钥应该是稳定的、可预测的和唯一的。不稳定的键(如 Math.random() 生成的键)将导致不必要地重新创建许多组件实例和 DOM 节点,这可能导致子组件的性能下降和状态丢失。

https://facebook.github.io/react/docs/reconciliation.html

这是一个好习惯吗?我可以使用它而不必担心任何事情吗?

没有也没有。

密钥应该是稳定的、可预测的和唯一的。不稳定的键(如 Math.random() 生成的键)将导致不必要地重新创建许多组件实例和 DOM 节点,这可能导致子组件的性能下降和状态丢失。

让我用一个简单的例子来说明这一点

class Input extends React.Component {
  handleChange = (e) =>  this.props.onChange({
    value: e.target.value,
    index: this.props.index
  });
  render() {
    return (
      <input value={this.props.value} onChange={this.handleChange}/>  
    )
  }
};

class TextInputs extends React.Component {
  state = {
    textArray: ['hi,','My','Name','is']
  };
  handleChange = ({value, index}) => {
    const {textArray} = this.state;
    textArray[index] = value;
    this.setState({textArray})
  };
  render(){
  return this.state.textArray.map((txt, i) => <Input onChange={this.handleChange} index={i} value={txt} key={Math.random()}/>)
  // using array's index is not as worse but this will also cause bugs.  
  // return this.state.textArray.map((txt, i) => <Input onChange={this.handleChange} index={i} value={txt} key={i}/>)                 
  };
};

为什么我不能在你问的输入中输入一个以上的字符?

这是因为我使用 Math.random() 作为关键props映射多个文本输入。每次我输入一个字符时,onChange props都会触发,并且父组件的状态会发生变化,这会导致重新渲染。这意味着每次输入都会再次调用 Math.random 并生成新的关键props。因此,react 会呈现新的子项 Input 组件。换句话说,每次键入一个字符时,react 都会创建一个新的输入元素,因为关键props发生了变化。

在此处阅读更多相关信息