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,是为了服务端渲染的需求才有的,客户端渲染无需这种复杂计算。不同层使用不同的前缀,通过移位进行区分。

相关推荐
inksci31 分钟前
Vue 3 打开 el-dialog 时使 el-input 获取焦点
前端·javascript·vue.js
邝邝邝邝丹3 小时前
React学习———React Router
前端·学习·react.js
绝美焦栖3 小时前
vue复杂数据类型多层嵌套的监听
前端·javascript·vue.js
郝开6 小时前
扩展:React 项目执行 yarn eject 后的 package.json 变化详解及参数解析
react.js·前端框架·react
我是Superman丶7 小时前
【技巧】前端VUE用中文方法名调用没效果的问题
前端·javascript·vue.js
斯~内克7 小时前
Vue 3 中 watch 的使用与深入理解
前端·javascript·vue.js
lqj_本人10 小时前
鸿蒙OS&UniApp制作多选框与单选框组件#三方框架 #Uniapp
前端·javascript·uni-app
SHIPKING39313 小时前
【HTML】个人博客页面
javascript·css·html
junjun.chen060614 小时前
【在qiankun模式下el-dropdown点击,浏览器报Failed to execute ‘getComputedStyle‘ on ‘Window‘: parameter 1 is not o
前端·javascript·前端框架
課代表14 小时前
AcroForm JavaScript Promise 对象应用示例: 异步加载PDF文件
开发语言·javascript·pdf·promise·对象