Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

useId #22644

Merged
merged 5 commits into from
Nov 1, 2021
Merged

useId #22644

merged 5 commits into from
Nov 1, 2021

Conversation

acdlite
Copy link
Collaborator

@acdlite acdlite commented Oct 28, 2021

  • Incremental hydration
  • Empty nodes inside arrays
  • Long sequences (> 32 bits)
  • Add comments to explain the id generation algorithm
  • In Fiber, find better way to get the number of children in the current set. Might need to store this on the parent fiber. [Ended up storing this on the context stack so that it's zero cost after hydration. Not totally confident it's the right trade off, though.]
  • In Fizz, we have to convert iterators to an array before we start recursively rendering. I think this is inherent to the algorithm we've chosen. Maybe not a big deal, especially since it's a rarely used feature.
  • react-debug-tools
  • Partial renderer (legacy server renderer) support? Do we need this? [I think I'll punt on this for now, since in open source the legacy API is implemented on top of the new renderer.]

New hook API that generates stable ids during server rendering and hydration to avoid mismatches.

Outside of server-rendered content, it falls back to a global counter.

This is intended to replace useOpaqueIdentifier.

Id generation algorithm

Ids are base 32 strings whose binary representation corresponds to the position of a node in a tree.

Every time the tree forks into multiple children, we add additional bits to the left of the sequence that represent the position of the child within the current level of children.

    00101       00010001011010101
    ╰─┬─╯       ╰───────┬───────╯
  Fork 5 of 20       Parent id

The leading 0s are important. In the above example, you only need 3 bits to represent slot 5. However, you need 5 bits to represent all the forks at the current level, so we must account for the empty bits at the end.

For this same reason, slots are 1-indexed instead of 0-indexed. Otherwise, the zeroth id at a level would be indistinguishable from its parent.

If a node has only one child, and does not materialize an id (i.e. does not contain a useId hook), then we don't need to allocate any space in the sequence. It's treated as a transparent indirection. For example, these two trees produce the same ids:

<>                          <>
  <Indirection>               <A />
    <A />                     <B />
  </Indirection>            </>
  <B />
</>

However, we cannot skip any node that materializes an id. Otherwise, a parent id that does not fork would be indistinguishable from its child id. For example, this tree does not fork, but the parent and child must have different ids.

<Parent>
  <Child />
</Parent>

To handle this scenario, every time we materialize an id, we allocate a new level with a single slot. You can think of this as a fork with only one prong, or an array of children with length 1.

It's possible for the the size of the sequence to exceed 32 bits, the max size for bitwise operations. When this happens, we make more room by converting the right part of the id to a string and storing it in an overflow variable. We use a base 32 string representation, because 32 is the largest power of 2 that is supported by toString(). We want the base to be large so that the resulting ids are compact, and we want the base to be a power of 2 because every log2(base) bits corresponds to a single character, i.e. every log2(32) = 5 bits = 1 base 32 character. That means we can lop bits off the end 5 at a time without affecting the final result.

@sizebot
Copy link

sizebot commented Oct 28, 2021

Comparing: a0d991f...371302f

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.min.js +0.90% 128.86 kB 130.01 kB +1.31% 41.01 kB 41.55 kB
oss-experimental/react-dom/cjs/react-dom.production.min.js +0.87% 133.83 kB 134.99 kB +1.18% 42.52 kB 43.02 kB
facebook-www/ReactDOM-prod.classic.js +1.05% 419.90 kB 424.29 kB +1.22% 77.34 kB 78.28 kB
facebook-www/ReactDOM-prod.modern.js +1.07% 408.48 kB 412.85 kB +1.22% 75.59 kB 76.51 kB
facebook-www/ReactDOMForked-prod.classic.js +1.05% 419.90 kB 424.29 kB +1.22% 77.34 kB 78.28 kB
oss-stable-semver/react-server/cjs/react-server.development.js +7.18% 116.04 kB 124.37 kB +9.59% 28.56 kB 31.30 kB
oss-stable/react-server/cjs/react-server.development.js +7.18% 116.04 kB 124.37 kB +9.59% 28.56 kB 31.30 kB
oss-experimental/react-server/cjs/react-server.development.js +7.15% 116.55 kB 124.88 kB +9.53% 28.70 kB 31.44 kB
oss-stable-semver/react-server/cjs/react-server.production.min.js +4.90% 18.69 kB 19.60 kB +5.70% 6.51 kB 6.88 kB
oss-stable/react-server/cjs/react-server.production.min.js +4.90% 18.69 kB 19.60 kB +5.70% 6.51 kB 6.88 kB
oss-experimental/react-server/cjs/react-server.production.min.js +4.86% 18.87 kB 19.78 kB +5.71% 6.56 kB 6.94 kB
oss-stable-semver/react-dom/cjs/react-dom-server.browser.development.js +3.81% 218.56 kB 226.89 kB +5.12% 52.84 kB 55.55 kB
oss-stable/react-dom/cjs/react-dom-server.browser.development.js +3.81% 218.56 kB 226.89 kB +5.12% 52.84 kB 55.55 kB
oss-stable-semver/react-dom/cjs/react-dom-server.node.development.js +3.81% 218.57 kB 226.90 kB +5.13% 52.74 kB 55.44 kB
oss-stable/react-dom/cjs/react-dom-server.node.development.js +3.81% 218.57 kB 226.90 kB +5.13% 52.74 kB 55.44 kB
oss-stable-semver/react-dom/umd/react-dom-server.browser.development.js +3.81% 229.46 kB 238.20 kB +5.05% 53.43 kB 56.13 kB
oss-stable/react-dom/umd/react-dom-server.browser.development.js +3.81% 229.46 kB 238.20 kB +5.05% 53.43 kB 56.13 kB
oss-experimental/react-dom/cjs/react-dom-server.browser.development.js +3.80% 219.08 kB 227.42 kB +5.09% 52.99 kB 55.69 kB
oss-experimental/react-dom/cjs/react-dom-server.node.development.js +3.80% 219.09 kB 227.43 kB +5.11% 52.89 kB 55.59 kB
oss-experimental/react-dom/umd/react-dom-server.browser.development.js +3.80% 230.02 kB 238.75 kB +5.02% 53.58 kB 56.27 kB
facebook-www/ReactDOMServer-dev.modern.js +3.79% 220.83 kB 229.19 kB +5.19% 52.33 kB 55.05 kB
facebook-www/ReactDOMServer-prod.modern.js +3.78% 72.70 kB 75.45 kB +4.43% 15.61 kB 16.30 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.browser.development.js +3.77% 221.04 kB 229.38 kB +5.08% 53.18 kB 55.88 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.browser.development.js +3.77% 221.04 kB 229.38 kB +5.08% 53.18 kB 55.88 kB
oss-stable-semver/react-dom/umd/react-dom-server-legacy.browser.development.js +3.76% 232.11 kB 240.84 kB +5.01% 53.78 kB 56.48 kB
oss-stable/react-dom/umd/react-dom-server-legacy.browser.development.js +3.76% 232.11 kB 240.84 kB +5.01% 53.78 kB 56.48 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.browser.development.js +3.76% 221.57 kB 229.90 kB +5.07% 53.33 kB 56.03 kB
oss-experimental/react-dom/umd/react-dom-server-legacy.browser.development.js +3.75% 232.67 kB 241.40 kB +4.99% 53.93 kB 56.62 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.node.development.js +3.74% 222.65 kB 230.99 kB +5.05% 53.62 kB 56.32 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.node.development.js +3.74% 222.65 kB 230.99 kB +5.05% 53.62 kB 56.32 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.node.development.js +3.73% 223.18 kB 231.51 kB +5.03% 53.76 kB 56.47 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.browser.production.min.js +2.93% 31.25 kB 32.16 kB +3.55% 10.41 kB 10.78 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.browser.production.min.js +2.93% 31.25 kB 32.16 kB +3.55% 10.41 kB 10.78 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.browser.production.min.js +2.92% 31.38 kB 32.30 kB +3.52% 10.46 kB 10.83 kB
oss-stable-semver/react-dom/umd/react-dom-server-legacy.browser.production.min.js +2.89% 31.44 kB 32.35 kB +3.55% 10.55 kB 10.92 kB
oss-stable/react-dom/umd/react-dom-server-legacy.browser.production.min.js +2.89% 31.44 kB 32.35 kB +3.55% 10.55 kB 10.92 kB
oss-experimental/react-dom/umd/react-dom-server-legacy.browser.production.min.js +2.88% 31.58 kB 32.49 kB +3.54% 10.60 kB 10.98 kB
oss-stable-semver/react-dom/cjs/react-dom-server.browser.production.min.js +2.87% 31.90 kB 32.82 kB +3.44% 10.84 kB 11.21 kB
oss-stable/react-dom/cjs/react-dom-server.browser.production.min.js +2.87% 31.90 kB 32.82 kB +3.44% 10.84 kB 11.21 kB
oss-experimental/react-dom/cjs/react-dom-server.browser.production.min.js +2.86% 32.04 kB 32.96 kB +3.42% 10.89 kB 11.27 kB
oss-stable-semver/react-dom/umd/react-dom-server.browser.production.min.js +2.83% 32.08 kB 32.99 kB +3.45% 10.96 kB 11.33 kB
oss-stable/react-dom/umd/react-dom-server.browser.production.min.js +2.83% 32.08 kB 32.99 kB +3.45% 10.96 kB 11.33 kB
oss-experimental/react-dom/umd/react-dom-server.browser.production.min.js +2.82% 32.22 kB 33.12 kB +3.41% 11.01 kB 11.38 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.node.production.min.js +2.62% 34.92 kB 35.84 kB +3.26% 11.64 kB 12.02 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.node.production.min.js +2.62% 34.92 kB 35.84 kB +3.26% 11.64 kB 12.02 kB
oss-stable-semver/react-dom/cjs/react-dom-server.node.production.min.js +2.61% 35.08 kB 36.00 kB +3.24% 11.80 kB 12.18 kB
oss-stable/react-dom/cjs/react-dom-server.node.production.min.js +2.61% 35.08 kB 36.00 kB +3.24% 11.80 kB 12.18 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.node.production.min.js +2.61% 35.12 kB 36.03 kB +3.24% 11.71 kB 12.09 kB
oss-experimental/react-dom/cjs/react-dom-server.node.production.min.js +2.60% 35.28 kB 36.19 kB +3.19% 11.87 kB 12.25 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable-semver/react-server/cjs/react-server.development.js +7.18% 116.04 kB 124.37 kB +9.59% 28.56 kB 31.30 kB
oss-stable/react-server/cjs/react-server.development.js +7.18% 116.04 kB 124.37 kB +9.59% 28.56 kB 31.30 kB
oss-experimental/react-server/cjs/react-server.development.js +7.15% 116.55 kB 124.88 kB +9.53% 28.70 kB 31.44 kB
oss-stable-semver/react-server/cjs/react-server.production.min.js +4.90% 18.69 kB 19.60 kB +5.70% 6.51 kB 6.88 kB
oss-stable/react-server/cjs/react-server.production.min.js +4.90% 18.69 kB 19.60 kB +5.70% 6.51 kB 6.88 kB
oss-experimental/react-server/cjs/react-server.production.min.js +4.86% 18.87 kB 19.78 kB +5.71% 6.56 kB 6.94 kB
oss-stable-semver/react-dom/cjs/react-dom-server.browser.development.js +3.81% 218.56 kB 226.89 kB +5.12% 52.84 kB 55.55 kB
oss-stable/react-dom/cjs/react-dom-server.browser.development.js +3.81% 218.56 kB 226.89 kB +5.12% 52.84 kB 55.55 kB
oss-stable-semver/react-dom/cjs/react-dom-server.node.development.js +3.81% 218.57 kB 226.90 kB +5.13% 52.74 kB 55.44 kB
oss-stable/react-dom/cjs/react-dom-server.node.development.js +3.81% 218.57 kB 226.90 kB +5.13% 52.74 kB 55.44 kB
oss-stable-semver/react-dom/umd/react-dom-server.browser.development.js +3.81% 229.46 kB 238.20 kB +5.05% 53.43 kB 56.13 kB
oss-stable/react-dom/umd/react-dom-server.browser.development.js +3.81% 229.46 kB 238.20 kB +5.05% 53.43 kB 56.13 kB
oss-experimental/react-dom/cjs/react-dom-server.browser.development.js +3.80% 219.08 kB 227.42 kB +5.09% 52.99 kB 55.69 kB
oss-experimental/react-dom/cjs/react-dom-server.node.development.js +3.80% 219.09 kB 227.43 kB +5.11% 52.89 kB 55.59 kB
oss-experimental/react-dom/umd/react-dom-server.browser.development.js +3.80% 230.02 kB 238.75 kB +5.02% 53.58 kB 56.27 kB
facebook-www/ReactDOMServer-dev.modern.js +3.79% 220.83 kB 229.19 kB +5.19% 52.33 kB 55.05 kB
facebook-www/ReactDOMServer-prod.modern.js +3.78% 72.70 kB 75.45 kB +4.43% 15.61 kB 16.30 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.browser.development.js +3.77% 221.04 kB 229.38 kB +5.08% 53.18 kB 55.88 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.browser.development.js +3.77% 221.04 kB 229.38 kB +5.08% 53.18 kB 55.88 kB
oss-stable-semver/react-dom/umd/react-dom-server-legacy.browser.development.js +3.76% 232.11 kB 240.84 kB +5.01% 53.78 kB 56.48 kB
oss-stable/react-dom/umd/react-dom-server-legacy.browser.development.js +3.76% 232.11 kB 240.84 kB +5.01% 53.78 kB 56.48 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.browser.development.js +3.76% 221.57 kB 229.90 kB +5.07% 53.33 kB 56.03 kB
oss-experimental/react-dom/umd/react-dom-server-legacy.browser.development.js +3.75% 232.67 kB 241.40 kB +4.99% 53.93 kB 56.62 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.node.development.js +3.74% 222.65 kB 230.99 kB +5.05% 53.62 kB 56.32 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.node.development.js +3.74% 222.65 kB 230.99 kB +5.05% 53.62 kB 56.32 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.node.development.js +3.73% 223.18 kB 231.51 kB +5.03% 53.76 kB 56.47 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.browser.production.min.js +2.93% 31.25 kB 32.16 kB +3.55% 10.41 kB 10.78 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.browser.production.min.js +2.93% 31.25 kB 32.16 kB +3.55% 10.41 kB 10.78 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.browser.production.min.js +2.92% 31.38 kB 32.30 kB +3.52% 10.46 kB 10.83 kB
oss-stable-semver/react-dom/umd/react-dom-server-legacy.browser.production.min.js +2.89% 31.44 kB 32.35 kB +3.55% 10.55 kB 10.92 kB
oss-stable/react-dom/umd/react-dom-server-legacy.browser.production.min.js +2.89% 31.44 kB 32.35 kB +3.55% 10.55 kB 10.92 kB
oss-experimental/react-dom/umd/react-dom-server-legacy.browser.production.min.js +2.88% 31.58 kB 32.49 kB +3.54% 10.60 kB 10.98 kB
oss-stable-semver/react-dom/cjs/react-dom-server.browser.production.min.js +2.87% 31.90 kB 32.82 kB +3.44% 10.84 kB 11.21 kB
oss-stable/react-dom/cjs/react-dom-server.browser.production.min.js +2.87% 31.90 kB 32.82 kB +3.44% 10.84 kB 11.21 kB
oss-experimental/react-dom/cjs/react-dom-server.browser.production.min.js +2.86% 32.04 kB 32.96 kB +3.42% 10.89 kB 11.27 kB
oss-stable-semver/react-dom/umd/react-dom-server.browser.production.min.js +2.83% 32.08 kB 32.99 kB +3.45% 10.96 kB 11.33 kB
oss-stable/react-dom/umd/react-dom-server.browser.production.min.js +2.83% 32.08 kB 32.99 kB +3.45% 10.96 kB 11.33 kB
oss-experimental/react-dom/umd/react-dom-server.browser.production.min.js +2.82% 32.22 kB 33.12 kB +3.41% 11.01 kB 11.38 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.node.production.min.js +2.62% 34.92 kB 35.84 kB +3.26% 11.64 kB 12.02 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.node.production.min.js +2.62% 34.92 kB 35.84 kB +3.26% 11.64 kB 12.02 kB
oss-stable-semver/react-dom/cjs/react-dom-server.node.production.min.js +2.61% 35.08 kB 36.00 kB +3.24% 11.80 kB 12.18 kB
oss-stable/react-dom/cjs/react-dom-server.node.production.min.js +2.61% 35.08 kB 36.00 kB +3.24% 11.80 kB 12.18 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.node.production.min.js +2.61% 35.12 kB 36.03 kB +3.24% 11.71 kB 12.09 kB
oss-experimental/react-dom/cjs/react-dom-server.node.production.min.js +2.60% 35.28 kB 36.19 kB +3.19% 11.87 kB 12.25 kB
oss-experimental/react-debug-tools/cjs/react-debug-tools.production.min.js +1.81% 6.59 kB 6.70 kB +0.53% 2.43 kB 2.45 kB
oss-stable-semver/react-debug-tools/cjs/react-debug-tools.production.min.js +1.81% 6.59 kB 6.70 kB +0.53% 2.43 kB 2.45 kB
oss-stable/react-debug-tools/cjs/react-debug-tools.production.min.js +1.81% 6.59 kB 6.70 kB +0.53% 2.43 kB 2.45 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler.development.js +1.62% 718.53 kB 730.15 kB +2.01% 152.46 kB 155.53 kB
oss-stable/react-reconciler/cjs/react-reconciler.development.js +1.62% 718.53 kB 730.15 kB +2.01% 152.46 kB 155.53 kB
oss-experimental/react-reconciler/cjs/react-reconciler.development.js +1.56% 745.43 kB 757.05 kB +1.92% 158.19 kB 161.23 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler.production.min.js +1.27% 90.74 kB 91.90 kB +1.86% 27.79 kB 28.31 kB
oss-stable/react-reconciler/cjs/react-reconciler.production.min.js +1.27% 90.74 kB 91.90 kB +1.86% 27.79 kB 28.31 kB
oss-experimental/react-reconciler/cjs/react-reconciler.production.min.js +1.21% 95.35 kB 96.50 kB +1.79% 29.04 kB 29.56 kB
oss-stable-semver/react-dom/umd/react-dom.development.js +1.18% 1,039.63 kB 1,051.85 kB +1.37% 224.80 kB 227.89 kB
oss-stable/react-dom/umd/react-dom.development.js +1.18% 1,039.63 kB 1,051.85 kB +1.37% 224.80 kB 227.89 kB
oss-stable-semver/react-dom/cjs/react-dom.development.js +1.17% 990.97 kB 1,002.59 kB +1.36% 222.52 kB 225.54 kB
oss-stable/react-dom/cjs/react-dom.development.js +1.17% 990.97 kB 1,002.59 kB +1.36% 222.52 kB 225.54 kB
facebook-www/ReactDOMTesting-dev.modern.js +1.17% 999.81 kB 1,011.47 kB +1.36% 224.88 kB 227.94 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler.profiling.min.js +1.16% 99.75 kB 100.91 kB +1.64% 30.36 kB 30.86 kB
oss-stable/react-reconciler/cjs/react-reconciler.profiling.min.js +1.16% 99.75 kB 100.91 kB +1.64% 30.36 kB 30.86 kB
oss-experimental/react-dom/umd/react-dom.development.js +1.14% 1,069.72 kB 1,081.94 kB +1.33% 230.79 kB 233.86 kB
oss-experimental/react-dom/cjs/react-dom.development.js +1.14% 1,019.65 kB 1,031.27 kB +1.32% 228.51 kB 231.52 kB
facebook-www/ReactDOMTesting-dev.classic.js +1.14% 1,026.56 kB 1,038.22 kB +1.33% 230.34 kB 233.40 kB
oss-experimental/react-suspense-test-utils/cjs/react-suspense-test-utils.js +1.12% 2.15 kB 2.17 kB +0.46% 0.86 kB 0.87 kB
oss-stable-semver/react-suspense-test-utils/cjs/react-suspense-test-utils.js +1.12% 2.15 kB 2.17 kB +0.46% 0.86 kB 0.87 kB
oss-stable/react-suspense-test-utils/cjs/react-suspense-test-utils.js +1.12% 2.15 kB 2.17 kB +0.46% 0.86 kB 0.87 kB
oss-experimental/react-reconciler/cjs/react-reconciler.profiling.min.js +1.11% 104.37 kB 105.53 kB +1.79% 31.66 kB 32.23 kB
facebook-www/ReactDOMTesting-prod.modern.js +1.09% 402.40 kB 406.77 kB +1.23% 75.85 kB 76.78 kB
facebook-www/ReactDOM-prod.modern.js +1.07% 408.48 kB 412.85 kB +1.22% 75.59 kB 76.51 kB
facebook-www/ReactDOMForked-prod.modern.js +1.07% 408.48 kB 412.85 kB +1.22% 75.59 kB 76.52 kB
facebook-www/ReactDOMForked-dev.modern.js +1.06% 1,088.72 kB 1,100.30 kB +1.26% 241.24 kB 244.27 kB
facebook-www/ReactDOM-dev.modern.js +1.06% 1,088.72 kB 1,100.30 kB +1.26% 241.24 kB 244.27 kB
facebook-www/ReactDOMTesting-prod.classic.js +1.06% 415.68 kB 420.08 kB +1.23% 77.98 kB 78.94 kB
facebook-www/ReactDOM-prod.classic.js +1.05% 419.90 kB 424.29 kB +1.22% 77.34 kB 78.28 kB
facebook-www/ReactDOMForked-prod.classic.js +1.05% 419.90 kB 424.29 kB +1.22% 77.34 kB 78.28 kB
facebook-www/ReactDOMForked-dev.classic.js +1.04% 1,112.84 kB 1,124.43 kB +1.25% 246.10 kB 249.17 kB
facebook-www/ReactDOM-dev.classic.js +1.04% 1,112.84 kB 1,124.43 kB +1.25% 246.10 kB 249.17 kB
facebook-www/ReactDOM-profiling.modern.js +0.99% 440.74 kB 445.11 kB +1.14% 81.25 kB 82.17 kB
facebook-www/ReactDOMForked-profiling.modern.js +0.99% 440.74 kB 445.11 kB +1.14% 81.25 kB 82.17 kB
facebook-www/ReactDOM-profiling.classic.js +0.97% 452.21 kB 456.61 kB +1.12% 82.96 kB 83.89 kB
facebook-www/ReactDOMForked-profiling.classic.js +0.97% 452.21 kB 456.61 kB +1.12% 82.97 kB 83.90 kB
oss-experimental/react-debug-tools/cjs/react-debug-tools.development.js +0.92% 23.39 kB 23.61 kB +0.47% 6.19 kB 6.22 kB
oss-stable-semver/react-debug-tools/cjs/react-debug-tools.development.js +0.92% 23.39 kB 23.61 kB +0.47% 6.19 kB 6.22 kB
oss-stable/react-debug-tools/cjs/react-debug-tools.development.js +0.92% 23.39 kB 23.61 kB +0.47% 6.19 kB 6.22 kB
oss-experimental/react/cjs/react-unstable-shared-subset.production.min.js +0.90% 6.63 kB 6.69 kB +0.33% 2.69 kB 2.70 kB
oss-stable-semver/react-dom/cjs/react-dom.production.min.js +0.90% 128.86 kB 130.01 kB +1.31% 41.01 kB 41.55 kB
oss-stable/react-dom/cjs/react-dom.production.min.js +0.90% 128.86 kB 130.01 kB +1.31% 41.01 kB 41.55 kB
oss-stable-semver/react-dom/umd/react-dom.production.min.js +0.89% 128.99 kB 130.14 kB +1.22% 41.77 kB 42.28 kB
oss-stable/react-dom/umd/react-dom.production.min.js +0.89% 128.99 kB 130.14 kB +1.22% 41.77 kB 42.28 kB
oss-experimental/react-dom/cjs/react-dom.production.min.js +0.87% 133.83 kB 134.99 kB +1.18% 42.52 kB 43.02 kB
oss-experimental/react-dom/umd/react-dom.production.min.js +0.86% 133.88 kB 135.03 kB +1.22% 43.17 kB 43.69 kB
oss-stable-semver/react-dom/umd/react-dom.profiling.min.js +0.84% 137.88 kB 139.04 kB +1.16% 44.44 kB 44.96 kB
oss-stable/react-dom/umd/react-dom.profiling.min.js +0.84% 137.88 kB 139.04 kB +1.16% 44.44 kB 44.96 kB
oss-stable-semver/react-dom/cjs/react-dom.profiling.min.js +0.84% 138.92 kB 140.08 kB +1.27% 44.06 kB 44.62 kB
oss-stable/react-dom/cjs/react-dom.profiling.min.js +0.84% 138.92 kB 140.08 kB +1.27% 44.06 kB 44.62 kB
oss-stable-semver/react/cjs/react.production.min.js +0.82% 7.32 kB 7.38 kB +0.24% 2.87 kB 2.88 kB
oss-stable/react/cjs/react.production.min.js +0.82% 7.32 kB 7.38 kB +0.24% 2.87 kB 2.88 kB
oss-experimental/react-dom/umd/react-dom.profiling.min.js +0.81% 142.77 kB 143.92 kB +1.18% 45.87 kB 46.42 kB
oss-experimental/react-dom/cjs/react-dom.profiling.min.js +0.81% 143.91 kB 145.07 kB +1.10% 45.65 kB 46.16 kB
oss-experimental/react/cjs/react.production.min.js +0.76% 7.92 kB 7.98 kB +0.20% 3.01 kB 3.02 kB
oss-stable-semver/react-test-renderer/umd/react-test-renderer.development.js +0.69% 639.92 kB 644.31 kB +0.80% 135.23 kB 136.31 kB
oss-stable/react-test-renderer/umd/react-test-renderer.development.js +0.69% 639.92 kB 644.31 kB +0.80% 135.23 kB 136.31 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.development.js +0.68% 610.37 kB 614.53 kB +0.80% 133.71 kB 134.78 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.development.js +0.68% 610.37 kB 614.53 kB +0.80% 133.71 kB 134.78 kB
facebook-react-native/react-test-renderer/cjs/ReactTestRenderer-dev.js +0.67% 622.77 kB 626.93 kB +0.82% 134.89 kB 135.99 kB
oss-stable-semver/react-art/cjs/react-art.development.js +0.67% 667.96 kB 672.41 kB +0.78% 144.68 kB 145.81 kB
oss-stable/react-art/cjs/react-art.development.js +0.67% 667.96 kB 672.41 kB +0.78% 144.68 kB 145.81 kB
oss-experimental/react-test-renderer/umd/react-test-renderer.development.js +0.66% 667.92 kB 672.31 kB +0.74% 140.94 kB 141.97 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.development.js +0.65% 637.05 kB 641.21 kB +0.77% 139.39 kB 140.46 kB
oss-experimental/react-art/cjs/react-art.development.js +0.64% 694.67 kB 699.12 kB +0.74% 150.39 kB 151.51 kB
facebook-www/ReactTestRenderer-dev.classic.js +0.62% 670.49 kB 674.65 kB +0.76% 144.41 kB 145.51 kB
facebook-www/ReactTestRenderer-dev.modern.js +0.62% 670.50 kB 674.66 kB +0.76% 144.42 kB 145.52 kB
oss-stable-semver/react-art/umd/react-art.development.js +0.61% 770.56 kB 775.25 kB +0.71% 162.64 kB 163.79 kB
oss-stable/react-art/umd/react-art.development.js +0.61% 770.56 kB 775.25 kB +0.71% 162.64 kB 163.79 kB
facebook-www/ReactART-dev.modern.js +0.59% 745.75 kB 750.14 kB +0.71% 159.40 kB 160.52 kB
oss-experimental/react-art/umd/react-art.development.js +0.59% 798.57 kB 803.26 kB +0.65% 168.29 kB 169.37 kB
facebook-www/ReactART-dev.classic.js +0.58% 755.96 kB 760.36 kB +0.70% 161.49 kB 162.63 kB
react-native/implementations/ReactFabric-dev.js +0.58% 718.46 kB 722.62 kB +0.72% 156.04 kB 157.15 kB
react-native/implementations/ReactNativeRenderer-dev.js +0.57% 732.39 kB 736.55 kB +0.71% 159.31 kB 160.44 kB
react-native/implementations/ReactFabric-dev.fb.js +0.55% 751.78 kB 755.94 kB +0.70% 162.05 kB 163.18 kB
react-native/implementations/ReactNativeRenderer-dev.fb.js +0.54% 763.74 kB 767.90 kB +0.69% 164.98 kB 166.11 kB
facebook-react-native/react/cjs/React-prod.js +0.52% 17.22 kB 17.31 kB +0.18% 4.39 kB 4.40 kB
facebook-www/React-prod.modern.js +0.52% 17.30 kB 17.39 kB +0.20% 4.42 kB 4.43 kB
facebook-www/React-prod.classic.js +0.52% 17.45 kB 17.54 kB +0.18% 4.47 kB 4.48 kB
facebook-react-native/react-test-renderer/cjs/ReactTestRenderer-prod.js +0.50% 235.86 kB 237.03 kB +0.56% 43.26 kB 43.51 kB
oss-stable-semver/react-art/cjs/react-art.production.min.js +0.49% 81.66 kB 82.06 kB +0.58% 25.28 kB 25.42 kB
oss-stable/react-art/cjs/react-art.production.min.js +0.49% 81.66 kB 82.06 kB +0.58% 25.28 kB 25.42 kB
facebook-react-native/react/cjs/React-profiling.js +0.49% 18.37 kB 18.46 kB +0.20% 4.57 kB 4.58 kB
facebook-www/React-profiling.modern.js +0.49% 18.45 kB 18.54 kB +0.26% 4.60 kB 4.61 kB
facebook-www/React-profiling.classic.js +0.48% 18.60 kB 18.69 kB +0.15% 4.65 kB 4.65 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.production.min.js +0.48% 84.11 kB 84.51 kB +0.60% 25.93 kB 26.09 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.production.min.js +0.48% 84.11 kB 84.51 kB +0.60% 25.93 kB 26.09 kB
oss-stable-semver/react/umd/react.profiling.min.js +0.47% 11.37 kB 11.43 kB +0.31% 4.51 kB 4.52 kB
oss-stable/react/umd/react.profiling.min.js +0.47% 11.37 kB 11.43 kB +0.31% 4.51 kB 4.52 kB
oss-stable-semver/react/umd/react.production.min.js +0.47% 11.37 kB 11.43 kB +0.31% 4.51 kB 4.52 kB
oss-stable/react/umd/react.production.min.js +0.47% 11.37 kB 11.43 kB +0.31% 4.51 kB 4.52 kB
oss-stable-semver/react-test-renderer/umd/react-test-renderer.production.min.js +0.47% 84.33 kB 84.73 kB +0.70% 26.21 kB 26.39 kB
oss-stable/react-test-renderer/umd/react-test-renderer.production.min.js +0.47% 84.33 kB 84.73 kB +0.70% 26.21 kB 26.39 kB
facebook-react-native/react-test-renderer/cjs/ReactTestRenderer-profiling.js +0.47% 250.96 kB 252.14 kB +0.52% 45.66 kB 45.89 kB
oss-experimental/react-art/cjs/react-art.production.min.js +0.47% 86.21 kB 86.61 kB +0.53% 26.61 kB 26.75 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.production.min.js +0.46% 88.65 kB 89.05 kB +0.58% 27.26 kB 27.41 kB
oss-experimental/react/umd/react.profiling.min.js +0.45% 11.90 kB 11.96 kB +0.17% 4.65 kB 4.66 kB
oss-experimental/react/umd/react.production.min.js +0.45% 11.91 kB 11.96 kB +0.19% 4.65 kB 4.66 kB
oss-experimental/react-test-renderer/umd/react-test-renderer.production.min.js +0.45% 88.86 kB 89.26 kB +0.39% 27.60 kB 27.71 kB
react-native/implementations/ReactFabric-prod.js +0.43% 272.07 kB 273.24 kB +0.50% 48.91 kB 49.15 kB
facebook-www/ReactART-prod.modern.js +0.42% 267.58 kB 268.71 kB +0.51% 47.60 kB 47.85 kB
facebook-www/ReactART-prod.classic.js +0.42% 275.24 kB 276.40 kB +0.50% 48.88 kB 49.13 kB
react-native/implementations/ReactFabric-prod.fb.js +0.42% 279.62 kB 280.79 kB +0.54% 50.30 kB 50.58 kB
react-native/implementations/ReactNativeRenderer-prod.js +0.42% 280.70 kB 281.87 kB +0.48% 50.56 kB 50.81 kB
react-native/implementations/ReactNativeRenderer-prod.fb.js +0.41% 283.53 kB 284.70 kB +0.49% 51.24 kB 51.49 kB
react-native/implementations/ReactFabric-profiling.js +0.40% 291.61 kB 292.78 kB +0.48% 52.27 kB 52.52 kB
react-native/implementations/ReactNativeRenderer-profiling.js +0.39% 300.28 kB 301.45 kB +0.44% 53.91 kB 54.15 kB
react-native/implementations/ReactFabric-profiling.fb.js +0.38% 308.05 kB 309.23 kB +0.44% 55.45 kB 55.70 kB
react-native/implementations/ReactNativeRenderer-profiling.fb.js +0.38% 311.86 kB 313.03 kB +0.45% 56.29 kB 56.54 kB
oss-stable-semver/react-art/umd/react-art.production.min.js +0.34% 117.59 kB 117.99 kB +0.55% 36.44 kB 36.64 kB
oss-stable/react-art/umd/react-art.production.min.js +0.34% 117.59 kB 117.99 kB +0.55% 36.44 kB 36.64 kB
oss-experimental/react-art/umd/react-art.production.min.js +0.33% 122.08 kB 122.48 kB +0.48% 37.74 kB 37.92 kB

Generated by 🚫 dangerJS against 371302f

@acdlite acdlite force-pushed the useid branch 6 times, most recently from 75a0cc2 to 78df833 Compare October 30, 2021 02:08
Comment on lines +286 to +291
const divs = container.querySelectorAll('div');

// Confirm that every id matches the expected pattern
for (let i = 0; i < divs.length; i++) {
// Example: R:clalalalalalalala...
expect(divs[i].id).toMatch(/^R:.(((al)*a?)((la)*l?))*$/);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For small trees collision is implicitly tested with a snapshot. But testing for collision is probably more interesting for large trees anyway. Does this make sense?

Suggested change
const divs = container.querySelectorAll('div');
// Confirm that every id matches the expected pattern
for (let i = 0; i < divs.length; i++) {
// Example: R:clalalalalalalala...
expect(divs[i].id).toMatch(/^R:.(((al)*a?)((la)*l?))*$/);
const divs = container.querySelectorAll('div');
const ids = new Set();
// Confirm that every id matches the expected pattern and is unique
for (let i = 0; i < divs.length; i++) {
// Example: R:clalalalalalalala...
expect(divs[i].id).toMatch(/^R:.(((al)*a?)((la)*l?))*$/);
expect(ids).not.toContain(divs[i].id);
ids.add(divs[i].id);

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this particular test it’s probably not that interesting because the structure of the tree follows such a rigid pattern. I’m planning to do this with a fuzz tester like we do for other features like context and Suspense.

@@ -656,6 +656,33 @@ describe('ReactHooksInspectionIntegration', () => {
});
});

it('should support useOpaqueIdentifier hook', () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: title

// <B />
// </>
//
// However, we cannot skip any materializes an id. Otherwise, a parent id that
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: grammar

it('should support useId hook', () => {
function Foo(props) {
const id = React.unstable_useId();
const [state] = React.useState(() => 'hello', []);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does React.useState support a second argument? What does this stand for?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No this was just copy pasta haha

Ids are base 32 strings whose binary representation corresponds to the
position of a node in a tree.

Every time the tree forks into multiple children, we add additional bits
to the left of the sequence that represent the position of the child
within the current level of children.

    00101       00010001011010101
    ╰─┬─╯       ╰───────┬───────╯
  Fork 5 of 20       Parent id

The leading 0s are important. In the above example, you only need 3 bits
to represent slot 5. However, you need 5 bits to represent all the forks
at the current level, so we must account for the empty bits at the end.

For this same reason, slots are 1-indexed instead of 0-indexed.
Otherwise, the zeroth id at a level would be indistinguishable from
its parent.

If a node has only one child, and does not materialize an id (i.e. does
not contain a useId hook), then we don't need to allocate any space in
the sequence. It's treated as a transparent indirection. For example,
these two trees produce the same ids:

<>                          <>
  <Indirection>               <A />
    <A />                     <B />
  </Indirection>            </>
  <B />
</>

However, we cannot skip any materializes an id. Otherwise, a parent id
that does not fork would be indistinguishable from its child id. For
example, this tree does not fork, but the parent and child must have
different ids.

<Parent>
  <Child />
</Parent>

To handle this scenario, every time we materialize an id, we allocate a
new level with a single slot. You can think of this as a fork with only
one prong, or an array of children with length 1.

It's possible for the the size of the sequence to exceed 32 bits, the
max size for bitwise operations. When this happens, we make more room by
converting the right part of the id to a string and storing it in an
overflow variable. We use a base 32 string representation, because 32 is
the largest power of 2 that is supported by toString(). We want the base
to be large so that the resulting ids are compact, and we want the base
to be a power of 2 because every log2(base) bits corresponds to a single
character, i.e. every log2(32) = 5 bits. That means we can lop bits off
the end 5 at a time without affecting the final result.
Stores the tree context on the dehydrated Suspense boundary's state
object so it resume where it left off.
Copy link
Contributor

@salazarm salazarm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome

'B',
]);
// The insertions should not cause a mismatch.
expect(container).toMatchInlineSnapshot(`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also test deletions? I'm guessing we won't match if a divWithID is missing on the client? Maybe we should encode it in the html?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I can add a test for deleting a sibling. Or do you mean deleting the Suspense boundary before it hydrates?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed out-of-band, added more tests

Demonstrates that selective hydration works and ids are preserved even
after subsequent client updates.
This was referenced Jul 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed React Core Team Opened by a member of the React Core Team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

9 participants