关于前端:为了生成唯一idReact18专门引入了新HookuseId

31次阅读

共计 1861 个字符,预计需要花费 5 分钟才能阅读完成。

大家好,我卡颂。

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

// 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推出了官网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 5
a.toString(2)   

//  "i" length 1
a.toString(32)  

具体的 useId 层级算法参考 useId

总结

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

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

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

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

正文完
 0