在React列表中使用对象实例作为键

时间:2018-08-07 12:20:43

标签: reactjs react-dom

我有一个要使用React呈现为列表的对象列表。

反应需要list元素上的特殊key属性,以便跟踪DOM和虚拟DOM之间的变化。

我的对象没有id之类的特殊特殊属性,实际上,两个对象甚至可以具有相同的属性,但是对象本身是唯一的。

有没有一种方法可以使用对象的引用作为渲染React列表的键?

否则,在这种情况下可以建议什么解决方法?


另外,我正在从第三方接收项目列表,所以我不能随便给每个项目附加一个随机ID,因为这会使React在每次收到更新数据时重新渲染所有项目

2 个答案:

答案 0 :(得分:0)

如果您没有唯一的ID(由后端返回,或者您无法在前端构建复合键),则可以将数组索引用作键:

render() {
  return array.map((item, index) => <Item key={index} />)
}

在没有唯一ID的情况下,无论选择哪种方法,如果更改项目的顺序,则列表可能会完全混乱。

如果您的情况满足以下3个条件,则使用this article足够安全,使用此方法:

  1. 列表和项目是静态的-它们不会被计算并且不会更改。
  2. 列表中的项目没有ID。
  3. 列表从不重新排序或过滤。

答案 1 :(得分:0)

  

将数组的index用作key将是一场灾难,因为如果数组发生更改,则视图将无法正确更新,或者将以不可预测的方式部分更新。

我发现的唯一解决方案是按照注释中的建议生成虚拟ID。

想法是实现生成器,该生成器将为每个项目创建一个ID,但同时会记住以前生成的ID。

WeakMap JavaScript类将使我们能够存储项目对象引用和生成的ID之间的映射信息:

export class IdGenerator {

  // Using WeakMap as index
  map = new WeakMap();

  // Internal counter
  lastId = 0;

  getItemId(item) {

    // Getting previously generated ID for the specified item
    let id = this.map.get(item);

    // If item is new, generating the ID and storing it for future use
    if (undefined === id) {
      id = ++(this.lastId);
      this.map.set(item, id);
    }

    // Returning ID to the caller in either case
    return id;

  }

}

然后可以在呈现列表之前将其用于为每个项目分配生成的ID:

class ListBox extends Component {

  // Instantiating the generator
  itemsIdGenerator = new IdGenerator();

  render() {
    return (
      <ul>
        {this.props.items.map(item => (
          <li key={this.itemsIdGenerator.getItemId(item)}>
            {item.title}
          </li>
        ))}
      </ul>
    );
  }

}

但是请确保为组件的每个实例使用不同的生成器实例,并且仅使用该组件创建一次生成器。

此外,调用组件的render方法中每个项目的生成器可能不是最佳的。但是,我不想触摸原始项目或更改它们。如果这是一个问题,请考虑在props更改时使用React提供的各种生命周期方法来设置ID。