From 5d08a24c21dcb3d42ea628ce3f94bdbd0d432756 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Fri, 25 Feb 2022 15:46:59 -0500 Subject: [PATCH] useId: Use 'H' to separate main id from hook index (#23363) No id should be a subset of any other id. Currently, this is not true when there are multiple hooks in the same component. We append the hook index to the end of the id, except for the first one. So you get this pattern. Before this change: - 1st hook's id: :R0: - 2nd hook's id: :R0:1: The first hook's id is a subset of all the other ids in the same component. The fix for this is to use a different character to separate the main id from the hook index. I've chosen a captial 'H' for this because capital letters are not part of the base 32 character set when encoding with `toString(32)`. After this change: - 1st hook's id: :R0: - 2nd hook's id: :R0H1: --- packages/react-dom/src/__tests__/ReactDOMUseId-test.js | 4 ++-- packages/react-dom/src/server/ReactDOMServerFormatConfig.js | 6 +++--- packages/react-reconciler/src/ReactFiberHooks.new.js | 6 ++++-- packages/react-reconciler/src/ReactFiberHooks.old.js | 6 ++++-- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/packages/react-dom/src/__tests__/ReactDOMUseId-test.js b/packages/react-dom/src/__tests__/ReactDOMUseId-test.js index 41c4ddcbcc489..d3fb8b3ae70a6 100644 --- a/packages/react-dom/src/__tests__/ReactDOMUseId-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMUseId-test.js @@ -93,7 +93,7 @@ describe('useId', () => { } function normalizeTreeIdForTesting(id) { - const result = id.match(/:(R|r)(.*):(([0-9]*):)?/); + const result = id.match(/:(R|r)([a-z0-9]*)(H([0-9]*))?:/); if (result === undefined) { throw new Error('Invalid id format'); } @@ -342,7 +342,7 @@ describe('useId', () => {
- :R0:, :R0:1:, :R0:2: + :R0:, :R0H1:, :R0H2:
`); diff --git a/packages/react-dom/src/server/ReactDOMServerFormatConfig.js b/packages/react-dom/src/server/ReactDOMServerFormatConfig.js index 0b87521c185c2..7cc5c72a16e5f 100644 --- a/packages/react-dom/src/server/ReactDOMServerFormatConfig.js +++ b/packages/react-dom/src/server/ReactDOMServerFormatConfig.js @@ -242,16 +242,16 @@ export function makeId( ): string { const idPrefix = responseState.idPrefix; - let id = ':' + idPrefix + 'R' + treeId + ':'; + let id = ':' + idPrefix + 'R' + treeId; // Unless this is the first id at this level, append a number at the end // that represents the position of this useId hook among all the useId // hooks for this fiber. if (localId > 0) { - id += localId.toString(32) + ':'; + id += 'H' + localId.toString(32); } - return id; + return id + ':'; } function encodeHTMLTextNode(text: string): string { diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js index 5c4b9357f909e..011c64f59d2c8 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.new.js +++ b/packages/react-reconciler/src/ReactFiberHooks.new.js @@ -2072,15 +2072,17 @@ function mountId(): string { const treeId = getTreeId(); // Use a captial R prefix for server-generated ids. - id = ':' + identifierPrefix + 'R' + treeId + ':'; + id = ':' + identifierPrefix + 'R' + treeId; // Unless this is the first id at this level, append a number at the end // that represents the position of this useId hook among all the useId // hooks for this fiber. const localId = localIdCounter++; if (localId > 0) { - id += localId.toString(32) + ':'; + id += 'H' + localId.toString(32); } + + id += ':'; } else { // Use a lowercase r prefix for client-generated ids. const globalClientId = globalClientIdCounter++; diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js index f0fcef733d581..48f8f14bf2e2a 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.old.js +++ b/packages/react-reconciler/src/ReactFiberHooks.old.js @@ -2072,15 +2072,17 @@ function mountId(): string { const treeId = getTreeId(); // Use a captial R prefix for server-generated ids. - id = ':' + identifierPrefix + 'R' + treeId + ':'; + id = ':' + identifierPrefix + 'R' + treeId; // Unless this is the first id at this level, append a number at the end // that represents the position of this useId hook among all the useId // hooks for this fiber. const localId = localIdCounter++; if (localId > 0) { - id += localId.toString(32) + ':'; + id += 'H' + localId.toString(32); } + + id += ':'; } else { // Use a lowercase r prefix for client-generated ids. const globalClientId = globalClientIdCounter++;