React useId Hook

React 中有一个 useId hook,可以生成一个唯一 ID,这个有什么用处呢,用个 UUID 是不是可以替代呢?如果我们只考虑客户端,那么生成唯一 Id 的方法比较简单,我们在 State 中保存一个计数器就好,但是 React 需要支持服务端渲染,需要确保服务器生成的内容与客户端保持一致。客户端和服务器端是完全不同的环境,通过简单的计数器无法确保两端的一致。我们看下面这个例子,例子中要将 label 和 输入框进行关联,可以 useId 这个 hook 来实现。通过简单的计数器无法实现

复制代码
import { useId } from 'react';

export default function Form() {
  const id = useId();
  return (
    <form>
      <label htmlFor={id + '-firstName'}>First Name:</label>
      <input id={id + '-firstName'} type="text" />
      <hr />
      <label htmlFor={id + '-lastName'}>Last Name:</label>
      <input id={id + '-lastName'} type="text" />
    </form>
  );
}

React 是通过 dom tree 的位置来实现的,不同层有不同的前缀,如下:

复制代码
<div>
  <div id="1">
    <div id="101" />
  </div>
  <div id="10" />
</div>

下面代码 React 源码中的 Id 生成算法,通过位置进行移位,并对索引进行相加。

复制代码
export function pushTreeId(
  workInProgress: Fiber,
  totalChildren: number,
  index: number,
) {
  warnIfNotHydrating();

  idStack[idStackIndex++] = treeContextId;
  idStack[idStackIndex++] = treeContextOverflow;
  idStack[idStackIndex++] = treeContextProvider;

  treeContextProvider = workInProgress;

  const baseIdWithLeadingBit = treeContextId;
  const baseOverflow = treeContextOverflow;

  // The leftmost 1 marks the end of the sequence, non-inclusive. It's not part
  // of the id; we use it to account for leading 0s.
  const baseLength = getBitLength(baseIdWithLeadingBit) - 1;
  const baseId = baseIdWithLeadingBit & ~(1 << baseLength);

  const slot = index + 1;
  const length = getBitLength(totalChildren) + baseLength;

  // 30 is the max length we can store without overflowing, taking into
  // consideration the leading 1 we use to mark the end of the sequence.
  if (length > 30) {
    // We overflowed the bitwise-safe range. Fall back to slower algorithm.
    // This branch assumes the length of the base id is greater than 5; it won't
    // work for smaller ids, because you need 5 bits per character.
    //
    // We encode the id in multiple steps: first the base id, then the
    // remaining digits.
    //
    // Each 5 bit sequence corresponds to a single base 32 character. So for
    // example, if the current id is 23 bits long, we can convert 20 of those
    // bits into a string of 4 characters, with 3 bits left over.
    //
    // First calculate how many bits in the base id represent a complete
    // sequence of characters.
    const numberOfOverflowBits = baseLength - (baseLength % 5);

    // Then create a bitmask that selects only those bits.
    const newOverflowBits = (1 << numberOfOverflowBits) - 1;

    // Select the bits, and convert them to a base 32 string.
    const newOverflow = (baseId & newOverflowBits).toString(32);

    // Now we can remove those bits from the base id.
    const restOfBaseId = baseId >> numberOfOverflowBits;
    const restOfBaseLength = baseLength - numberOfOverflowBits;

    // Finally, encode the rest of the bits using the normal algorithm. Because
    // we made more room, this time it won't overflow.
    const restOfLength = getBitLength(totalChildren) + restOfBaseLength;
    const restOfNewBits = slot << restOfBaseLength;
    const id = restOfNewBits | restOfBaseId;
    const overflow = newOverflow + baseOverflow;

    treeContextId = (1 << restOfLength) | id;
    treeContextOverflow = overflow;
  } else {
    // Normal path
    const newBits = slot << baseLength;
    const id = newBits | baseId;
    const overflow = baseOverflow;

    treeContextId = (1 << length) | id;
    treeContextOverflow = overflow;
  }
}

这个Id 算法只有服务器渲染才需要,如果只是客户端渲染无需这么复杂的计算,React 也做了这样的处理,代码如下,客户端渲染直接使用全局计数器。

总结

useId 这个 Hook,是为了服务端渲染的需求才有的,客户端渲染无需这种复杂计算。不同层使用不同的前缀,通过移位进行区分。

相关推荐
牧濑红莉21 小时前
网络安全竞赛pwn全解及第一道ai的wp
javascript·.net
kyriewen21 小时前
别再用AI debug了——这5种bug越修越烂
前端·javascript·ai编程
顾昂_1 天前
内存泄漏排查和 Chrome DevTools 使用教程
前端·javascript·面试
An_s1 天前
rust开发wasm与浏览器(js)交互
javascript·rust·wasm
山河木马1 天前
GPU自动处理专题4-填充覆盖区域(光栅化)
javascript·webgl·计算机图形学
悦儿遥遥雨11 天前
PXE + Kickstart 无人值守批量部署系统
linux·javascript·nginx
清秋1 天前
全网最全 ECMAScript 攻略( 更新至 ES2026)
前端·javascript·编程语言
hxy06011 天前
React-Call让你的React组件像异步函数一样调用
前端·react.js
Hilaku1 天前
为什么 AI 写前端时,总是优先选择 React,而不是 Vue?
前端·javascript·程序员
Patrick_Wilson1 天前
从一次 Safari 白屏,聊聊 CDN 外置 React 的坑与前端监控盲区
前端·react.js·cdn