diff --git a/packages/babel-plugin-hot-reload/index.js b/packages/babel-plugin-hot-reload/index.js
new file mode 100644
index 00000000000..587aa6cf6ac
--- /dev/null
+++ b/packages/babel-plugin-hot-reload/index.js
@@ -0,0 +1,208 @@
+'use strict';
+const sysPath = require('path');
+const fs = require('fs');
+
+function functionReturnsElement(path) {
+ const { body } = path.body;
+ const last = body[body.length - 1];
+ if (typeof last !== 'object' || last.type !== 'ReturnStatement') {
+ return false;
+ }
+ const { type: returnType } = last.argument;
+ if (returnType !== 'JSXElement') {
+ return false;
+ }
+ return true;
+}
+
+function hotAssign(types, name, func) {
+ return [
+ types.variableDeclaration('var', [
+ types.variableDeclarator(
+ types.identifier(name),
+ hotRegister(types, name, func)
+ ),
+ ]),
+ types.exportDefaultDeclaration({ type: 'Identifier', name: name }),
+ ];
+}
+
+function isHotCall(call) {
+ return (
+ call &&
+ call.type === 'CallExpression' &&
+ call.callee.type === 'MemberExpression' &&
+ call.callee.object.name === 'window' &&
+ call.callee.property.name === '__assign'
+ );
+}
+
+function isIdentifierCandidate(identifier) {
+ return (
+ identifier.type === 'Identifier' &&
+ identifier.name[0] >= 'A' &&
+ identifier.name[0] <= 'Z'
+ );
+}
+
+function isVariableCandidate(declaration) {
+ return (
+ isIdentifierCandidate(declaration.id) &&
+ declaration.init &&
+ !isHotCall(declaration.init)
+ );
+}
+
+function isAssignmentCandidate(assignment) {
+ return isIdentifierCandidate(assignment.left) && assignment.operator === '=';
+}
+
+function hotRegister(t, name, content) {
+ if (t.isFunctionDeclaration(content)) {
+ content.type = 'FunctionExpression'; // TODO: why do we have to do this hack?
+ }
+ return t.callExpression(
+ t.memberExpression(t.identifier('window'), t.identifier('__assign')),
+ [t.identifier('module'), t.stringLiteral(name), content]
+ );
+}
+
+function hotDeclare(types, path) {
+ path.replaceWith(
+ types.variableDeclarator(
+ types.identifier(path.node.id.name),
+ hotRegister(types, path.node.id.name, path.node.init)
+ )
+ );
+}
+
+function isFile(path) {
+ try {
+ const stats = fs.lstatSync(path);
+ return stats.isFile();
+ } catch (err) {
+ if (err.code === 'ENOENT') {
+ return false;
+ }
+ throw err;
+ }
+}
+
+function naiveResolve(path, exts = ['js', 'jsx', 'ts', 'tsx']) {
+ return [path, ...exts.map(ext => path + '.' + ext)].find(isFile) || null;
+}
+
+function shouldReloadFile(file) {
+ // TODO: this is really naive
+ const contents = fs.readFileSync(file, 'utf8');
+ return contents.includes('React');
+}
+
+module.exports = function({ types }) {
+ return {
+ name: 'hot-reload',
+ visitor: {
+ ImportDeclaration(path) {
+ if (this.file.code.includes('no-hot')) {
+ return;
+ }
+
+ if (!types.isStringLiteral(path.node.source)) {
+ return;
+ }
+
+ const target = path.node.source.value;
+ if (!target.startsWith('.')) {
+ return;
+ }
+
+ const file = naiveResolve(
+ sysPath.resolve(sysPath.dirname(this.file.opts.filename), target)
+ );
+ if (file == null) {
+ return;
+ }
+
+ if (shouldReloadFile(file)) {
+ path.insertAfter(
+ types.expressionStatement(
+ types.callExpression(
+ types.memberExpression(
+ types.memberExpression(
+ types.identifier('module'),
+ types.identifier('hot')
+ ),
+ types.identifier('accept')
+ ),
+ [
+ types.stringLiteral(target),
+ types.memberExpression(
+ types.identifier('window'),
+ types.identifier('__invalidate')
+ ),
+ ]
+ )
+ )
+ );
+ }
+ },
+ ExportDefaultDeclaration(path) {
+ if (this.file.code.includes('no-hot')) {
+ return;
+ }
+
+ const { type } = path.node.declaration;
+ if (
+ type !== 'FunctionDeclaration' ||
+ !functionReturnsElement(path.node.declaration)
+ ) {
+ return;
+ }
+ const {
+ id: { name },
+ } = path.node.declaration;
+ path.replaceWithMultiple(hotAssign(types, name, path.node.declaration));
+ },
+ VariableDeclaration(path) {
+ if (path.parent.type !== 'Program') {
+ // Only traverse top level variable declaration
+ return;
+ }
+ if (this.file.code.includes('no-hot')) {
+ return;
+ }
+
+ path.traverse({
+ VariableDeclarator(path) {
+ if (isVariableCandidate(path.node)) {
+ hotDeclare(types, path);
+ }
+ },
+ });
+ },
+ ExpressionStatement(path) {
+ if (path.parent.type !== 'Program') {
+ // Only traverse top level variable declaration
+ return;
+ }
+ if (this.file.code.includes('no-hot')) {
+ return;
+ }
+
+ path.traverse({
+ AssignmentExpression(path) {
+ if (isAssignmentCandidate(path.node)) {
+ if (!isHotCall(path.node.right)) {
+ path.node.right = hotRegister(
+ types,
+ path.node.left.name,
+ path.node.right
+ );
+ }
+ }
+ },
+ });
+ },
+ },
+ };
+};
diff --git a/packages/babel-plugin-hot-reload/package.json b/packages/babel-plugin-hot-reload/package.json
new file mode 100644
index 00000000000..0c50959c06b
--- /dev/null
+++ b/packages/babel-plugin-hot-reload/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "babel-plugin-hot-reload",
+ "version": "0.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "license": "BSD-3-Clause",
+ "dependencies": {},
+ "devDependencies": {
+ "@babel/core": "^7.2.0",
+ "@babel/template": "^7.1.2",
+ "babel-core": "7.0.0-bridge.0",
+ "babel-plugin-tester": "^5.5.2",
+ "jest": "^23.6.0"
+ }
+}
diff --git a/packages/babel-plugin-hot-reload/tests/__fixtures__/assignment.js b/packages/babel-plugin-hot-reload/tests/__fixtures__/assignment.js
new file mode 100644
index 00000000000..f1c8fdc57d8
--- /dev/null
+++ b/packages/babel-plugin-hot-reload/tests/__fixtures__/assignment.js
@@ -0,0 +1,4 @@
+let LazyCC;
+LazyCC = 5;
+
+let DoubleLazyCC = LazyCC;
diff --git a/packages/babel-plugin-hot-reload/tests/__fixtures__/components/Hello.js b/packages/babel-plugin-hot-reload/tests/__fixtures__/components/Hello.js
new file mode 100644
index 00000000000..5ca10759334
--- /dev/null
+++ b/packages/babel-plugin-hot-reload/tests/__fixtures__/components/Hello.js
@@ -0,0 +1,5 @@
+import React from 'react';
+
+export default class extends React.Component {
+ render() {}
+}
diff --git a/packages/babel-plugin-hot-reload/tests/__fixtures__/components/Layout.js b/packages/babel-plugin-hot-reload/tests/__fixtures__/components/Layout.js
new file mode 100644
index 00000000000..5ca10759334
--- /dev/null
+++ b/packages/babel-plugin-hot-reload/tests/__fixtures__/components/Layout.js
@@ -0,0 +1,5 @@
+import React from 'react';
+
+export default class extends React.Component {
+ render() {}
+}
diff --git a/packages/babel-plugin-hot-reload/tests/__fixtures__/default-export-class.js b/packages/babel-plugin-hot-reload/tests/__fixtures__/default-export-class.js
new file mode 100644
index 00000000000..44a35fed54f
--- /dev/null
+++ b/packages/babel-plugin-hot-reload/tests/__fixtures__/default-export-class.js
@@ -0,0 +1,8 @@
+import React from 'react';
+import Hello from './components/Hello';
+
+export default class App extends React.Component {
+ render() {
+ return ;
+ }
+}
diff --git a/packages/babel-plugin-hot-reload/tests/__fixtures__/default-export.js b/packages/babel-plugin-hot-reload/tests/__fixtures__/default-export.js
new file mode 100644
index 00000000000..5a51123bfae
--- /dev/null
+++ b/packages/babel-plugin-hot-reload/tests/__fixtures__/default-export.js
@@ -0,0 +1,12 @@
+import React from 'react';
+import Hello from './components/Hello';
+import Layout from './components/Layout';
+import './App.css';
+
+export default function App({ children }) {
+ return (
+
+
+
+ );
+}
diff --git a/packages/babel-plugin-hot-reload/tests/__fixtures__/empty-variable.js b/packages/babel-plugin-hot-reload/tests/__fixtures__/empty-variable.js
new file mode 100644
index 00000000000..a4d53dfb049
--- /dev/null
+++ b/packages/babel-plugin-hot-reload/tests/__fixtures__/empty-variable.js
@@ -0,0 +1,2 @@
+// no-snap
+let LazyCC;
diff --git a/packages/babel-plugin-hot-reload/tests/__fixtures__/lazy-component.js b/packages/babel-plugin-hot-reload/tests/__fixtures__/lazy-component.js
new file mode 100644
index 00000000000..359892823f2
--- /dev/null
+++ b/packages/babel-plugin-hot-reload/tests/__fixtures__/lazy-component.js
@@ -0,0 +1,2 @@
+import { lazy } from 'react';
+const LazyCC = lazy(() => import('./CounterClass'));
diff --git a/packages/babel-plugin-hot-reload/tests/__snapshots__/fixtures.test.js.snap b/packages/babel-plugin-hot-reload/tests/__snapshots__/fixtures.test.js.snap
new file mode 100644
index 00000000000..fd283c38a11
--- /dev/null
+++ b/packages/babel-plugin-hot-reload/tests/__snapshots__/fixtures.test.js.snap
@@ -0,0 +1,91 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`hot-reload assignment: assignment 1`] = `
+"
+let LazyCC;
+LazyCC = 5;
+
+let DoubleLazyCC = LazyCC;
+
+ ↓ ↓ ↓ ↓ ↓ ↓
+
+let LazyCC;
+LazyCC = window.__assign(module, \\"LazyCC\\", 5);
+
+let DoubleLazyCC = window.__assign(module, \\"DoubleLazyCC\\", LazyCC);
+"
+`;
+
+exports[`hot-reload default export class: default export class 1`] = `
+"
+import React from 'react';
+import Hello from './components/Hello';
+
+export default class App extends React.Component {
+ render() {
+ return ;
+ }
+}
+
+ ↓ ↓ ↓ ↓ ↓ ↓
+
+import React from 'react';
+import Hello from './components/Hello';
+module.hot.accept(\\"./components/Hello\\", window.__invalidate);
+export default class App extends React.Component {
+ render() {
+ return ;
+ }
+
+}
+"
+`;
+
+exports[`hot-reload default export: default export 1`] = `
+"
+import React from 'react';
+import Hello from './components/Hello';
+import Layout from './components/Layout';
+import './App.css';
+
+export default function App({ children }) {
+ return (
+
+
+
+ );
+}
+
+ ↓ ↓ ↓ ↓ ↓ ↓
+
+import React from 'react';
+import Hello from './components/Hello';
+module.hot.accept(\\"./components/Hello\\", window.__invalidate);
+import Layout from './components/Layout';
+module.hot.accept(\\"./components/Layout\\", window.__invalidate);
+import './App.css';
+
+var App = window.__assign(module, \\"App\\", function App({
+ children
+}) {
+ return
+
+ ;
+});
+
+export default App;
+"
+`;
+
+exports[`hot-reload lazy component: lazy component 1`] = `
+"
+import { lazy } from 'react';
+const LazyCC = lazy(() => import('./CounterClass'));
+
+ ↓ ↓ ↓ ↓ ↓ ↓
+
+import { lazy } from 'react';
+
+const LazyCC = window.__assign(module, \\"LazyCC\\", lazy(() => import('./CounterClass')));
+"
+`;
diff --git a/packages/babel-plugin-hot-reload/tests/fixtures.test.js b/packages/babel-plugin-hot-reload/tests/fixtures.test.js
new file mode 100644
index 00000000000..9c0e81d692d
--- /dev/null
+++ b/packages/babel-plugin-hot-reload/tests/fixtures.test.js
@@ -0,0 +1,26 @@
+'use strict';
+
+const pluginTester = require('babel-plugin-tester');
+const hotReload = require('..');
+const path = require('path');
+const fs = require('fs');
+
+pluginTester({
+ plugin: hotReload,
+ filename: __filename,
+ babelOptions: {
+ parserOpts: { plugins: ['jsx', 'dynamicImport'] },
+ generatorOpts: {},
+ babelrc: false,
+ },
+ snapshot: true,
+ tests: fs
+ .readdirSync(path.join(__dirname, '__fixtures__'))
+ .map(entry => path.join(__dirname, '__fixtures__', entry))
+ .filter(entry => fs.lstatSync(entry).isFile() && entry.endsWith('js'))
+ .map(file => ({
+ title: path.basename(file, '.js').replace(/-/g, ' '),
+ snapshot: !fs.readFileSync(file, 'utf8').includes('no-snap'),
+ fixture: file,
+ })),
+});
diff --git a/packages/babel-preset-react-app/create.js b/packages/babel-preset-react-app/create.js
index a0423f2551c..0f928b5bb65 100644
--- a/packages/babel-preset-react-app/create.js
+++ b/packages/babel-preset-react-app/create.js
@@ -180,6 +180,9 @@ module.exports = function(api, opts, env) {
isEnvTest &&
// Transform dynamic import to require
require('babel-plugin-dynamic-import-node'),
+ isEnvDevelopment &&
+ // Transform for functional hot reloading
+ require('babel-plugin-hot-reload'),
].filter(Boolean),
overrides: [
isFlowEnabled && {
diff --git a/packages/babel-preset-react-app/package.json b/packages/babel-preset-react-app/package.json
index 753ce324f87..57fb54da0ff 100644
--- a/packages/babel-preset-react-app/package.json
+++ b/packages/babel-preset-react-app/package.json
@@ -34,6 +34,7 @@
"@babel/runtime": "7.1.5",
"babel-loader": "8.0.4",
"babel-plugin-dynamic-import-node": "2.2.0",
+ "babel-plugin-hot-reload": "^0.0.0",
"babel-plugin-macros": "2.4.2",
"babel-plugin-transform-react-remove-prop-types": "0.4.20"
}
diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json
index 1f6b8cfafec..65cf676b12b 100644
--- a/packages/react-scripts/package.json
+++ b/packages/react-scripts/package.json
@@ -73,8 +73,8 @@
"workbox-webpack-plugin": "3.6.3"
},
"devDependencies": {
- "react": "^16.3.2",
- "react-dom": "^16.3.2"
+ "react": "^16.7.0-alpha.2",
+ "react-dom": "^16.7.0-alpha.2"
},
"optionalDependencies": {
"fsevents": "1.2.4"
diff --git a/packages/react-scripts/template/src/App.js b/packages/react-scripts/template/src/App.js
index 7e261ca47e6..6a387218dea 100644
--- a/packages/react-scripts/template/src/App.js
+++ b/packages/react-scripts/template/src/App.js
@@ -1,28 +1,12 @@
-import React, { Component } from 'react';
-import logo from './logo.svg';
+import React from 'react';
+import Hello from './Hello';
+import Layout from './Layout';
import './App.css';
-class App extends Component {
- render() {
- return (
-
- );
- }
+export default function App({ children }) {
+ return (
+
+
+
+ );
}
-
-export default App;
diff --git a/packages/react-scripts/template/src/CounterClass.js b/packages/react-scripts/template/src/CounterClass.js
new file mode 100644
index 00000000000..ddd8d7fff8f
--- /dev/null
+++ b/packages/react-scripts/template/src/CounterClass.js
@@ -0,0 +1,30 @@
+import React, { Component } from 'react';
+import CounterFunction from './CounterFunction';
+import HOCFunction from './HOCFunction';
+
+let HFF = HOCFunction(CounterFunction);
+
+export default class Counter extends Component {
+ state = { value: 0 };
+ componentDidMount() {
+ this.interval = setInterval(
+ () => this.setState(s => ({ value: s.value + 1 })),
+ 1000
+ );
+ }
+ componentWillUnmount() {
+ clearInterval(this.interval);
+ }
+ render() {
+ return (
+
+ {this.state.value}{' '}
+ {this.props.hocChild && (
+ <>
+ (inner HOC: {HFF.field})
+ >
+ )}
+
+ );
+ }
+}
diff --git a/packages/react-scripts/template/src/CounterFunction.js b/packages/react-scripts/template/src/CounterFunction.js
new file mode 100644
index 00000000000..2b6d41accd0
--- /dev/null
+++ b/packages/react-scripts/template/src/CounterFunction.js
@@ -0,0 +1,35 @@
+import React, { forwardRef, memo, useReducer, useLayoutEffect } from 'react';
+import HOCFunction from './HOCFunction';
+
+let Fwd = forwardRef((props, ref) => (
+
+
+
+));
+let HFF = HOCFunction(Fwd);
+
+let Counter = memo(
+ memo(function Counter(props) {
+ const [value, dispatch] = useReducer((v, a) => {
+ return a === 'inc' ? v + 1 : v;
+ }, 0);
+ useLayoutEffect(() => {
+ const id = setInterval(() => dispatch('inc'), 1000);
+ return () => clearInterval(id);
+ }, []);
+
+ return (
+
+ {value}
+ {props.hocChild && (
+ <>
+ (inner HOC: {HFF.field})
+ >
+ )}
+
+ );
+ })
+);
+
+export let N = 10;
+export default Fwd;
diff --git a/packages/react-scripts/template/src/HOCClass.js b/packages/react-scripts/template/src/HOCClass.js
new file mode 100644
index 00000000000..5f442b854e3
--- /dev/null
+++ b/packages/react-scripts/template/src/HOCClass.js
@@ -0,0 +1,14 @@
+import React, { Component } from 'react';
+
+export default function withStuff(Wrapped, color) {
+ return class extends Component {
+ static field = 42;
+ render() {
+ return (
+
+
+
+ );
+ }
+ };
+}
diff --git a/packages/react-scripts/template/src/HOCFunction.js b/packages/react-scripts/template/src/HOCFunction.js
new file mode 100644
index 00000000000..029790888e1
--- /dev/null
+++ b/packages/react-scripts/template/src/HOCFunction.js
@@ -0,0 +1,13 @@
+import React from 'react';
+
+export default function withStuff(Wrapped, color) {
+ function Wrapper(props) {
+ return (
+
+
+
+ );
+ }
+ Wrapper.field = 42;
+ return Wrapper;
+}
diff --git a/packages/react-scripts/template/src/Hello.js b/packages/react-scripts/template/src/Hello.js
new file mode 100644
index 00000000000..ea1d0e53ec6
--- /dev/null
+++ b/packages/react-scripts/template/src/Hello.js
@@ -0,0 +1,37 @@
+import React, { Suspense, lazy, useState } from 'react';
+import HOCClass from './HOCClass';
+import HOCFunction from './HOCFunction';
+import CounterClass from './CounterClass';
+import CounterFunction, { N } from './CounterFunction';
+
+let LazyCC = lazy(() => import('./CounterClass')),
+ LazyCF = lazy(() => import('./CounterFunction'));
+let DblCC = CounterClass,
+ DblCF = CounterFunction;
+let HCC = HOCClass(CounterClass, 'red'),
+ HCF = HOCClass(CounterFunction, 'orange'),
+ HFC = HOCFunction(CounterClass, 'yellow'),
+ HFF = HOCFunction(CounterFunction, 'green');
+
+export default function Hello() {
+ const [value] = useState(Math.random());
+ return (
+ }>
+
+ {N} - {value.toString().slice(0, 5)}
+
+ hello world!
+
+ class:
+
+ function:
+
+ doublewrapped:
+
+ lazy:
+
+ hocs:
+
+
+ );
+}
diff --git a/packages/react-scripts/template/src/Layout.js b/packages/react-scripts/template/src/Layout.js
new file mode 100644
index 00000000000..0a50b3cf34f
--- /dev/null
+++ b/packages/react-scripts/template/src/Layout.js
@@ -0,0 +1,26 @@
+import React, { Component } from 'react';
+import logo from './logo.svg';
+
+export default class Layout extends Component {
+ render() {
+ return (
+
+ );
+ }
+}
diff --git a/packages/react-scripts/template/src/TODO.md b/packages/react-scripts/template/src/TODO.md
new file mode 100644
index 00000000000..88964b96a45
--- /dev/null
+++ b/packages/react-scripts/template/src/TODO.md
@@ -0,0 +1,49 @@
+- other strategies:
+ - seb suggested walking down the tree and reconciling
+- write down constraints.
+ - actually, tests!
+
+* don't accept module if either assignment failed
+
+ - write down why granular invalidation is useful
+ https://mobile.twitter.com/wSokra/status/1071503979014692865
+
+- effects can change arg num
+
+* frags
+* error handling
+ - reenable overlay
+ - offer a way to reset?
+ - suppress error dialog?
+ - suppress messages?
+ - check types? debug tools?
+
+- highlight updates
+
+* directives?
+ - !
+* hooks
+ - remount on error?
+ - compare introspection?
+* hocs?
+ - with self as input
+ - returning a class
+ - HOC itself by mistake
+ - protect against nested?
+* type equality
+ - memo and friends
+ - nested / applied twice
+ - mutually recursive
+ - re-exports
+ - same HOC applied early, two callsites
+* classes
+ - how to detect and reload?
+* render props
+* when to accept?
+* test integrations
+* exotic (lazy, memo, fwd)
+* how to force update all (incl. inside memo)
+* displayName etc
+* false positives for things getting wrapped
+* forwardRef
+ - make sure refs _on_ proxies actually work
diff --git a/packages/react-scripts/template/src/hot.js b/packages/react-scripts/template/src/hot.js
new file mode 100644
index 00000000000..1d523ef143b
--- /dev/null
+++ b/packages/react-scripts/template/src/hot.js
@@ -0,0 +1,272 @@
+// no-hot
+import React from 'react';
+
+let HotContext = React.createContext();
+
+let invalidated;
+let __setInc;
+window.__invalidate = () => {
+ if (!invalidated) {
+ invalidated = true;
+ setTimeout(() => {
+ invalidated = false;
+ __setInc(c => c + 1);
+ });
+ }
+};
+
+export function HotContainer({ children }) {
+ const [inc, setInc] = React.useState(0);
+ __setInc = setInc;
+ return {children};
+}
+
+let CurrentOwner =
+ React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner;
+function readContext(Ctx) {
+ CurrentOwner.currentDispatcher.readContext(Ctx);
+}
+
+let idToPersistentType = new Map();
+let idToRawFunction = new Map();
+let proxies = new WeakSet();
+
+function getKind(type) {
+ if (typeof type === 'function') {
+ if (type.prototype && type.prototype.isReactComponent) {
+ return 'class';
+ } else {
+ return 'function';
+ }
+ } else if (type !== null && typeof type === 'object') {
+ if (type.$$typeof === Symbol.for('react.memo')) {
+ return 'memo';
+ } else if (type.$$typeof === Symbol.for('react.lazy')) {
+ return 'lazy';
+ } else if (type.$$typeof === Symbol.for('react.forward_ref')) {
+ return 'forward_ref';
+ }
+ }
+ return 'other';
+}
+
+function init(rawType, id) {
+ let kind = getKind(rawType);
+ switch (kind) {
+ case 'function': {
+ if (proxies.has(rawType)) {
+ return rawType;
+ }
+ idToRawFunction.set(id, rawType);
+
+ let abandonedHooks = [];
+ let isAbandoningHooks = false;
+ let previousHooks = [];
+ const NEVER = {};
+ const NOOP = {};
+
+ const proxy = new Proxy(rawType, {
+ apply(target, thisArg, args) {
+ readContext(HotContext);
+
+ let realDispatcher = CurrentOwner.currentDispatcher;
+ let freshRawType = idToRawFunction.get(id);
+ let currentHooks = [];
+
+ function callNoopHook([hook, data]) {
+ switch (hook) {
+ case realDispatcher.useState:
+ realDispatcher.useState();
+ break;
+ case realDispatcher.useRef:
+ realDispatcher.useRef();
+ break;
+ case realDispatcher.useReducer:
+ realDispatcher.useReducer(state => state);
+ break;
+ case realDispatcher.useLayoutEffect: {
+ let inputs;
+ if (data) {
+ inputs = new Array(data).fill(NOOP);
+ }
+ realDispatcher.useLayoutEffect(() => {}, inputs);
+ break;
+ }
+ case realDispatcher.useEffect: {
+ let inputs;
+ if (data) {
+ inputs = new Array(data).fill(NOOP);
+ }
+ realDispatcher.useEffect(() => {}, inputs);
+ break;
+ }
+ case realDispatcher.useMemo: {
+ let inputs;
+ if (data) {
+ inputs = new Array(data).fill(NOOP);
+ }
+ realDispatcher.useMemo(() => {}, inputs);
+ break;
+ }
+ case realDispatcher.useCallback: {
+ let inputs;
+ if (data) {
+ inputs = new Array(data).fill(NOOP);
+ }
+ realDispatcher.useCallback(() => {}, inputs);
+ break;
+ }
+ case realDispatcher.readContext:
+ case realDispatcher.useContext:
+ break;
+ default:
+ throw new Error('TODO');
+ }
+ }
+
+ // Pad left abandoned Hooks
+ for (let i = 0; i < abandonedHooks.length; i++) {
+ const hook = abandonedHooks[i];
+ callNoopHook(hook);
+ }
+
+ CurrentOwner.currentDispatcher = new Proxy(realDispatcher, {
+ get(target, prop, receiver) {
+ const hook = Reflect.get(...arguments);
+ return new Proxy(hook, {
+ apply(t, thisArg, argumentsList) {
+ let prevHook = previousHooks[currentHooks.length];
+ if (prevHook && prevHook[0] !== hook) {
+ throw new Error('Hook mismatch.');
+ }
+ switch (hook) {
+ case realDispatcher.useState:
+ if (
+ prevHook &&
+ typeof prevHook[1] !== typeof argumentsList[0]
+ ) {
+ throw new Error('State type mismatch.');
+ }
+ currentHooks.push([hook, argumentsList[0]]);
+ break;
+ case realDispatcher.useLayoutEffect:
+ case realDispatcher.useEffect:
+ case realDispatcher.useMemo:
+ case realDispatcher.useCallback:
+ {
+ let inputs = argumentsList[1];
+ if (inputs) {
+ if (inputs.length === 0) {
+ // Allows us to clean up if this Hook is removed later.
+ argumentsList[1] = inputs = [NEVER];
+ }
+ currentHooks.push([hook, inputs.length]);
+ } else {
+ currentHooks.push([hook]);
+ }
+ }
+ break;
+ default:
+ currentHooks.push([hook]);
+ break;
+ }
+ return Reflect.apply(t, thisArg, argumentsList);
+ },
+ });
+ },
+ });
+ let ret;
+ try {
+ ret = freshRawType.apply(null, args);
+ isAbandoningHooks = false;
+ } catch (err) {
+ if (isAbandoningHooks) {
+ isAbandoningHooks = false;
+ throw err;
+ }
+ isAbandoningHooks = true;
+ } finally {
+ CurrentOwner.currentDispatcher = realDispatcher;
+ }
+
+ // Pad right missing Hooks
+ for (let i = currentHooks.length; i < previousHooks.length; i++) {
+ const hook = previousHooks[i];
+ callNoopHook(hook);
+ currentHooks.push(hook);
+ }
+
+ previousHooks = currentHooks;
+
+ if (isAbandoningHooks) {
+ const [, reset] = realDispatcher.useState();
+ // Shift Hooks to recover. This leaks memory. Ideallly we'd reset.
+ previousHooks.push([realDispatcher.useState]);
+ abandonedHooks.push(...previousHooks);
+ previousHooks = [];
+ reset();
+ }
+
+ return (
+ {ret}
+ );
+ },
+ });
+ proxies.add(proxy);
+ return proxy;
+ }
+ case 'memo': {
+ rawType.type = init(rawType.type, id);
+ return rawType;
+ }
+ case 'lazy': {
+ return rawType;
+ }
+ case 'forward_ref': {
+ rawType.render = init(rawType.render, id);
+ return rawType;
+ }
+ default: {
+ return rawType;
+ }
+ }
+}
+
+function accept(type, nextRawType, id) {
+ let kind = getKind(type);
+ let nextKind = getKind(nextRawType);
+ if (kind !== nextKind) {
+ return false;
+ }
+ switch (kind) {
+ case 'function': {
+ idToRawFunction.set(id, nextRawType);
+ const forceRemount =
+ nextRawType.toString().indexOf('//!') !== -1 ||
+ nextRawType.toString().indexOf('// !') !== -1;
+ return !forceRemount;
+ }
+ case 'memo': {
+ return accept(type.type, nextRawType.type, id);
+ }
+ case 'forward_ref': {
+ return accept(type.render, nextRawType.render, id);
+ }
+ case 'lazy': {
+ return true;
+ }
+ default: {
+ return false;
+ }
+ }
+}
+
+window.__assign = function(webpackModule, localId, nextRawType) {
+ const id = webpackModule.i + '$' + localId;
+ let type = idToPersistentType.get(id);
+ if (!accept(type, nextRawType, id)) {
+ type = init(nextRawType, id);
+ idToPersistentType.set(id, type);
+ }
+ return type;
+};
diff --git a/packages/react-scripts/template/src/index.js b/packages/react-scripts/template/src/index.js
index 0c5e75da1cd..b86dd84a690 100644
--- a/packages/react-scripts/template/src/index.js
+++ b/packages/react-scripts/template/src/index.js
@@ -1,10 +1,16 @@
import React from 'react';
import ReactDOM from 'react-dom';
+import { HotContainer } from './hot';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
-ReactDOM.render(, document.getElementById('root'));
+ReactDOM.render(
+
+
+ ,
+ document.getElementById('root')
+);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.