8
头图

Hi everyone, I'm Kasong.

See what is wrong with the following components:

// App.tsx
const id = Math.random();

export default function App() {
  return <div id={id}>Hello</div>
}

If the application is CSR (client rendering), id is stable, and there is no problem App

But if the application is SSR (server rendering), then App.tsx will experience:

  1. React is rendered on the server side to generate random id (assuming 0.1234 ), this step is called dehydrate (dehydration)
  2. <div id="0.12345">Hello</div> passed to the client as HTML as the first screen content
  3. React is rendered on the client and generates random id (assuming 0.6789 ), this step is called hydrate (water injection)

id generated by the client and server do not match!

In fact, the fact that the server and client cannot simply generate a stable and unique id is a long-standing problem. As early as 15 years, someone mentioned issue :

Generating random/unique attributes server-side that don't break client-side mounting

Until recently, React18 launched the official Hook —— useId to solve the above problems. His usage is very simple:

function Checkbox() {
  // 生成唯一、稳定id
  const id = useId();
  return (
    <>
      <label htmlFor={id}>Do you like React?</label>
      <input type="checkbox" name="react" id={id} />
    </>
  );
);

Although the usage is simple, the principle behind it is very interesting-each id represents the hierarchical structure of the component in the component tree.

This article lets us understand the principle of useId

Welcome to join human high-quality front-end frame group , take flight

React18 is here, everything has changed

Although this problem has always existed, the global count variable of auto-increment can be used as id . Consider the following example:

// 全局通用的计数变量
let globalIdIndex = 0;


export default function App() {
  const id = useState(() => globalIdIndex++);
  return <div id={id}>Hello</div>
}

As long as React on the server side and the client side is consistent, then the id produced by both ends corresponds.

However, with the React Fizz ( React new server-side streaming renderer), the rendering order is no longer certain.

For example, there is a feature called Selective Hydration , the user can interactively change the hydrate order.

When the left part of the hydrate is at 061a4c7d7baf34, the user clicks on the lower right part:

At this time, React will give priority to the lower right part hydrate :

Selective Hydration more detailed explanation of see: 161a4c7d7bafb0 New Suspense SSR Architecture in React 18

If the application uses the auto-incremented global count variable as id , then obviously the first hydrate component id will be smaller, so id is unstable.

So, is there a stable mark for both the server and the client?

The answer is: the hierarchy of components.

The principle of useId

Suppose the component tree of the application is as follows:

Regardless of whether B or C first hydrate , their hierarchical structure is the same, so the level itself can be used as a constant identifier between the server and the client.

For example, B can use 2-1 as id , and C use 2-2 as id :

function B() {
  // id为"2-1"
  const id = useId();
  return <div id={id}>B</div>;
}

There are actually two elements that need to be considered:

1. Use multiple IDs for the same component

For example:

function B() {
  const id0 = useId();
  const id1 = useId();
  return (
    <ul>
      <li id={id0}></li>
      <li id={id1}></li>
    </ul>
  );
}

2. To skip components that do not use useId

Still consider this component tree structure:

If the component A , D used useId , B , C not used, then you only need to A , D delineated hierarchy, so that we can reduce the need to represent the hierarchy .

In useId , the level is expressed as a 32-ary number.

The reason for choosing 32 hexadecimal is because choosing the largest possible hexadecimal will make the generated string as compact as possible. for example:

const a = 18;

// "10010" length 5
a.toString(2)   

//  "i" length 1
a.toString(32)  
For the specific useId level algorithm useId

Summarize

React source code has a variety of stack structures (such as the stack context data).

useId stack is one of the more complicated ones.

Who would have thought API , which is so simple to use, is so complicated?

React team with the , which is really not easy...


卡颂
3.1k 声望16.7k 粉丝