简介

useId是新增的用于生成惟一ID值的hook钩子,次要用于客户端和服务器端应用,同时防止 dehydrate 过程中数据不匹配的问题。

它次要用于与须要惟一 ID 的可拜访性 API 集成的组件库。这解决了 React 17 及更低版本中曾经存在的问题,但在 React 18 中更为重要,因为新的流式服务器渲染器如何无序交付 HTML。

然而,不倡议用于List中作为key应用,列表中的惟一key应该应用List中的数据。

问题

React渲染有客户端渲染(CSR)和服务端渲染(SSR)。

假如有如下代码片段:

// App.tsxconst id = Math.random();export default function App() {  return <div id={id}>Hello</div>}

如果利用是CSR(客户端渲染),id是稳固的,App组件没有问题。

但如果利用是SSR(服务端渲染),那么App.tsx会分为以下几步:

  1. React在服务端渲染,生成随机id(假如为0.1234),这一步叫dehydrate(脱水);
  2. <div id="0.12345">Hello</div>作为HTML传递给客户端,作为首屏内容;
  3. React在客户端渲染,生成随机id(假如为0.6789),这一步叫hydrate(注水)。

客户端、服务端生成的id不匹配!

原始解决形式:

// 全局通用的计数变量let globalIdIndex = 0;export default function App() {  const id = useState(() => globalIdIndex++);  return <div id={id}>Hello</div>}

只有React在服务端、客户端的运行流程统一,那么双端产生的id就是对应的。

然而,随着React Fizz(React新的服务端流式渲染器)的到来,渲染程序不再肯定。

比方,有个个性叫 Selective Hydration,能够依据用户交互扭转hydrate的程序。

当下图左侧局部在hydrate时,用户点击了右下角局部:

此时React会优先对右下角局部hydrate:

因而,自增的全局计数变量作为id,不再精确!!

那么,有没有什么是服务端、客户端都稳固的标记呢?

答案是:组件的层次结构。

useId的原理

假如利用的组件树如下图:

不论B和C谁先hydrate,他们的层级构造是不变的,所以「层级」自身就能作为服务端、客户端之间不变的标识。

比方B能够应用2-1作为id,C应用2-2作为id:

function B() {  // id为"2-1"  const id = useId();  return <div id={id}>B</div>;}

如何在一个组件中应用多个useId()?
react举荐应用雷同的id+后缀:

function NameFields() {  const id = useId();  return (    <div>      <label htmlFor={id + '-firstName'}>First Name</label>      <div>        <input id={id + '-firstName'} type="text" />      </div>      <label htmlFor={id + '-lastName'}>Last Name</label>      <div>        <input id={id + '-lastName'} type="text" />      </div>    </div>  );}