Skip to content

Commit

Permalink
feat: support React 17 (#1557)
Browse files Browse the repository at this point in the history
* feat: support React 17

* add react 17 tests

* fix: use enzyme 16 adapter for React 17

* fix: add React 17 support for lazy, related to #1425

* ci uses absolute paths

* fix: split trackTailUpdate into two options. Enable lazy wrapping by default

* update tests

* update yarn.lock

* linting

* reverse wrapLazy option effect
  • Loading branch information
theKashey committed Sep 22, 2020
1 parent e44103a commit c05396b
Show file tree
Hide file tree
Showing 15 changed files with 445 additions and 98 deletions.
4 changes: 2 additions & 2 deletions examples/styled-components/package.json
Expand Up @@ -23,8 +23,8 @@
"dependencies": {
"babel-polyfill": "^6.26.0",
"emotion": "^8.0.12",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react": "^17.0.0-rc.2",
"react-dom": "^17.0.0-rc.2",
"react-emotion": "^9.2.12",
"react-hot-loader": "^4.12.21",
"react-spring": "^8.0.25",
Expand Down
36 changes: 13 additions & 23 deletions examples/styled-components/yarn.lock
Expand Up @@ -4118,14 +4118,6 @@ prop-types@^15.6.1:
loose-envify "^1.3.1"
object-assign "^4.1.1"

prop-types@^15.6.2:
version "15.6.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102"
integrity sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==
dependencies:
loose-envify "^1.3.1"
object-assign "^4.1.1"

proxy-addr@~2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.2.tgz#6571504f47bb988ec8180253f85dd7e14952bdec"
Expand Down Expand Up @@ -4247,15 +4239,14 @@ rc@^1.2.7:
minimist "^1.2.0"
strip-json-comments "~2.0.1"

react-dom@^16.13.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f"
integrity sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==
react-dom@^17.0.0-rc.2:
version "17.0.0-rc.2"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.0-rc.2.tgz#7c13267e1e97d53d9a6395ef1acf1a44157dc38a"
integrity sha512-Nx6SK3s8dhQbKeOoRYPpQcYTrhojXw6nrX7Lb/rpMKyAp1wfhyzC3ukZtWczwILR9h4MzxtTLt/IIs8Bk4XqcQ==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
prop-types "^15.6.2"
scheduler "^0.19.1"
scheduler "0.20.0-rc.2"

react-emotion@^9.2.12:
version "9.2.12"
Expand Down Expand Up @@ -4307,14 +4298,13 @@ react-spring@^8.0.25:
"@babel/runtime" "^7.3.1"
prop-types "^15.5.8"

react@^16.13.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e"
integrity sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==
react@^17.0.0-rc.2:
version "17.0.0-rc.2"
resolved "https://registry.yarnpkg.com/react/-/react-17.0.0-rc.2.tgz#3d454a888c119f5bde8ac016840aab8ecda22413"
integrity sha512-Z/qj0VWOF0e7VlUY2IhQr05vQ/gFSGlqmPMgxD4JytqmQxh5T7We0t5n1M98UmgJyRFTSbdCs58ZEyv6WwacWA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
prop-types "^15.6.2"

"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@~2.3.6:
version "2.3.6"
Expand Down Expand Up @@ -4556,10 +4546,10 @@ sax@^1.2.4:
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==

scheduler@^0.19.1:
version "0.19.1"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196"
integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==
scheduler@0.20.0-rc.2:
version "0.20.0-rc.2"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.0-rc.2.tgz#5c48ac29e1a352a43c74cb5fc54c5ca94d8396fc"
integrity sha512-tgvUKHPbQr0fq5EfRd3S8hgohi/fI3h/gv1jxVb9IungCF/t2jUG946dlwgCxy8bnYvSIwRjcuw7eksDDpplLg==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
Expand Down
7 changes: 4 additions & 3 deletions package.json
Expand Up @@ -54,6 +54,7 @@
"devDependencies": {
"@hot-loader/react-dom": "^16.8.6",
"@types/react": "16",
"@wojtekmaj/enzyme-adapter-react-17": "^0.1.1",
"babel-cli": "^6.7.5",
"babel-core": "^6.26.3",
"babel-eslint": "^8.2.3",
Expand Down Expand Up @@ -99,9 +100,9 @@
"yarn-deduplicate": "^1.1.1"
},
"peerDependencies": {
"@types/react": "^15.0.0 || ^16.0.0",
"react": "^15.0.0 || ^16.0.0",
"react-dom": "^15.0.0 || ^16.0.0"
"@types/react": "^15.0.0 || ^16.0.0 || ^17.0.0 ",
"react": "^15.0.0 || ^16.0.0 || ^17.0.0 ",
"react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 "
},
"peerDependenciesMeta": {
"@types/react": {
Expand Down
46 changes: 2 additions & 44 deletions scripts/ci.sh
Expand Up @@ -8,47 +8,5 @@ echo "Linting"
yarn lint
echo "\n\n"

echo "Installing React 15"
yarn add react@15 react-dom@15 react-test-renderer@15 --pure-lockfile
echo "\n\n"

yarn test:react-dom:prepare

# echo "Running tests on React 15 - Babel ES2015"
# yarn test:es2015
# echo "\n\n"

# echo "Running tests on React 15 - Babel Modern"
# yarn test:modern
# echo "\n\n"
# echo "\n\n"

echo "Installing React 16.10"
yarn add react@16 react-dom@16.10 react-test-renderer@16.10 --pure-lockfile
echo "\n\n"

yarn test:react-dom:prepare

echo "Running tests on React 16.10 - Babel ES2015"
yarn test:es2015
echo "\n\n"

echo "Running tests on React 16.10 - Babel Modern"
yarn test:modern --coverage && codecov
echo "\n\n"



echo "Installing React 16:latest"
yarn add react@16 react-dom@16 react-test-renderer@16 --pure-lockfile
echo "\n\n"

yarn test:react-dom:prepare

echo "Running tests on React 16:latest - Babel ES2015"
yarn test:es2015
echo "\n\n"

echo "Running tests on React 16:latest - Babel Modern"
yarn test:modern --coverage && codecov
echo "\n\n"
./scripts/react-16.ci.sh
./scripts/react-17.ci.sh
14 changes: 14 additions & 0 deletions scripts/react-15.ci.sh
@@ -0,0 +1,14 @@
echo "Installing React 15"
yarn add react@15 react-dom@15 react-test-renderer@15 --pure-lockfile
echo "\n\n"

yarn test:react-dom:prepare

echo "Running tests on React 15 - Babel ES2015"
yarn test:es2015
echo "\n\n"

echo "Running tests on React 15 - Babel Modern"
yarn test:modern
echo "\n\n"
echo "\n\n"
29 changes: 29 additions & 0 deletions scripts/react-16.ci.sh
@@ -0,0 +1,29 @@
echo "Installing React 16.10"
yarn add react@16 react-dom@16.10 react-test-renderer@16.10 --pure-lockfile
echo "\n\n"

yarn test:react-dom:prepare

echo "Running tests on React 16.10 - Babel ES2015"
yarn test:es2015
echo "\n\n"

echo "Running tests on React 16.10 - Babel Modern"
yarn test:modern --coverage && codecov
echo "\n\n"



echo "Installing React 16:latest"
yarn add react@16 react-dom@16 react-test-renderer@16 --pure-lockfile
echo "\n\n"

yarn test:react-dom:prepare

echo "Running tests on React 16:latest - Babel ES2015"
yarn test:es2015
echo "\n\n"

echo "Running tests on React 16:latest - Babel Modern"
yarn test:modern --coverage && codecov
echo "\n\n"
13 changes: 13 additions & 0 deletions scripts/react-17.ci.sh
@@ -0,0 +1,13 @@
echo "Installing React 17"
yarn add react@17.0.0-rc.2 react-dom@17.0.0-rc.2 react-test-renderer@17.0.0-rc.2 --pure-lockfile
echo "\n\n"

yarn test:react-dom:prepare

echo "Running tests on React 17 - Babel ES2015"
yarn test:es2015
echo "\n\n"

echo "Running tests on React 17 - Babel Modern"
yarn test:modern --coverage && codecov
echo "\n\n"
3 changes: 3 additions & 0 deletions src/configuration.js
Expand Up @@ -58,6 +58,9 @@ const configuration = {
// Actively track lazy loaded components
trackTailUpdates: true,

// wrap lazy with AppProvider to allow independent updates
wrapLazy: true,

// react hot dom features enabled
IS_REACT_MERGE_ENABLED: false,
};
Expand Down
2 changes: 2 additions & 0 deletions src/hot.dev.js
Expand Up @@ -177,4 +177,6 @@ const hot = sourceModule => {
};
};

reactHotLoader.register(AppContainer, 'AppContainer', 'hot-dev');

export default hot;
68 changes: 54 additions & 14 deletions src/reconciler/fiberUpdater.js
@@ -1,3 +1,4 @@
/* eslint-disable no-underscore-dangle */
import React from 'react';
import configuration from '../configuration';
import { enterHotUpdate } from '../global/generation';
Expand All @@ -6,38 +7,77 @@ import { resolveType } from './resolver';

const lazyConstructor = '_ctor';

const getLazyConstructor = target => {
// React 16
if (target[lazyConstructor]) {
return target[lazyConstructor];
}

// React 17
if (target._payload) {
return target._payload._result;
}
return null;
};

const setLazyConstructor = (target, replacement) => {
replacement.isPatchedByReactHotLoader = true;

// React 16
if (target[lazyConstructor]) {
target[lazyConstructor] = replacement;
}
// React 17
else if (target._payload) {
target._payload._hotUpdated = true;
target._payload._result = replacement;
} else {
console.error('could not update lazy component');
}
};

const patched = fn => {
fn.isPatchedByReactHotLoader = true;
return fn;
};

const patchLazyConstructor = target => {
if (!configuration.trackTailUpdates && !target[lazyConstructor].isPatchedByReactHotLoader) {
const ctor = target[lazyConstructor];
target[lazyConstructor] = () =>
if (configuration.wrapLazy && !getLazyConstructor(target).isPatchedByReactHotLoader) {
const ctor = getLazyConstructor(target);
setLazyConstructor(target, () =>
ctor().then(m => {
const C = resolveType(m.default);
// chunks has been updated - new hot loader process is taking a place
enterHotUpdate();
if (!React.forwardRef) {
return {
default: props => (
default: patched(props => (
<AppContainer>
<C {...props} />
</AppContainer>
),
)),
};
}
return {
default: React.forwardRef((props, ref) => (
<AppContainer>
<C {...props} ref={ref} />
</AppContainer>
)),
default: patched(
// eslint-disable-next-line prefer-arrow-callback
React.forwardRef(function HotLoaderLazyWrapper(props, ref) {
return (
<AppContainer>
<C {...props} ref={ref} />
</AppContainer>
);
}),
),
};
});
target[lazyConstructor].isPatchedByReactHotLoader = true;
}),
);
}
};

export const updateLazy = (target, type) => {
const ctor = type[lazyConstructor];
if (target[lazyConstructor] !== type[lazyConstructor]) {
const ctor = getLazyConstructor(type);
if (getLazyConstructor(target) !== ctor) {
// just execute `import` and RHL.register will do the job
ctor();
}
Expand Down
14 changes: 14 additions & 0 deletions src/webpack/patch.js
Expand Up @@ -59,6 +59,20 @@ const additional = {
'if (current!== null&&hotCompareElements(current.type,element.type,hotUpdateChild(current)))',
],

'17.0.0-rc1': [
'function createFiberFromTypeAndProps(type, // React$ElementType\n' +
'key, pendingProps, owner, mode, lanes) {',
'function createFiberFromTypeAndProps(type, // React$ElementType\n' +
'key, pendingProps, owner, mode, lanes) {type = hotResolveType(type);',
],

'17.0.0-rc1-compact': [
'function createFiberFromTypeAndProps(type,// React$ElementType\n' +
'key,pendingProps,owner,mode,lanes){',
'function createFiberFromTypeAndProps(type,// React$ElementType\n' +
'key,pendingProps,owner,mode,lanes){type = hotResolveType(type);',
],

'16.8-type': [
'function createFiberFromTypeAndProps(type, // React$ElementType\nkey, pendingProps, owner, mode, expirationTime) {',
'function createFiberFromTypeAndProps(type, // React$ElementType\nkey, pendingProps, owner, mode, expirationTime) {type = hotResolveType(type);',
Expand Down
2 changes: 1 addition & 1 deletion test/hot/react-dom.integration.spec.js
Expand Up @@ -432,7 +432,7 @@ describe(`🔥-dom`, () => {

it('support lazy memo forward in Provider', () => {
setConfig({
trackTailUpdates: false,
wrapLazy: false,
});

const spy = jest.fn();
Expand Down
2 changes: 1 addition & 1 deletion test/hot/react-dom.no-integration.spec.js
Expand Up @@ -271,7 +271,7 @@ describe(`🔥-dom`, () => {

it('support lazy memo forward', () => {
setConfig({
trackTailUpdates: false,
wrapLazy: false,
});

const spy = jest.fn();
Expand Down
4 changes: 3 additions & 1 deletion testConfig/setupTests.js
Expand Up @@ -7,9 +7,11 @@ function getAdapter() {
return require('enzyme-adapter-react-15');
} else if (React.version.startsWith('16')) {
return require('enzyme-adapter-react-16');
} else if (React.version.startsWith('17')) {
return require('@wojtekmaj/enzyme-adapter-react-17');
}

return null;
throw new Error('this version of React is not supported by Enzyme');
}

const Adapter = getAdapter();
Expand Down

0 comments on commit c05396b

Please sign in to comment.