使用 UUID 作为列表键会导致 React 中不必要的重新渲染吗?

IT技术 reactjs rendering
2021-05-16 13:10:41

我有一个项目列表,其中包含的数据不足以生成唯一键。如果我使用 uuid 库生成 ID,单个项目的更改是否也会导致其他项目重新呈现,因为它们的密钥每次都会更改?

const people = [
  {
    gender: 'male',
    firstName: 'david',
  },
  {
    gender: 'male',
    firstName: 'david',
  },
  {
    gender: 'male',
    firstName: 'joe',
  },
]

const renderPeople = () => {
  return people.map(person => {
    return (
      <div key={uuid.v4() /* a new value each time? */ }>
        <p>{person.gender}</p>
        <p>{person.firstName}</p>
      </div>
    )
  })
}

一段时间后......其中一个大卫改变了

const people = [
  {
    gender: 'male',
    firstName: 'david',
  },
  {
    gender: 'male',
    firstName: 'davidzz',
  },
  {
    gender: 'male',
    firstName: 'joe',
  },
]
3个回答

<div key={uuid.v4()}>每次<div>分配新的密钥,所以没用。

如果数组保持不变,则应在创建数组时生成 UUID。

如果数组发生变化,例如从 HTTP 请求中接收到,具有相同内容的元素的 UUID 将再次生成。

为了避免这种情况,key应该特定于person实体。最好使用内部标识符(数据库 ID),以确保无歧义。

如果标识符不可用,key可能取决于元素内容:

return (
  <div key={JSON.stringify(person)}>
    <p>{person.gender}</p>
    <p>{person.firstName}</p>
  </div>
)

在创建数组时散列一次元素会更有效,例如uuid

import uuidv3 from 'uuid/v3';
...

for (const person of people) {
  person.key = uuidv3(JSON.stringify(person), uuidv3.URL);
}

或者使用专用的散列函数,如object-hash.

请注意,如果存在具有相同内容的元素,散列可能会导致密钥冲突。

是的。键应该是稳定的。如果不是,React 无法推断这些项目可能完全相同,并且必须完全重新渲染它们。

如前所述,密钥必须稳定。为了实现这一点,我有一个建议给你。

我发现使用 UUID 作为键非常简单和方便。我将它们用作单例并使用钩子创建它们。

export function useSingleton<T>(provider: () => T): T {
    const singleton = useRef<T>();

    function getSingleton() {
        if (singleton.current == null) {
            singleton.current = provider();
        }
        return singleton.current;
    }

    return getSingleton();
}

export function useComponentId(): string {
    return useSingleton(uuidv4);
}

并像使用它一样

const componentId = useComponentId();
...
something.map((thing, index) =>
    <Component key={`${componentId}.label.${index}`}/>
);

不过,这只是一个使用示例。我不会说在那种情况下你需要一个 UUID。例如,当您的“事物”对象没有唯一 ID 时,它非常方便。它比计算散列或执行 JSON.stringify() 快得多。也不存在碰撞危险。