Skip to content

Commit

Permalink
Do a hard reload if hot update can't be applied
Browse files Browse the repository at this point in the history
Summary:
Hot reloading propagates upwards through the inverse dependency tree — from a file you edited, to the files that import it, and so on. However, we can't always reevaluate everything. Many core infra modules can't run twice, and also the more you run, the more the risk of encountering a module with init side effects. So our practical compromise is to stop the propagation when we reach a module whose exports look like React components. We say that such module "accepts" an update. This means that in practice, changes trigger module reevaluation up to the closest component modules from the edited file. (If you edited a component file, it re-executes alone — unless it exports a non-component.)

However, current implementation has a problem. Sometimes there is an inverse dependency path that has no "accepting" modules whatsoever. For example, maybe you're editing some core module, and its inverse dependency tree reaches goes into React Native itself. Or maybe it reaches the entry point with a bunch of side effects that can't be repeated, like registering the app root component.

In the past, such cases would lead to confusing errors like "Expected `FBPrelude.conclude()` to have been called" after hot reload. This was because we kept re-executing modules all the way upwards, even if there is nothing that can accept the update on the path. Eventually we'd reach top-level modules in the import graph that don't like to run twice.

This diff changes the logic so that we *don't attempt* to re-execute the module factories if we know that some inverse dependency path doesn't terminate in a component. In that case we know we simply *can't apply the hot update* because it doesn't stop at a point we can handle, like a React component.

Since the hot update fails in this case, I'm making it fall back to a regular reload. This is similar to how webpack handles a similar situation on the web. This means that hot updates normally don't refresh, but if we can't apply a hot update to this file, we do refresh automatically.

Reviewed By: cpojer

Differential Revision: D15631864

fbshipit-source-id: 52cd1b03739fd760f1b1b1ab8c7276a150cc3c4c
  • Loading branch information
gaearon authored and facebook-github-bot committed Jun 7, 2019
1 parent d9a8ac5 commit 3b1dbcc
Showing 1 changed file with 9 additions and 0 deletions.
9 changes: 9 additions & 0 deletions Libraries/Core/InitializeCore.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ require('./setUpSegmentFetcher');
if (__DEV__) {
require('./checkNativeVersion');
require('./setUpDeveloperTools');

// This is used by the require.js polyfill for hot reloading.
// TODO(t9759686) Scan polyfills for dependencies, too
const reload = require('../NativeModules/specs/NativeDevSettings').default
.reload;
if (typeof reload !== 'function') {
throw new Error('Could not find the reload() implementation.');
}
(require: any).reload = reload;
}

const GlobalPerformanceLogger = require('../Utilities/GlobalPerformanceLogger');
Expand Down

0 comments on commit 3b1dbcc

Please sign in to comment.