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

相关推荐
萧大侠jdeps3 分钟前
图片生成视频-右进
前端·javascript·音视频
Domain-zhuo26 分钟前
JS对于数组去重都有哪些方法?
开发语言·前端·javascript
明月清风徐徐1 小时前
Vue实训---2-路由搭建
前端·javascript·vue.js
鸽鸽程序猿1 小时前
【前端】javaScript
开发语言·前端·javascript
秦时明月之君临天下1 小时前
React和Next.js的相关内容
前端·javascript·react.js
米奇妙妙wuu2 小时前
React中 setState 是同步的还是异步的?调和阶段 setState 干了什么?
前端·javascript·react.js
李刚大人2 小时前
react-amap海量点优化
前端·react.js·前端框架
new出一个对象8 小时前
uniapp接入BMapGL百度地图
javascript·百度·uni-app
你挚爱的强哥9 小时前
✅✅✅【Vue.js】sd.js基于jQuery Ajax最新原生完整版for凯哥API版本
javascript·vue.js·jquery
前端Hardy9 小时前
纯HTML&CSS实现3D旋转地球
前端·javascript·css·3d·html