共计 1861 个字符,预计需要花费 5 分钟才能阅读完成。
大家好,我卡颂。
看看如下组件有什么问题:
// App.tsx
const id = Math.random();
export default function App() {return <div id={id}>Hello</div>
}
如果利用是 CSR
(客户端渲染),id
是稳固的,App
组件没有问题。
但如果利用是 SSR
(服务端渲染),那么App.tsx
会经验:
React
在服务端渲染,生成随机id
(假如为0.1234
),这一步叫dehydrate
(脱水)<div id="0.12345">Hello</div>
作为HTML
传递给客户端,作为首屏内容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 Fizz
(React
新的服务端流式渲染器)的到来,渲染程序不再肯定。
比方,有个个性叫 Selective Hydration
,能够依据用户交互扭转 hydrate
的程序。
当下图左侧局部在 hydrate
时,用户点击了右下角局部:
此时 React
会优先对右下角局部hydrate
:
对于
Selective Hydration
更具体的解释见:New Suspense SSR Architecture in React 18
如果利用中应用 自增的全局计数变量
作为 id
,那么显然先hydrate
的组件 id
会更小,所以 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>;
}
理论须要思考两个因素:
1. 同一个组件应用多个 id
比方这样:
function B() {const id0 = useId();
const id1 = useId();
return (
<ul>
<li id={id0}></li>
<li id={id1}></li>
</ul>
);
}
2. 要跳过没有应用 useId 的组件
还是思考这个组件树结构:
如果组件 A
、D
应用了 useId
,B
、C
没有应用,那么只须要为 A
、D
划定层级,这样就能 缩小须要示意层级。
在 useId
的理论实现中,层级被示意为 32 进制 的数。
之所以抉择32 进制,是因为抉择尽可能大的进制会让生成的字符串尽可能紧凑。比方:
const a = 18;
// "10010" length 5
a.toString(2)
// "i" length 1
a.toString(32)
具体的
useId
层级算法参考 useId
总结
React
源码外部有多种 栈
构造(比方用于保留 context
数据的 栈
)。
useId
栈
的逻辑是其中比较复杂的一种。
谁能想到用法如此简略的 API
背地,实现起来竟然这么简单?
React
团队捣鼓 并发个性,真挺不容易的 …