大家好,我卡颂。

看看如下组件有什么问题:

// 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不匹配!

事实上,服务端、客户端无奈简略生成稳固、惟一的id是个由来已久的问题,早在15年就有人提过issue

Generating random/unique attributes server-side that don't break client-side mounting

直到最近,React18推出了官网Hook——useId,才解决以上问题。他的用法很简略:

function Checkbox() {  // 生成惟一、稳固id  const id = useId();  return (    <>      <label htmlFor={id}>Do you like React?</label>      <input type="checkbox" name="react" id={id} />    </>  ););

尽管用法简略,但背地的原理却很有意思 —— 每个id代表该组件在组件树中的层级构造。

本文让咱们来理解useId的原理。

欢送退出人类高质量前端框架群,带飞

React18来了,所有都变了

这个问题尽管始终存在,但之前始终能够应用自增的全局计数变量作为id,思考如下例子:

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

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

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

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

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

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

对于Selective Hydration更具体的解释见:New Suspense SSR Architecture in React 18

如果利用中应用自增的全局计数变量作为id,那么显然先hydrate的组件id会更小,所以id是不稳固的。

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

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

useId的原理

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

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

比方B能够应用2-1作为idC应用2-2作为id

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

理论须要思考两个因素:

1. 同一个组件应用多个id

比方这样:

function B() {  const id0 = useId();  const id1 = useId();  return (    <ul>      <li id={id0}></li>      <li id={id1}></li>    </ul>  );}

2. 要跳过没有应用useId的组件

还是思考这个组件树结构:

如果组件AD应用了useIdBC没有应用,那么只须要为AD划定层级,这样就能缩小须要示意层级

useId的理论实现中,层级被示意为32进制的数。

之所以抉择32进制,是因为抉择尽可能大的进制会让生成的字符串尽可能紧凑。比方:

const a = 18;// "10010" length 5a.toString(2)   //  "i" length 1a.toString(32)  
具体的useId层级算法参考useId

总结

React源码外部有多种构造(比方用于保留context数据的)。

useId 的逻辑是其中比较复杂的一种。

谁能想到用法如此简略的API背地,实现起来竟然这么简单?

React团队捣鼓并发个性,真挺不容易的...