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

Sos*_*der 36 reactjs

问题如下:

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

据我所知,密钥主要用于区分组件.我认为,就我的代码中的密钥没有任何关系,这应该没问题?为了确保没有重复的数字,我可以将两个数学格式相互分开以获得几乎肯定唯一的密钥.

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

Mar*_*pse 42

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

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

但重申一下,如果您要重新排序列表,这只会是一个问题,所以在您的情况下,如果您确定不会以任何方式重新排序列表,您可以安全地使用索引作为键.

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

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

const myItemsWithIds = myItems.map((item, index) => { ...item, myId: index });
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

  3. 如果上述工作都没有,但你可以小姐承诺你不会以任何方式重新排序列表,你可以使用数组索引作为键,但你最好在列表中添加自己的id到列表项首先收到或创建(见上文)

  • 根据react eslint的说法,不建议使用索引:“使用数组索引是个坏主意,因为它不能唯一地标识您的元素。在对数组进行排序或将元素添加到数组开头的情况下,即使代表该索引的元素可能相同,索引也会更改。这将导致不必要的渲染。” 参见[no-array-index-key](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-array-index-key.md) (3认同)
  • @thisguyheisaguy 如果你无论如何都要重新排序你的列表,那么使用索引作为键绝对是一个坏主意,但如果它是某种静态列表,那么它根本没有问题,除了你可能会看到一个烦人的当然是 eslint 警告:) 我会编辑我的帖子以使其更清晰 (2认同)

小智 14

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

constructor( props ) {
    super( props );

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

getKey(){
    return this.keyCount++;
}
Run Code Online (Sandbox Code Playgroud)

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

key={this.getKey()}
Run Code Online (Sandbox Code Playgroud)

  • 但这根本不能回答问题 (3认同)
  • 它可能无法回答问题,但肯定可以解决。 (3认同)

Sh*_*rim 8

这是一个好的做法吗?我可以使用这个而不必担心任何事情吗?

不,不。

密钥应该稳定、可预测且唯一。不稳定的键(例如由 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}/>)                 
  };
};
Run Code Online (Sandbox Code Playgroud)

为什么我不能在您要求的输入中输入多个字符?

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

在这里阅读更多相关内容


Gle*_*ift 5

来自反应文件:

如果您不提供稳定键(例如,通过使用Math.random()),则每次都会重新渲染所有子树.通过让用户选择按键,他们有能力在脚下射击.


Jos*_*eau 5

其他答案说这Math.random()是一个坏主意,但这只说对了一半。Math.random() 如果我们在最初接收数据时而不是在渲染时这样做,那么使用它是完全可以接受的。

例如:

function SomeComponent() {
  const [items, setItems] = React.useState(() => []);

  React.useEffect(() => {
    fetch('/api/data')
      .then((res) => res.json())
      .then((data) => {
        const itemsWithIds = data.map((item) => {
          return {
            ...item,
            id: Math.random(),
          };
        });

        setItems(itemsWithIds);
      });
  }, []);

  return (
    <div>
      {items.map((item) => (
        <Item key={item.id} item={item} />
      ))}
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

当我收到数据时,我会映射列表中的每个项目并附加一个带有Math.random(). 渲染时,我使用之前创建的 ID 作为键。

最重要的是我们不会在每次渲染时重新生成密钥。通过这种设置,当我们从 API 接收数据时,我们只生成一次密钥。

您可以在几乎所有情况下使用此模式,甚至对于本地数据也是如此。在调用状态设置器之前(或者在初始化状态挂钩之前),您可以为每个项目生成唯一的 ID。

您还可以使用crypto.randomUUID()Math.random() 来生成 uuid 而不是随机数。