Skip to content

useId

主要用于生成一个唯一的 ID,这个 ID 在同一个应用中是唯一的,并且在组件更新的时候不会改变。这个功能在处理服务端渲染、列表渲染、表单元素和无障碍属性时非常有用,因为它可以确保每个元素都有一个唯一的标识符。

例子

jsx
function IdComponent() {
  const [count, setCount] = React.useState(1);
  const vid = React.useId();

  const handleChange = () => {
    setCount(count + 1);
  };

  React.useEffect(() => {
    setCount(count + 1);
  }, []);
  return (
    <div>
      <div> count = {count}</div>
      <div> vid = {vid}</div>
      <button onClick={handleChange}>修改</button>
    </div>
  );
}

源码阅读

初次渲染

js
/**
 * 初次渲染阶段 - 生成组件唯一ID
 * @returns
 */
function mountId(): string {
  // 获取或者创建一个 hook 对象
  const hook = mountWorkInProgressHook();
  // 获取根节点
  const root = ((getWorkInProgressRoot(): any): FiberRoot);
  // 获取用户自定义的 id 前缀字符串
  // createRoot(dom , { identifierPrefix : 'myPrefix'  })
  const identifierPrefix = root.identifierPrefix;

  let id;
  // 水合状态 创建id
  if (getIsHydrating()) {
  } else {
    // 生成一个 `:r1` 类型的id
    // Use a lowercase r prefix for client-generated ids.
    const globalClientId = globalClientIdCounter++;
    id = ":" + identifierPrefix + "r" + globalClientId.toString(32) + ":";
  }
  // 缓存到 hook.memoizedState 上
  hook.memoizedState = id;
  return id;
}

核心就是通过一个 全局变量 globalClientIdCounter的自增去生成应用内唯一的 id,其格式为 :${identifierPrefix}r${id}:, 最后并将生成的值缓存到 hook.memoizedState

更新渲染、更新渲染更新阶段

js
/**
 * useId 在更新阶段实现方法
 * @returns
 */
function updateId(): string {
  // 获取或者创建一个 hook 对象
  const hook = updateWorkInProgressHook();
  // 直接复用缓存的id
  const id: string = hook.memoizedState;
  return id;
}

核心就是获取缓存到 hook.memoizedState上的 id 的值

使用场景

  1. 为可访问属性、无障碍属性生成唯一 ID

在表单中,<label> 标签需要通过 for 属性与对应的 <input> 标签的 id 属性相匹配,以实现点击标签时输入框获得焦点的功能。使用 useId 可以为每个 <input> 元素生成一个唯一的 id,确保这一功能的正常工作。例如

jsx
function IdForm() {
  const nameId = useId();    // 生成一个 id ':r1:'
  return (
    <div>
      <div>
        <label htmlFor={nameId}>用户名称</label>
        <input type="text" id={nameId} name="用户名称" />
      </div>
    </div>
  );
}