热搜: fiddler git ip 代理
历史搜索

React18引入了新Hook: useId,用于生成唯一id

游客2024-12-15 07:30:01
目录文章目录
  1. React18 来了,一切都变了
  2. useId 的原理
  3. 总结

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

// App.tsx
const 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推出了官方HookuseId,才解决以上问题。他的用法很简单:

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时,用户点击了右下角部分:

React18引入了新Hook: useId,用于生成唯一id 1

如果组件AD使用了useIdBC没有使用,那么只需要为AD划定层级,这样就能减少需要表示层级

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团队捣鼓并发特性,真挺不容易的…

标签:React