useId: Remove unnecessary try/finally blocks (#27340)

To generate IDs for useId, we modify a context variable whenever
multiple siblings are rendered, or when a component includes a useId
hook.

When this happens, we must ensure that the context is reset properly on
unwind if something errors or suspends. When I originally implemented
this, I did this by wrapping the child's rendering with a try/finally
block. But a better way to do this is by using the non-destructive
renderNode path instead of renderNodeDestructive.
This commit is contained in:
Andrew Clark
2023-09-06 16:30:29 -04:00
committed by GitHub
parent b9be4537c2
commit ee7f9c9351

View File

@@ -1021,12 +1021,13 @@ function renderIndeterminateComponent(
const prevTreeContext = task.treeContext;
const totalChildren = 1;
const index = 0;
// Modify the id context. Because we'll need to reset this if something
// suspends or errors, we'll use the non-destructive render path.
task.treeContext = pushTreeContext(prevTreeContext, totalChildren, index);
try {
renderNodeDestructive(request, task, null, value, 0);
} finally {
task.treeContext = prevTreeContext;
}
renderNode(request, task, value, 0);
// Like the other contexts, this does not need to be in a finally block
// because renderNode takes care of unwinding the stack.
task.treeContext = prevTreeContext;
} else {
renderNodeDestructive(request, task, null, value, 0);
}
@@ -1126,12 +1127,12 @@ function renderForwardRef(
const prevTreeContext = task.treeContext;
const totalChildren = 1;
const index = 0;
// Modify the id context. Because we'll need to reset this if something
// suspends or errors, we'll use the non-destructive render path.
task.treeContext = pushTreeContext(prevTreeContext, totalChildren, index);
try {
renderNodeDestructive(request, task, null, children, 0);
} finally {
task.treeContext = prevTreeContext;
}
renderNode(request, task, children, 0);
// Like the other contexts, this does not need to be in a finally block
// because renderNode takes care of unwinding the stack.
} else {
renderNodeDestructive(request, task, null, children, 0);
}
@@ -1656,26 +1657,27 @@ function renderChildrenArray(
children: Array<any>,
childIndex: number,
) {
const prevKeyPath = task.keyPath;
const prevTreeContext = task.treeContext;
const totalChildren = children.length;
for (let i = 0; i < totalChildren; i++) {
const prevTreeContext = task.treeContext;
const node = children[i];
task.treeContext = pushTreeContext(prevTreeContext, totalChildren, i);
try {
const node = children[i];
if (isArray(node) || getIteratorFn(node)) {
// Nested arrays behave like a "fragment node" which is keyed.
// Therefore we need to add the current index as a parent key.
task.keyPath = [task.keyPath, '', childIndex];
}
if (isArray(node) || getIteratorFn(node)) {
// Nested arrays behave like a "fragment node" which is keyed.
// Therefore we need to add the current index as a parent key.
const prevKeyPath = task.keyPath;
task.keyPath = [task.keyPath, '', childIndex];
renderNode(request, task, node, i);
task.keyPath = prevKeyPath;
} else {
// We need to use the non-destructive form so that we can safely pop back
// up and render the sibling if something suspends.
renderNode(request, task, node, i);
} finally {
task.treeContext = prevTreeContext;
task.keyPath = prevKeyPath;
}
}
// Because this context is always set right before rendering every child, we
// only need to reset it to the previous value at the very end.
task.treeContext = prevTreeContext;
}
function trackPostpone(
@@ -1824,6 +1826,7 @@ function renderNode(
const previousLegacyContext = task.legacyContext;
const previousContext = task.context;
const previousKeyPath = task.keyPath;
const previousTreeContext = task.treeContext;
let previousComponentStack = null;
if (__DEV__) {
previousComponentStack = task.componentStack;
@@ -1860,6 +1863,7 @@ function renderNode(
task.legacyContext = previousLegacyContext;
task.context = previousContext;
task.keyPath = previousKeyPath;
task.treeContext = previousTreeContext;
// Restore all active ReactContexts to what they were before.
switchContext(previousContext);
if (__DEV__) {
@@ -1892,6 +1896,7 @@ function renderNode(
task.legacyContext = previousLegacyContext;
task.context = previousContext;
task.keyPath = previousKeyPath;
task.treeContext = previousTreeContext;
// Restore all active ReactContexts to what they were before.
switchContext(previousContext);
if (__DEV__) {
@@ -1906,6 +1911,7 @@ function renderNode(
task.legacyContext = previousLegacyContext;
task.context = previousContext;
task.keyPath = previousKeyPath;
task.treeContext = previousTreeContext;
// Restore all active ReactContexts to what they were before.
switchContext(previousContext);
if (__DEV__) {