const id = Math.random();
export default () => {
return 哈哈哈哈
}
1、React在服务端渲染,生成随机id(假设为0.1),这一步叫dehydrate(脱水)
2、
3、React在客户端渲染,生成随机id(假设为0.2),这一步叫hydrate(注水)
就会出现客户端、服务端生成的id不匹配!
当然,服务端、客户端无法简单生成稳定、唯一的id是个由来已久的问题,早在15年就有人提过issue:
https://github.com/facebook/react/issues/4000
那么react的useId就是用来解决这个问题的
import { useId } from 'react'
const id = useId();
export default () => {
return 哈哈哈哈
}
身份生成算法
身份 id 是 32 进制的字符串,其二进制表示对应树中节点的位置。
每次树分叉成多个子节点时,我们都会在序列的左侧添加额外的位数,表示子节点在当前子节点层级中的位置。
00101 00010001011010101
╰─┬─╯ ╰───────┬───────╯
Fork 5 of 20 Parent id
这里我们使用了两个前置 0 位。如果只考虑当前的子节点,我们使用 101 就可以了,但是要表达当前层级所有的子节点,三位就不够用。因此需要 5 位。
出于同样的原因,slots 是 1-indexed 而不是 0-indexed 。否则就无法区分该层级第 0 个子节点与父节点。
如果一个节点只有一个子节点,并且没有具体化的 id,声明时没有包含 useId hook。那么我们不需要在序列中分配任何空间。例如这两颗数会产生相同的 id:
<> <>
>
>
为了处理这种情况,每次我们生成一个 id 时,都会分配一个一个新的层级。当然这个层级就只有一个节点「长度为 1 的数组」。
最后,序列的大小可能会超出 32 位,发生这种情况时,我们通过将 id 的右侧部分转换为字符串并将其存储在溢出变量中。之所以使用 32 位字符串,是因为 32 是 toString() 支持的 2 的最大幂数。这样基数足够大就能够得到紧凑的 id 序列,并且我们希望基数是 2 的幂,因为每个 log2(base) 对应一个字符,也就是 log2(32) = 5 bits = 1 ,这样意味着我们可以在不影响最终结果的情况下删除末尾 5 的位。
参考https://www.fly63.com/article/detial/11316