From 0a17699fd59f83468c0db64cc94e050c96d3c119 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 24 Jun 2019 13:49:01 -0700 Subject: [PATCH] Recover from render errors by remounting root Summary: This updates the renderer and Fresh packages to pull in the new error handling behavior. The new feature is that roots that errored on last save get remounted after an edit. This allows much faster iteration in the Fast Refresh mode as you don't need to do a full reload after typos. Reviewed By: bvaughn Differential Revision: D15967396 fbshipit-source-id: 96a82e6a4e00a8cb636d7bca037a1a43552a4cd2 --- .../implementations/ReactFabric-dev.fb.js | 17 ++++++++++++++++- .../Renderer/implementations/ReactFabric-dev.js | 17 ++++++++++++++++- .../implementations/ReactFabric-prod.fb.js | 8 +++++++- .../implementations/ReactFabric-prod.js | 8 +++++++- .../implementations/ReactFabric-profiling.fb.js | 8 +++++++- .../implementations/ReactFabric-profiling.js | 8 +++++++- .../ReactNativeRenderer-dev.fb.js | 17 ++++++++++++++++- .../implementations/ReactNativeRenderer-dev.js | 17 ++++++++++++++++- .../ReactNativeRenderer-prod.fb.js | 8 +++++++- .../implementations/ReactNativeRenderer-prod.js | 8 +++++++- .../ReactNativeRenderer-profiling.fb.js | 8 +++++++- .../ReactNativeRenderer-profiling.js | 8 +++++++- package.json | 2 +- yarn.lock | 8 ++++---- 14 files changed, 125 insertions(+), 17 deletions(-) diff --git a/Libraries/Renderer/implementations/ReactFabric-dev.fb.js b/Libraries/Renderer/implementations/ReactFabric-dev.fb.js index eda5738ed0..559d7b606a 100644 --- a/Libraries/Renderer/implementations/ReactFabric-dev.fb.js +++ b/Libraries/Renderer/implementations/ReactFabric-dev.fb.js @@ -5074,7 +5074,8 @@ function injectInternals(internals) { var rendererID = hook.inject(internals); // We have successfully injected, so now it is safe to set up hooks. onCommitFiberRoot = catchErrors(function(root) { - return hook.onCommitFiberRoot(rendererID, root); + var didError = (root.current.effectTag & DidCapture) === DidCapture; + hook.onCommitFiberRoot(rendererID, root, undefined, didError); }); onCommitFiberUnmount = catchErrors(function(fiber) { return hook.onCommitFiberUnmount(rendererID, fiber); @@ -18287,6 +18288,19 @@ var scheduleRefresh = function(root, update) { } }; +var scheduleRoot = function(root, element) { + { + if (root.context !== emptyContextObject) { + // Super edge case: root has a legacy _renderSubtree context + // but we don't know the parentComponent so we can't pass it. + // Just ignore. We'll delete this with _renderSubtree code path later. + return; + } + flushPassiveEffects(); + updateContainerAtExpirationTime(element, root, null, Sync, null); + } +}; + function scheduleFibersWithFamiliesRecursively( fiber, updatedFamilies, @@ -19469,6 +19483,7 @@ function injectIntoDevTools(devToolsConfig) { // React Refresh findHostInstancesForRefresh: findHostInstancesForRefresh, scheduleRefresh: scheduleRefresh, + scheduleRoot: scheduleRoot, setRefreshHandler: setRefreshHandler }) ); diff --git a/Libraries/Renderer/implementations/ReactFabric-dev.js b/Libraries/Renderer/implementations/ReactFabric-dev.js index c60e838a39..22db260994 100644 --- a/Libraries/Renderer/implementations/ReactFabric-dev.js +++ b/Libraries/Renderer/implementations/ReactFabric-dev.js @@ -5070,7 +5070,8 @@ function injectInternals(internals) { var rendererID = hook.inject(internals); // We have successfully injected, so now it is safe to set up hooks. onCommitFiberRoot = catchErrors(function(root) { - return hook.onCommitFiberRoot(rendererID, root); + var didError = (root.current.effectTag & DidCapture) === DidCapture; + hook.onCommitFiberRoot(rendererID, root, undefined, didError); }); onCommitFiberUnmount = catchErrors(function(fiber) { return hook.onCommitFiberUnmount(rendererID, fiber); @@ -18283,6 +18284,19 @@ var scheduleRefresh = function(root, update) { } }; +var scheduleRoot = function(root, element) { + { + if (root.context !== emptyContextObject) { + // Super edge case: root has a legacy _renderSubtree context + // but we don't know the parentComponent so we can't pass it. + // Just ignore. We'll delete this with _renderSubtree code path later. + return; + } + flushPassiveEffects(); + updateContainerAtExpirationTime(element, root, null, Sync, null); + } +}; + function scheduleFibersWithFamiliesRecursively( fiber, updatedFamilies, @@ -19465,6 +19479,7 @@ function injectIntoDevTools(devToolsConfig) { // React Refresh findHostInstancesForRefresh: findHostInstancesForRefresh, scheduleRefresh: scheduleRefresh, + scheduleRoot: scheduleRoot, setRefreshHandler: setRefreshHandler }) ); diff --git a/Libraries/Renderer/implementations/ReactFabric-prod.fb.js b/Libraries/Renderer/implementations/ReactFabric-prod.fb.js index ba0c1332f7..5337dbf521 100644 --- a/Libraries/Renderer/implementations/ReactFabric-prod.fb.js +++ b/Libraries/Renderer/implementations/ReactFabric-prod.fb.js @@ -1736,7 +1736,12 @@ function injectInternals(internals) { try { var rendererID = hook.inject(internals); onCommitFiberRoot = catchErrors(function(root) { - return hook.onCommitFiberRoot(rendererID, root); + hook.onCommitFiberRoot( + rendererID, + root, + void 0, + 64 === (root.current.effectTag & 64) + ); }); onCommitFiberUnmount = catchErrors(function(fiber) { return hook.onCommitFiberUnmount(rendererID, fiber); @@ -6960,6 +6965,7 @@ var roots = new Map(), }, findHostInstancesForRefresh: null, scheduleRefresh: null, + scheduleRoot: null, setRefreshHandler: null }) ); diff --git a/Libraries/Renderer/implementations/ReactFabric-prod.js b/Libraries/Renderer/implementations/ReactFabric-prod.js index 88c21b4008..7c6c7709b9 100644 --- a/Libraries/Renderer/implementations/ReactFabric-prod.js +++ b/Libraries/Renderer/implementations/ReactFabric-prod.js @@ -1736,7 +1736,12 @@ function injectInternals(internals) { try { var rendererID = hook.inject(internals); onCommitFiberRoot = catchErrors(function(root) { - return hook.onCommitFiberRoot(rendererID, root); + hook.onCommitFiberRoot( + rendererID, + root, + void 0, + 64 === (root.current.effectTag & 64) + ); }); onCommitFiberUnmount = catchErrors(function(fiber) { return hook.onCommitFiberUnmount(rendererID, fiber); @@ -6960,6 +6965,7 @@ var roots = new Map(), }, findHostInstancesForRefresh: null, scheduleRefresh: null, + scheduleRoot: null, setRefreshHandler: null }) ); diff --git a/Libraries/Renderer/implementations/ReactFabric-profiling.fb.js b/Libraries/Renderer/implementations/ReactFabric-profiling.fb.js index 9cc78f0788..4825c76378 100644 --- a/Libraries/Renderer/implementations/ReactFabric-profiling.fb.js +++ b/Libraries/Renderer/implementations/ReactFabric-profiling.fb.js @@ -1738,7 +1738,12 @@ function injectInternals(internals) { try { var rendererID = hook.inject(internals); onCommitFiberRoot = catchErrors(function(root) { - return hook.onCommitFiberRoot(rendererID, root); + hook.onCommitFiberRoot( + rendererID, + root, + void 0, + 64 === (root.current.effectTag & 64) + ); }); onCommitFiberUnmount = catchErrors(function(fiber) { return hook.onCommitFiberUnmount(rendererID, fiber); @@ -7197,6 +7202,7 @@ var roots = new Map(), }, findHostInstancesForRefresh: null, scheduleRefresh: null, + scheduleRoot: null, setRefreshHandler: null }) ); diff --git a/Libraries/Renderer/implementations/ReactFabric-profiling.js b/Libraries/Renderer/implementations/ReactFabric-profiling.js index ee4825b1d2..78586f0f85 100644 --- a/Libraries/Renderer/implementations/ReactFabric-profiling.js +++ b/Libraries/Renderer/implementations/ReactFabric-profiling.js @@ -1738,7 +1738,12 @@ function injectInternals(internals) { try { var rendererID = hook.inject(internals); onCommitFiberRoot = catchErrors(function(root) { - return hook.onCommitFiberRoot(rendererID, root); + hook.onCommitFiberRoot( + rendererID, + root, + void 0, + 64 === (root.current.effectTag & 64) + ); }); onCommitFiberUnmount = catchErrors(function(fiber) { return hook.onCommitFiberUnmount(rendererID, fiber); @@ -7197,6 +7202,7 @@ var roots = new Map(), }, findHostInstancesForRefresh: null, scheduleRefresh: null, + scheduleRoot: null, setRefreshHandler: null }) ); diff --git a/Libraries/Renderer/implementations/ReactNativeRenderer-dev.fb.js b/Libraries/Renderer/implementations/ReactNativeRenderer-dev.fb.js index 458e7f3068..7de0e18570 100644 --- a/Libraries/Renderer/implementations/ReactNativeRenderer-dev.fb.js +++ b/Libraries/Renderer/implementations/ReactNativeRenderer-dev.fb.js @@ -5389,7 +5389,8 @@ function injectInternals(internals) { var rendererID = hook.inject(internals); // We have successfully injected, so now it is safe to set up hooks. onCommitFiberRoot = catchErrors(function(root) { - return hook.onCommitFiberRoot(rendererID, root); + var didError = (root.current.effectTag & DidCapture) === DidCapture; + hook.onCommitFiberRoot(rendererID, root, undefined, didError); }); onCommitFiberUnmount = catchErrors(function(fiber) { return hook.onCommitFiberUnmount(rendererID, fiber); @@ -18601,6 +18602,19 @@ var scheduleRefresh = function(root, update) { } }; +var scheduleRoot = function(root, element) { + { + if (root.context !== emptyContextObject) { + // Super edge case: root has a legacy _renderSubtree context + // but we don't know the parentComponent so we can't pass it. + // Just ignore. We'll delete this with _renderSubtree code path later. + return; + } + flushPassiveEffects(); + updateContainerAtExpirationTime(element, root, null, Sync, null); + } +}; + function scheduleFibersWithFamiliesRecursively( fiber, updatedFamilies, @@ -19783,6 +19797,7 @@ function injectIntoDevTools(devToolsConfig) { // React Refresh findHostInstancesForRefresh: findHostInstancesForRefresh, scheduleRefresh: scheduleRefresh, + scheduleRoot: scheduleRoot, setRefreshHandler: setRefreshHandler }) ); diff --git a/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js b/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js index 1e25bdc09a..e79d69f0bf 100644 --- a/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js +++ b/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js @@ -5385,7 +5385,8 @@ function injectInternals(internals) { var rendererID = hook.inject(internals); // We have successfully injected, so now it is safe to set up hooks. onCommitFiberRoot = catchErrors(function(root) { - return hook.onCommitFiberRoot(rendererID, root); + var didError = (root.current.effectTag & DidCapture) === DidCapture; + hook.onCommitFiberRoot(rendererID, root, undefined, didError); }); onCommitFiberUnmount = catchErrors(function(fiber) { return hook.onCommitFiberUnmount(rendererID, fiber); @@ -18597,6 +18598,19 @@ var scheduleRefresh = function(root, update) { } }; +var scheduleRoot = function(root, element) { + { + if (root.context !== emptyContextObject) { + // Super edge case: root has a legacy _renderSubtree context + // but we don't know the parentComponent so we can't pass it. + // Just ignore. We'll delete this with _renderSubtree code path later. + return; + } + flushPassiveEffects(); + updateContainerAtExpirationTime(element, root, null, Sync, null); + } +}; + function scheduleFibersWithFamiliesRecursively( fiber, updatedFamilies, @@ -19779,6 +19793,7 @@ function injectIntoDevTools(devToolsConfig) { // React Refresh findHostInstancesForRefresh: findHostInstancesForRefresh, scheduleRefresh: scheduleRefresh, + scheduleRoot: scheduleRoot, setRefreshHandler: setRefreshHandler }) ); diff --git a/Libraries/Renderer/implementations/ReactNativeRenderer-prod.fb.js b/Libraries/Renderer/implementations/ReactNativeRenderer-prod.fb.js index ba81492928..31d34c44a6 100644 --- a/Libraries/Renderer/implementations/ReactNativeRenderer-prod.fb.js +++ b/Libraries/Renderer/implementations/ReactNativeRenderer-prod.fb.js @@ -1772,7 +1772,12 @@ function injectInternals(internals) { try { var rendererID = hook.inject(internals); onCommitFiberRoot = catchErrors(function(root) { - return hook.onCommitFiberRoot(rendererID, root); + hook.onCommitFiberRoot( + rendererID, + root, + void 0, + 64 === (root.current.effectTag & 64) + ); }); onCommitFiberUnmount = catchErrors(function(fiber) { return hook.onCommitFiberUnmount(rendererID, fiber); @@ -7190,6 +7195,7 @@ var roots = new Map(), }, findHostInstancesForRefresh: null, scheduleRefresh: null, + scheduleRoot: null, setRefreshHandler: null }) ); diff --git a/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js b/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js index 569befb878..9d7209a9c0 100644 --- a/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js +++ b/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js @@ -1772,7 +1772,12 @@ function injectInternals(internals) { try { var rendererID = hook.inject(internals); onCommitFiberRoot = catchErrors(function(root) { - return hook.onCommitFiberRoot(rendererID, root); + hook.onCommitFiberRoot( + rendererID, + root, + void 0, + 64 === (root.current.effectTag & 64) + ); }); onCommitFiberUnmount = catchErrors(function(fiber) { return hook.onCommitFiberUnmount(rendererID, fiber); @@ -7190,6 +7195,7 @@ var roots = new Map(), }, findHostInstancesForRefresh: null, scheduleRefresh: null, + scheduleRoot: null, setRefreshHandler: null }) ); diff --git a/Libraries/Renderer/implementations/ReactNativeRenderer-profiling.fb.js b/Libraries/Renderer/implementations/ReactNativeRenderer-profiling.fb.js index 8576852053..742a08fe5f 100644 --- a/Libraries/Renderer/implementations/ReactNativeRenderer-profiling.fb.js +++ b/Libraries/Renderer/implementations/ReactNativeRenderer-profiling.fb.js @@ -1774,7 +1774,12 @@ function injectInternals(internals) { try { var rendererID = hook.inject(internals); onCommitFiberRoot = catchErrors(function(root) { - return hook.onCommitFiberRoot(rendererID, root); + hook.onCommitFiberRoot( + rendererID, + root, + void 0, + 64 === (root.current.effectTag & 64) + ); }); onCommitFiberUnmount = catchErrors(function(fiber) { return hook.onCommitFiberUnmount(rendererID, fiber); @@ -7430,6 +7435,7 @@ var roots = new Map(), }, findHostInstancesForRefresh: null, scheduleRefresh: null, + scheduleRoot: null, setRefreshHandler: null }) ); diff --git a/Libraries/Renderer/implementations/ReactNativeRenderer-profiling.js b/Libraries/Renderer/implementations/ReactNativeRenderer-profiling.js index b33e6b7a16..2acb589d9f 100644 --- a/Libraries/Renderer/implementations/ReactNativeRenderer-profiling.js +++ b/Libraries/Renderer/implementations/ReactNativeRenderer-profiling.js @@ -1774,7 +1774,12 @@ function injectInternals(internals) { try { var rendererID = hook.inject(internals); onCommitFiberRoot = catchErrors(function(root) { - return hook.onCommitFiberRoot(rendererID, root); + hook.onCommitFiberRoot( + rendererID, + root, + void 0, + 64 === (root.current.effectTag & 64) + ); }); onCommitFiberUnmount = catchErrors(function(fiber) { return hook.onCommitFiberUnmount(rendererID, fiber); @@ -7430,6 +7435,7 @@ var roots = new Map(), }, findHostInstancesForRefresh: null, scheduleRefresh: null, + scheduleRoot: null, setRefreshHandler: null }) ); diff --git a/package.json b/package.json index f1bec9869a..a686e8b830 100644 --- a/package.json +++ b/package.json @@ -103,7 +103,7 @@ "promise": "^7.1.1", "prop-types": "^15.7.2", "react-devtools-core": "^3.6.0", - "react-refresh": "0.0.7", + "react-refresh": "0.0.8", "regenerator-runtime": "^0.13.2", "scheduler": "0.14.0", "stacktrace-parser": "^0.1.3", diff --git a/yarn.lock b/yarn.lock index f8867c0ac9..1c9c9f189c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5925,10 +5925,10 @@ react-proxy@^1.1.7: lodash "^4.6.1" react-deep-force-update "^1.0.0" -react-refresh@0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.0.7.tgz#0d900c2fd605a89624e4ec79ec1ad786246e1f55" - integrity sha512-AOUyJUlII1ZcrOt5k72dkSleQeLwFOZqVvkMkZO4uLeU2Qsc2EEJN1MRyyHsnl9LHxBlVJ0gBnIFIJ0w8hsPhQ== +react-refresh@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.0.8.tgz#ee1083f08d32c99b52018660b7ba0242369359c8" + integrity sha512-Xs0garuWQugSMGq382tKn0dG5sIouCzxNb8I4I9A9A3ebRq+6Qgg+puDbFITe10s0vigIq5fm3m2j3oDRhkbnQ== react-test-renderer@16.8.6: version "16.8.6"