React中的useId

简介 useId是新增的用于生成唯一ID值的hook钩子,主要用于客户端和服务器端使用,同时避免 dehydrate 过程中数据不匹配的问题。
它主要用于与需要唯一 ID 的可访问性 API 集成的组件库。这解决了 React 17 及更低版本中已经存在的问题,但在 React 18 中更为重要,因为新的流式服务器渲染器如何无序交付 HTML。
但是,不建议用于List中作为key使用,列表中的唯一key应该使用List中的数据。
问题 React渲染有客户端渲染(CSR)和服务端渲染(SSR)。
假设有如下代码片段:

// App.tsx const id = Math.random(); export default function App() { return Hello }

如果应用是CSR(客户端渲染),id是稳定的,App组件没有问题。
但如果应用是SSR(服务端渲染),那么App.tsx会分为以下几步:
  1. React在服务端渲染,生成随机id(假设为0.1234),这一步叫dehydrate(脱水);
  2. Hello作为HTML传递给客户端,作为首屏内容;
  3. React在客户端渲染,生成随机id(假设为0.6789),这一步叫hydrate(注水)。
客户端、服务端生成的id不匹配!
原始解决方式:
// 全局通用的计数变量 let globalIdIndex = 0; export default function App() { const id = useState(() => globalIdIndex++); return Hello }

只要React在服务端、客户端的运行流程一致,那么双端产生的id就是对应的。
但是,随着React Fizz(React新的服务端流式渲染器)的到来,渲染顺序不再一定。
比如,有个特性叫 Selective Hydration,可以根据用户交互改变hydrate的顺序。
当下图左侧部分在hydrate时,用户点击了右下角部分:
React中的useId
文章图片

此时React会优先对右下角部分hydrate:
React中的useId
文章图片

因此,自增的全局计数变量作为id,不再准确!!
那么,有没有什么是服务端、客户端都稳定的标记呢?
答案是:组件的层次结构。
useId的原理 假设应用的组件树如下图:
React中的useId
文章图片

不管B和C谁先hydrate,他们的层级结构是不变的,所以「层级」本身就能作为服务端、客户端之间不变的标识。
比如B可以使用2-1作为id,C使用2-2作为id:
function B() { // id为"2-1" const id = useId(); return B; }

【React中的useId】如何在一个组件中使用多个useId()?
react推荐使用相同的id+后缀:
function NameFields() { const id = useId(); return (); }

    推荐阅读