@samitouri / QOS-React / commits / f9e6aef828

Wrap try-catch directly around each user function

This moves the try-catch from around each fiber's mutation phase to direclty around each user function (effect function, callback, etc). We already do this when unmounting because if one unmount function errors, we still need to call all the others so they can clean up their resources. Previously we didn't bother to do this for anything but unmount, because if a mount effect throws, we're going to delete that whole tree anyway. But now that we're switching from an iterative loop to a recursive one, we don't want every call frame on the stack to have a try-catch, since the error handling requires additional memory. Wrapping every user function is a bit tedious, but it's better for performance. Many of them already had try blocks around them already.

Andrew Clark committed Apr 8, 2022 at 00:34 UTC f9e6aef82880615d7d11fb9facf9edfd8c80dcf6
2 files changed +208 -96
packages/react-reconciler/src/ReactFiberCommitWork.new.js
+104 -48
@@ -1077,21 +1077,28 @@ function hideOrUnhideAllChildren(finishedWork, isHidden) {
1077 if (node.tag === HostComponent) {
1078 if (hostSubtreeRoot === null) {
1079 hostSubtreeRoot = node;
1080 -
1081 - const instance = node.stateNode;
1082 - if (isHidden) {
1083 - hideInstance(instance);
1084 - } else {
1085 - unhideInstance(node.stateNode, node.memoizedProps);
1080 + try {
1081 + const instance = node.stateNode;
1082 + if (isHidden) {
1083 + hideInstance(instance);
1084 + } else {
1085 + unhideInstance(node.stateNode, node.memoizedProps);
1086 + }
1087 + } catch (error) {
1088 + captureCommitPhaseError(finishedWork, finishedWork.return, error);
1089 }
1090 }
1091 } else if (node.tag === HostText) {
1092 if (hostSubtreeRoot === null) {
1090 - const instance = node.stateNode;
1091 - if (isHidden) {
1092 - hideTextInstance(instance);
1093 - } else {
1094 - unhideTextInstance(instance, node.memoizedProps);
1093 + try {
1094 + const instance = node.stateNode;
1095 + if (isHidden) {
1096 + hideTextInstance(instance);
1097 + } else {
1098 + unhideTextInstance(instance, node.memoizedProps);
1099 + }
1100 + } catch (error) {
1101 + captureCommitPhaseError(finishedWork, finishedWork.return, error);
1102 }
1103 }
1104 } else if (
@@ -1938,11 +1945,7 @@ function commitMutationEffects_complete(root: FiberRoot, lanes: Lanes) {
1945 while (nextEffect !== null) {
1946 const fiber = nextEffect;
1947 setCurrentDebugFiberInDEV(fiber);
1941 - try {
1942 - commitMutationEffectsOnFiber(fiber, root, lanes);
1943 - } catch (error) {
1944 - captureCommitPhaseError(fiber, fiber.return, error);
1945 - }
1948 + commitMutationEffectsOnFiber(fiber, root, lanes);
1949 resetCurrentDebugFiberInDEV();
1950
1951 const sibling = fiber.sibling;
@@ -1975,12 +1978,19 @@ function commitMutationEffectsOnFiber(
1978 commitReconciliationEffects(finishedWork);
1979
1980 if (flags & Update) {
1978 - commitHookEffectListUnmount(
1979 - HookInsertion | HookHasEffect,
1980 - finishedWork,
1981 - finishedWork.return,
1982 - );
1983 - commitHookEffectListMount(HookInsertion | HookHasEffect, finishedWork);
1981 + try {
1982 + commitHookEffectListUnmount(
1983 + HookInsertion | HookHasEffect,
1984 + finishedWork,
1985 + finishedWork.return,
1986 + );
1987 + commitHookEffectListMount(
1988 + HookInsertion | HookHasEffect,
1989 + finishedWork,
1990 + );
1991 + } catch (error) {
1992 + captureCommitPhaseError(finishedWork, finishedWork.return, error);
1993 + }
1994 // Layout effects are destroyed during the mutation phase so that all
1995 // destroy functions for all fibers are called before any create functions.
1996 // This prevents sibling component effects from interfering with each other,
@@ -1998,15 +2008,20 @@ function commitMutationEffectsOnFiber(
2008 finishedWork,
2009 finishedWork.return,
2010 );
2001 - } finally {
2002 - recordLayoutEffectDuration(finishedWork);
2011 + } catch (error) {
2012 + captureCommitPhaseError(finishedWork, finishedWork.return, error);
2013 }
2014 + recordLayoutEffectDuration(finishedWork);
2015 } else {
2005 - commitHookEffectListUnmount(
2006 - HookLayout | HookHasEffect,
2007 - finishedWork,
2008 - finishedWork.return,
2009 - );
2016 + try {
2017 + commitHookEffectListUnmount(
2018 + HookLayout | HookHasEffect,
2019 + finishedWork,
2020 + finishedWork.return,
2021 + );
2022 + } catch (error) {
2023 + captureCommitPhaseError(finishedWork, finishedWork.return, error);
2024 + }
2025 }
2026 }
2027 return;
@@ -2016,7 +2031,7 @@ function commitMutationEffectsOnFiber(
2031
2032 if (flags & Ref) {
2033 if (current !== null) {
2019 - commitDetachRef(current);
2034 + safelyDetachRef(current, current.return);
2035 }
2036 }
2037 return;
@@ -2026,13 +2041,17 @@ function commitMutationEffectsOnFiber(
2041
2042 if (flags & Ref) {
2043 if (current !== null) {
2029 - commitDetachRef(current);
2044 + safelyDetachRef(current, current.return);
2045 }
2046 }
2047 if (supportsMutation) {
2048 if (flags & ContentReset) {
2049 const instance: Instance = finishedWork.stateNode;
2035 - resetTextContent(instance);
2050 + try {
2051 + resetTextContent(instance);
2052 + } catch (error) {
2053 + captureCommitPhaseError(finishedWork, finishedWork.return, error);
2054 + }
2055 }
2056
2057 if (flags & Update) {
@@ -2050,14 +2069,22 @@ function commitMutationEffectsOnFiber(
2069 const updatePayload: null | UpdatePayload = (finishedWork.updateQueue: any);
2070 finishedWork.updateQueue = null;
2071 if (updatePayload !== null) {
2053 - commitUpdate(
2054 - instance,
2055 - updatePayload,
2056 - type,
2057 - oldProps,
2058 - newProps,
2059 - finishedWork,
2060 - );
2072 + try {
2073 + commitUpdate(
2074 + instance,
2075 + updatePayload,
2076 + type,
2077 + oldProps,
2078 + newProps,
2079 + finishedWork,
2080 + );
2081 + } catch (error) {
2082 + captureCommitPhaseError(
2083 + finishedWork,
2084 + finishedWork.return,
2085 + error,
2086 + );
2087 + }
2088 }
2089 }
2090 }
@@ -2083,7 +2110,12 @@ function commitMutationEffectsOnFiber(
2110 // this case.
2111 const oldText: string =
2112 current !== null ? current.memoizedProps : newText;
2086 - commitTextUpdate(textInstance, oldText, newText);
2113 +
2114 + try {
2115 + commitTextUpdate(textInstance, oldText, newText);
2116 + } catch (error) {
2117 + captureCommitPhaseError(finishedWork, finishedWork.return, error);
2118 + }
2119 }
2120 }
2121 return;
@@ -2096,14 +2128,26 @@ function commitMutationEffectsOnFiber(
2128 if (current !== null) {
2129 const prevRootState: RootState = current.memoizedState;
2130 if (prevRootState.isDehydrated) {
2099 - commitHydratedContainer(root.containerInfo);
2131 + try {
2132 + commitHydratedContainer(root.containerInfo);
2133 + } catch (error) {
2134 + captureCommitPhaseError(
2135 + finishedWork,
2136 + finishedWork.return,
2137 + error,
2138 + );
2139 + }
2140 }
2141 }
2142 }
2143 if (supportsPersistence) {
2144 const containerInfo = root.containerInfo;
2145 const pendingChildren = root.pendingChildren;
2106 - replaceContainerChildren(containerInfo, pendingChildren);
2146 + try {
2147 + replaceContainerChildren(containerInfo, pendingChildren);
2148 + } catch (error) {
2149 + captureCommitPhaseError(finishedWork, finishedWork.return, error);
2150 + }
2151 }
2152 }
2153 return;
@@ -2116,7 +2160,11 @@ function commitMutationEffectsOnFiber(
2160 const portal = finishedWork.stateNode;
2161 const containerInfo = portal.containerInfo;
2162 const pendingChildren = portal.pendingChildren;
2119 - replaceContainerChildren(containerInfo, pendingChildren);
2163 + try {
2164 + replaceContainerChildren(containerInfo, pendingChildren);
2165 + } catch (error) {
2166 + captureCommitPhaseError(finishedWork, finishedWork.return, error);
2167 + }
2168 }
2169 }
2170 return;
@@ -2136,7 +2184,11 @@ function commitMutationEffectsOnFiber(
2184 }
2185 }
2186 if (flags & Update) {
2139 - commitSuspenseCallback(finishedWork);
2187 + try {
2188 + commitSuspenseCallback(finishedWork);
2189 + } catch (error) {
2190 + captureCommitPhaseError(finishedWork, finishedWork.return, error);
2191 + }
2192 attachSuspenseRetryListeners(finishedWork);
2193 }
2194 return;
@@ -2194,9 +2246,9 @@ function commitMutationEffectsOnFiber(
2246 // from React Flare on www.
2247 if (flags & Ref) {
2248 if (current !== null) {
2197 - commitDetachRef(current);
2249 + safelyDetachRef(finishedWork, finishedWork.return);
2250 }
2199 - commitAttachRef(finishedWork);
2251 + safelyAttachRef(finishedWork, finishedWork.return);
2252 }
2253 if (flags & Update) {
2254 const scopeInstance = finishedWork.stateNode;
@@ -2217,7 +2269,11 @@ function commitReconciliationEffects(finishedWork: Fiber) {
2269 // before the effects on this fiber have fired.
2270 const flags = finishedWork.flags;
2271 if (flags & Placement) {
2220 - commitPlacement(finishedWork);
2272 + try {
2273 + commitPlacement(finishedWork);
2274 + } catch (error) {
2275 + captureCommitPhaseError(finishedWork, finishedWork.return, error);
2276 + }
2277 // Clear the "placement" from effect tag so that we know that this is
2278 // inserted, before any life-cycles like componentDidMount gets called.
2279 // TODO: findDOMNode doesn't rely on this any more but isMounted does
packages/react-reconciler/src/ReactFiberCommitWork.old.js
+104 -48
@@ -1077,21 +1077,28 @@ function hideOrUnhideAllChildren(finishedWork, isHidden) {
1077 if (node.tag === HostComponent) {
1078 if (hostSubtreeRoot === null) {
1079 hostSubtreeRoot = node;
1080 -
1081 - const instance = node.stateNode;
1082 - if (isHidden) {
1083 - hideInstance(instance);
1084 - } else {
1085 - unhideInstance(node.stateNode, node.memoizedProps);
1080 + try {
1081 + const instance = node.stateNode;
1082 + if (isHidden) {
1083 + hideInstance(instance);
1084 + } else {
1085 + unhideInstance(node.stateNode, node.memoizedProps);
1086 + }
1087 + } catch (error) {
1088 + captureCommitPhaseError(finishedWork, finishedWork.return, error);
1089 }
1090 }
1091 } else if (node.tag === HostText) {
1092 if (hostSubtreeRoot === null) {
1090 - const instance = node.stateNode;
1091 - if (isHidden) {
1092 - hideTextInstance(instance);
1093 - } else {
1094 - unhideTextInstance(instance, node.memoizedProps);
1093 + try {
1094 + const instance = node.stateNode;
1095 + if (isHidden) {
1096 + hideTextInstance(instance);
1097 + } else {
1098 + unhideTextInstance(instance, node.memoizedProps);
1099 + }
1100 + } catch (error) {
1101 + captureCommitPhaseError(finishedWork, finishedWork.return, error);
1102 }
1103 }
1104 } else if (
@@ -1938,11 +1945,7 @@ function commitMutationEffects_complete(root: FiberRoot, lanes: Lanes) {
1945 while (nextEffect !== null) {
1946 const fiber = nextEffect;
1947 setCurrentDebugFiberInDEV(fiber);
1941 - try {
1942 - commitMutationEffectsOnFiber(fiber, root, lanes);
1943 - } catch (error) {
1944 - captureCommitPhaseError(fiber, fiber.return, error);
1945 - }
1948 + commitMutationEffectsOnFiber(fiber, root, lanes);
1949 resetCurrentDebugFiberInDEV();
1950
1951 const sibling = fiber.sibling;
@@ -1975,12 +1978,19 @@ function commitMutationEffectsOnFiber(
1978 commitReconciliationEffects(finishedWork);
1979
1980 if (flags & Update) {
1978 - commitHookEffectListUnmount(
1979 - HookInsertion | HookHasEffect,
1980 - finishedWork,
1981 - finishedWork.return,
1982 - );
1983 - commitHookEffectListMount(HookInsertion | HookHasEffect, finishedWork);
1981 + try {
1982 + commitHookEffectListUnmount(
1983 + HookInsertion | HookHasEffect,
1984 + finishedWork,
1985 + finishedWork.return,
1986 + );
1987 + commitHookEffectListMount(
1988 + HookInsertion | HookHasEffect,
1989 + finishedWork,
1990 + );
1991 + } catch (error) {
1992 + captureCommitPhaseError(finishedWork, finishedWork.return, error);
1993 + }
1994 // Layout effects are destroyed during the mutation phase so that all
1995 // destroy functions for all fibers are called before any create functions.
1996 // This prevents sibling component effects from interfering with each other,
@@ -1998,15 +2008,20 @@ function commitMutationEffectsOnFiber(
2008 finishedWork,
2009 finishedWork.return,
2010 );
2001 - } finally {
2002 - recordLayoutEffectDuration(finishedWork);
2011 + } catch (error) {
2012 + captureCommitPhaseError(finishedWork, finishedWork.return, error);
2013 }
2014 + recordLayoutEffectDuration(finishedWork);
2015 } else {
2005 - commitHookEffectListUnmount(
2006 - HookLayout | HookHasEffect,
2007 - finishedWork,
2008 - finishedWork.return,
2009 - );
2016 + try {
2017 + commitHookEffectListUnmount(
2018 + HookLayout | HookHasEffect,
2019 + finishedWork,
2020 + finishedWork.return,
2021 + );
2022 + } catch (error) {
2023 + captureCommitPhaseError(finishedWork, finishedWork.return, error);
2024 + }
2025 }
2026 }
2027 return;
@@ -2016,7 +2031,7 @@ function commitMutationEffectsOnFiber(
2031
2032 if (flags & Ref) {
2033 if (current !== null) {
2019 - commitDetachRef(current);
2034 + safelyDetachRef(current, current.return);
2035 }
2036 }
2037 return;
@@ -2026,13 +2041,17 @@ function commitMutationEffectsOnFiber(
2041
2042 if (flags & Ref) {
2043 if (current !== null) {
2029 - commitDetachRef(current);
2044 + safelyDetachRef(current, current.return);
2045 }
2046 }
2047 if (supportsMutation) {
2048 if (flags & ContentReset) {
2049 const instance: Instance = finishedWork.stateNode;
2035 - resetTextContent(instance);
2050 + try {
2051 + resetTextContent(instance);
2052 + } catch (error) {
2053 + captureCommitPhaseError(finishedWork, finishedWork.return, error);
2054 + }
2055 }
2056
2057 if (flags & Update) {
@@ -2050,14 +2069,22 @@ function commitMutationEffectsOnFiber(
2069 const updatePayload: null | UpdatePayload = (finishedWork.updateQueue: any);
2070 finishedWork.updateQueue = null;
2071 if (updatePayload !== null) {
2053 - commitUpdate(
2054 - instance,
2055 - updatePayload,
2056 - type,
2057 - oldProps,
2058 - newProps,
2059 - finishedWork,
2060 - );
2072 + try {
2073 + commitUpdate(
2074 + instance,
2075 + updatePayload,
2076 + type,
2077 + oldProps,
2078 + newProps,
2079 + finishedWork,
2080 + );
2081 + } catch (error) {
2082 + captureCommitPhaseError(
2083 + finishedWork,
2084 + finishedWork.return,
2085 + error,
2086 + );
2087 + }
2088 }
2089 }
2090 }
@@ -2083,7 +2110,12 @@ function commitMutationEffectsOnFiber(
2110 // this case.
2111 const oldText: string =
2112 current !== null ? current.memoizedProps : newText;
2086 - commitTextUpdate(textInstance, oldText, newText);
2113 +
2114 + try {
2115 + commitTextUpdate(textInstance, oldText, newText);
2116 + } catch (error) {
2117 + captureCommitPhaseError(finishedWork, finishedWork.return, error);
2118 + }
2119 }
2120 }
2121 return;
@@ -2096,14 +2128,26 @@ function commitMutationEffectsOnFiber(
2128 if (current !== null) {
2129 const prevRootState: RootState = current.memoizedState;
2130 if (prevRootState.isDehydrated) {
2099 - commitHydratedContainer(root.containerInfo);
2131 + try {
2132 + commitHydratedContainer(root.containerInfo);
2133 + } catch (error) {
2134 + captureCommitPhaseError(
2135 + finishedWork,
2136 + finishedWork.return,
2137 + error,
2138 + );
2139 + }
2140 }
2141 }
2142 }
2143 if (supportsPersistence) {
2144 const containerInfo = root.containerInfo;
2145 const pendingChildren = root.pendingChildren;
2106 - replaceContainerChildren(containerInfo, pendingChildren);
2146 + try {
2147 + replaceContainerChildren(containerInfo, pendingChildren);
2148 + } catch (error) {
2149 + captureCommitPhaseError(finishedWork, finishedWork.return, error);
2150 + }
2151 }
2152 }
2153 return;
@@ -2116,7 +2160,11 @@ function commitMutationEffectsOnFiber(
2160 const portal = finishedWork.stateNode;
2161 const containerInfo = portal.containerInfo;
2162 const pendingChildren = portal.pendingChildren;
2119 - replaceContainerChildren(containerInfo, pendingChildren);
2163 + try {
2164 + replaceContainerChildren(containerInfo, pendingChildren);
2165 + } catch (error) {
2166 + captureCommitPhaseError(finishedWork, finishedWork.return, error);
2167 + }
2168 }
2169 }
2170 return;
@@ -2136,7 +2184,11 @@ function commitMutationEffectsOnFiber(
2184 }
2185 }
2186 if (flags & Update) {
2139 - commitSuspenseCallback(finishedWork);
2187 + try {
2188 + commitSuspenseCallback(finishedWork);
2189 + } catch (error) {
2190 + captureCommitPhaseError(finishedWork, finishedWork.return, error);
2191 + }
2192 attachSuspenseRetryListeners(finishedWork);
2193 }
2194 return;
@@ -2194,9 +2246,9 @@ function commitMutationEffectsOnFiber(
2246 // from React Flare on www.
2247 if (flags & Ref) {
2248 if (current !== null) {
2197 - commitDetachRef(current);
2249 + safelyDetachRef(finishedWork, finishedWork.return);
2250 }
2199 - commitAttachRef(finishedWork);
2251 + safelyAttachRef(finishedWork, finishedWork.return);
2252 }
2253 if (flags & Update) {
2254 const scopeInstance = finishedWork.stateNode;
@@ -2217,7 +2269,11 @@ function commitReconciliationEffects(finishedWork: Fiber) {
2269 // before the effects on this fiber have fired.
2270 const flags = finishedWork.flags;
2271 if (flags & Placement) {
2220 - commitPlacement(finishedWork);
2272 + try {
2273 + commitPlacement(finishedWork);
2274 + } catch (error) {
2275 + captureCommitPhaseError(finishedWork, finishedWork.return, error);
2276 + }
2277 // Clear the "placement" from effect tag so that we know that this is
2278 // inserted, before any life-cycles like componentDidMount gets called.
2279 // TODO: findDOMNode doesn't rely on this any more but isMounted does