commit e6e4ca7b06e0e85dec75e53f02ccbb819b996f2a
parent db08f54fb907b047d6ed163419fd00a60a456582
Author: Vladimir Kutepov <frenzzy.man@gmail.com>
Date: Thu, 3 Nov 2016 02:43:58 +0400
Improve Hot Updates (#938)
Improve Hot Updates
- Add support for hot update of [`PureComponent`](https://facebook.github.io/react/docs/react-api.html#react.purecomponent) and components with [`shouldComponentUpdate`](https://facebook.github.io/react/docs/react-component.html#shouldcomponentupdate)
- Display application errors at full-screen in development mode
- Display hot update errors at full-screen when hot module replacement is enabled
closes #932, #929, #907, #871, #772, #436
Diffstat:
4 files changed, 60 insertions(+), 4 deletions(-)
diff --git a/package.json b/package.json
@@ -94,6 +94,7 @@
"postcss-url": "^5.1.2",
"raw-loader": "^0.5.1",
"react-addons-test-utils": "15.3.2",
+ "react-deep-force-update": "^2.0.1",
"react-hot-loader": "^3.0.0-beta.6",
"redbox-react": "^1.3.2",
"rimraf": "^2.5.4",
diff --git a/src/client.js b/src/client.js
@@ -16,6 +16,7 @@ import queryString from 'query-string';
import { createPath } from 'history/PathUtils';
import history from './core/history';
import App from './components/App';
+import { ErrorReporter, deepForceUpdate } from './core/devUtils';
// Global (context) variables that can be easily accessed from any React component
// https://facebook.github.io/react/docs/context.html
@@ -108,6 +109,7 @@ let onRenderComplete = function initialRenderComplete() {
FastClick.attach(document.body);
const container = document.getElementById('app');
+let appInstance;
let currentLocation = history.location;
let routes = require('./routes').default;
@@ -143,18 +145,28 @@ async function onLocationChange(location) {
return;
}
- ReactDOM.render(
+ appInstance = ReactDOM.render(
<App context={context}>{route.component}</App>,
container,
() => onRenderComplete(route, location)
);
- } catch (err) {
+ } catch (error) {
+ console.error(error); // eslint-disable-line no-console
+
+ // Current url has been changed during navigation process, do nothing
+ if (currentLocation.key !== location.key) {
+ return;
+ }
+
+ // Display the error in full-screen for development mode
if (process.env.NODE_ENV !== 'production') {
- throw err;
+ appInstance = null;
+ document.title = `Error: ${error.message}`;
+ ReactDOM.render(<ErrorReporter error={error} />, container);
+ return;
}
// Avoid broken navigation in production mode by a full page reload on error
- console.error(err); // eslint-disable-line no-console
window.location.reload();
}
}
@@ -169,6 +181,18 @@ if (module.hot) {
module.hot.accept('./routes', () => {
routes = require('./routes').default; // eslint-disable-line global-require
+ if (appInstance) {
+ try {
+ // Force-update the whole tree, including components that refuse to update
+ deepForceUpdate(appInstance);
+ } catch (error) {
+ appInstance = null;
+ document.title = `Hot Update Error: ${error.message}`;
+ ReactDOM.render(<ErrorReporter error={error} />, container);
+ return;
+ }
+ }
+
onLocationChange(currentLocation);
});
}
diff --git a/src/core/devUtils.js b/src/core/devUtils.js
@@ -0,0 +1,22 @@
+/**
+ * React Starter Kit (https://www.reactstarterkit.com/)
+ *
+ * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE.txt file in the root directory of this source tree.
+ */
+
+/* eslint-disable global-require */
+
+if (module.hot || process.env.NODE_ENV !== 'production') {
+ module.exports = {
+ // The red box (aka red screen of death) component to display your errors
+ // https://github.com/commissure/redbox-react
+ ErrorReporter: require('redbox-react').default,
+
+ // Force-updates React component tree recursively
+ // https://github.com/gaearon/react-deep-force-update
+ deepForceUpdate: require('react-deep-force-update'),
+ };
+}
diff --git a/src/core/history.js b/src/core/history.js
@@ -1,3 +1,12 @@
+/**
+ * React Starter Kit (https://www.reactstarterkit.com/)
+ *
+ * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE.txt file in the root directory of this source tree.
+ */
+
import createBrowserHistory from 'history/createBrowserHistory';
// Navigation manager, e.g. history.push('/home')