Skip to content

Commit c5b96c2

Browse files
Add experimental react-refresh support (#8582)
Co-authored-by: Ian Schmitz <ianschmitz@gmail.com>
1 parent e0b179c commit c5b96c2

File tree

6 files changed

+62
-4
lines changed

6 files changed

+62
-4
lines changed

docusaurus/docs/advanced-configuration.md

+1
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,5 @@ You can adjust various development and production settings by setting environmen
2626
| INLINE_RUNTIME_CHUNK | 🚫 Ignored | ✅ Used | By default, Create React App will embed the runtime script into `index.html` during the production build. When set to `false`, the script will not be embedded and will be imported as usual. This is normally required when dealing with CSP. |
2727
| IMAGE_INLINE_SIZE_LIMIT | 🚫 Ignored | ✅ Used | By default, images smaller than 10,000 bytes are encoded as a data URI in base64 and inlined in the CSS or JS build artifact. Set this to control the size limit in bytes. Setting it to 0 will disable the inlining of images. |
2828
| EXTEND_ESLINT | ✅ Used | ✅ Used | When set to `true`, user provided ESLint configs will be used by `eslint-loader`. Note that any rules set to `"error"` will stop the application from building. |
29+
| FAST_REFRESH | ✅ Used | 🚫 Ignored | When set to `true`, enables experimental support for fast refresh to allow you to tweak your components in real time without reloading the page. |
2930
| TSC_COMPILE_ON_ERROR | ✅ Used | ✅ Used | When set to `true`, you can run and properly build TypeScript projects even if there are TypeScript type check errors. These errors are printed as warnings in the terminal and/or browser console. |

packages/react-dev-utils/webpackHotDevClient.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,10 @@ function tryApplyUpdates(onHotUpdateSuccess) {
243243
}
244244

245245
function handleApplyUpdates(err, updatedModules) {
246-
if (err || !updatedModules || hadRuntimeError) {
246+
const hasReactRefresh = process.env.FAST_REFRESH;
247+
const wantsForcedReload = err || !updatedModules || hadRuntimeError;
248+
// React refresh can handle hot-reloading over errors.
249+
if (!hasReactRefresh && wantsForcedReload) {
247250
window.location.reload();
248251
return;
249252
}

packages/react-scripts/config/env.js

+5
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ function getClientEnvironment(publicUrl) {
9393
WDS_SOCKET_HOST: process.env.WDS_SOCKET_HOST,
9494
WDS_SOCKET_PATH: process.env.WDS_SOCKET_PATH,
9595
WDS_SOCKET_PORT: process.env.WDS_SOCKET_PORT,
96+
// Whether or not react-refresh is enabled.
97+
// react-refresh is not 100% stable at this time,
98+
// which is why it's disabled by default.
99+
// It is defined here so it is available in the webpackHotDevClient.
100+
FAST_REFRESH: process.env.FAST_REFRESH || false,
96101
}
97102
);
98103
// Stringify all values so we can feed into webpack DefinePlugin
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
// This file is currently required because the error overlay has a bug preventing the its outright disabling.
9+
10+
'use strict';
11+
12+
function handleRuntimeError() {
13+
// Stubbed out due to a bug.
14+
}
15+
function clearRuntimeErrors() {
16+
// Stubbed out due to a bug.
17+
}
18+
19+
module.exports = {
20+
handleRuntimeError,
21+
clearRuntimeErrors,
22+
};

packages/react-scripts/config/webpack.config.js

+28-3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const getClientEnvironment = require('./env');
3232
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
3333
const ForkTsCheckerWebpackPlugin = require('react-dev-utils/ForkTsCheckerWebpackPlugin');
3434
const typescriptFormatter = require('react-dev-utils/typescriptFormatter');
35+
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
3536
// @remove-on-eject-begin
3637
const getCacheIdentifier = require('react-dev-utils/getCacheIdentifier');
3738
// @remove-on-eject-end
@@ -41,6 +42,11 @@ const appPackageJson = require(paths.appPackageJson);
4142

4243
// Source maps are resource heavy and can cause out of memory issue for large source files.
4344
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
45+
46+
const webpackDevClientEntry = require.resolve(
47+
'react-dev-utils/webpackHotDevClient'
48+
);
49+
4450
// Some apps do not need the benefits of saving a web request, so not inlining the chunk
4551
// makes for a smoother build process.
4652
const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false';
@@ -77,6 +83,8 @@ module.exports = function(webpackEnv) {
7783
// Get environment variables to inject into our app.
7884
const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));
7985

86+
const shouldUseReactRefresh = env.raw.FAST_REFRESH;
87+
8088
// common function to get style loaders
8189
const getStyleLoaders = (cssOptions, preProcessor) => {
8290
const loaders = [
@@ -158,10 +166,13 @@ module.exports = function(webpackEnv) {
158166
// Note: instead of the default WebpackDevServer client, we use a custom one
159167
// to bring better experience for Create React App users. You can replace
160168
// the line below with these two lines if you prefer the stock client:
169+
//
161170
// require.resolve('webpack-dev-server/client') + '?/',
162171
// require.resolve('webpack/hot/dev-server'),
163-
isEnvDevelopment &&
164-
require.resolve('react-dev-utils/webpackHotDevClient'),
172+
//
173+
// When using the experimental react-refresh integration,
174+
// the webpack plugin takes care of injecting the dev client for us.
175+
isEnvDevelopment && !shouldUseReactRefresh && webpackDevClientEntry,
165176
// Finally, this is your app's code:
166177
paths.appIndexJs,
167178
// We include the app code last so that if there is a runtime error during
@@ -419,7 +430,10 @@ module.exports = function(webpackEnv) {
419430
},
420431
},
421432
},
422-
],
433+
isEnvDevelopment &&
434+
shouldUseReactRefresh &&
435+
require.resolve('react-refresh/babel'),
436+
].filter(Boolean),
423437
],
424438
// This is a feature of `babel-loader` for webpack (not Babel itself).
425439
// It enables caching results in ./node_modules/.cache/babel-loader/
@@ -607,6 +621,17 @@ module.exports = function(webpackEnv) {
607621
new webpack.DefinePlugin(env.stringified),
608622
// This is necessary to emit hot updates (currently CSS only):
609623
isEnvDevelopment && new webpack.HotModuleReplacementPlugin(),
624+
// Experimental hot reloading for React .
625+
// https://github.com/facebook/react/tree/master/packages/react-refresh
626+
isEnvDevelopment &&
627+
shouldUseReactRefresh &&
628+
new ReactRefreshWebpackPlugin({
629+
overlay: {
630+
entry: webpackDevClientEntry,
631+
// TODO: This is just a stub module. Clean this up if possible.
632+
module: require.resolve('./hotRefreshOverlayModuleStub'),
633+
},
634+
}),
610635
// Watcher doesn't work well if you mistype casing in a path so we use
611636
// a plugin that prints an error when you attempt to do this.
612637
// See https://github.com/facebook/create-react-app/issues/240

packages/react-scripts/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"types": "./lib/react-app.d.ts",
3030
"dependencies": {
3131
"@babel/core": "7.9.0",
32+
"@pmmmwh/react-refresh-webpack-plugin": "0.3.0-beta.1",
3233
"@svgr/webpack": "4.3.3",
3334
"@typescript-eslint/eslint-plugin": "^2.10.0",
3435
"@typescript-eslint/parser": "^2.10.0",
@@ -68,6 +69,7 @@
6869
"postcss-safe-parser": "4.0.1",
6970
"react-app-polyfill": "^1.0.6",
7071
"react-dev-utils": "^10.2.1",
72+
"react-refresh": "^0.8.1",
7173
"resolve": "1.15.0",
7274
"resolve-url-loader": "3.1.1",
7375
"sass-loader": "8.0.2",

0 commit comments

Comments
 (0)