From 9fe4cad6ea943f828bf0ad20d821216a39770599 Mon Sep 17 00:00:00 2001 From: Anton Korzunov Date: Fri, 31 May 2019 20:00:03 +1000 Subject: [PATCH] feat: transparent class wrapping, fixes #304 --- src/reactHotLoader.js | 92 +++++++++++++++------------ src/reconciler/componentComparator.js | 4 +- src/webpack/patch.js | 18 +++++- 3 files changed, 70 insertions(+), 44 deletions(-) diff --git a/src/reactHotLoader.js b/src/reactHotLoader.js index 198eb8a7c..23e9f30ce 100644 --- a/src/reactHotLoader.js +++ b/src/reactHotLoader.js @@ -117,53 +117,61 @@ const reactHotLoader = { configuration.showReactDomPatchNotification = false; // console.warn('react-🔥-loader activated.'); } - /* eslint-enable */ - if (!React.createElement.isPatchedByReactHotLoader) { - const originalCreateElement = React.createElement; - // Trick React into rendering a proxy so that - // its state is preserved when the class changes. - // This will update the proxy if it's for a known type. - React.createElement = (type, ...args) => originalCreateElement(resolveType(type), ...args); - React.createElement.isPatchedByReactHotLoader = true; + if (ReactDOM && ReactDOM.setHotTypeResolver) { + console.log('Types 2'); + configuration.intergratedResolver = true; + ReactDOM.setHotTypeResolver(resolveType); } - if (!React.cloneElement.isPatchedByReactHotLoader) { - const originalCloneElement = React.cloneElement; - - React.cloneElement = (element, ...args) => { - const newType = element.type && resolveType(element.type); - if (newType && newType !== element.type) { - return originalCloneElement( - { - ...element, - type: newType, - }, - ...args, - ); - } - return originalCloneElement(element, ...args); - }; + if (!configuration.intergratedResolver) { + /* eslint-enable */ + if (!React.createElement.isPatchedByReactHotLoader) { + const originalCreateElement = React.createElement; + // Trick React into rendering a proxy so that + // its state is preserved when the class changes. + // This will update the proxy if it's for a known type. + React.createElement = (type, ...args) => originalCreateElement(resolveType(type), ...args); + React.createElement.isPatchedByReactHotLoader = true; + } - React.cloneElement.isPatchedByReactHotLoader = true; - } + if (!React.cloneElement.isPatchedByReactHotLoader) { + const originalCloneElement = React.cloneElement; + + React.cloneElement = (element, ...args) => { + const newType = element.type && resolveType(element.type); + if (newType && newType !== element.type) { + return originalCloneElement( + { + ...element, + type: newType, + }, + ...args, + ); + } + return originalCloneElement(element, ...args); + }; - if (!React.createFactory.isPatchedByReactHotLoader) { - // Patch React.createFactory to use patched createElement - // because the original implementation uses the internal, - // unpatched ReactElement.createElement - React.createFactory = type => { - const factory = React.createElement.bind(null, type); - factory.type = type; - return factory; - }; - React.createFactory.isPatchedByReactHotLoader = true; - } + React.cloneElement.isPatchedByReactHotLoader = true; + } + + if (!React.createFactory.isPatchedByReactHotLoader) { + // Patch React.createFactory to use patched createElement + // because the original implementation uses the internal, + // unpatched ReactElement.createElement + React.createFactory = type => { + const factory = React.createElement.bind(null, type); + factory.type = type; + return factory; + }; + React.createFactory.isPatchedByReactHotLoader = true; + } - if (!React.Children.only.isPatchedByReactHotLoader) { - const originalChildrenOnly = React.Children.only; - // Use the same trick as React.createElement - React.Children.only = children => originalChildrenOnly({ ...children, type: resolveType(children.type) }); - React.Children.only.isPatchedByReactHotLoader = true; + if (!React.Children.only.isPatchedByReactHotLoader) { + const originalChildrenOnly = React.Children.only; + // Use the same trick as React.createElement + React.Children.only = children => originalChildrenOnly({ ...children, type: resolveType(children.type) }); + React.Children.only.isPatchedByReactHotLoader = true; + } } if (React.useEffect && !React.useState.isPatchedByReactHotLoader) { diff --git a/src/reconciler/componentComparator.js b/src/reconciler/componentComparator.js index 72ee0c7db..578ce3406 100644 --- a/src/reconciler/componentComparator.js +++ b/src/reconciler/componentComparator.js @@ -5,6 +5,7 @@ import { areSwappable } from './utils'; import { PROXY_KEY, UNWRAP_PROXY } from '../proxy'; import { resolveType } from './resolver'; import logger from '../logger'; +import configuration from '../configuration'; const getInnerComponentType = component => { const unwrapper = component[UNWRAP_PROXY]; @@ -89,8 +90,9 @@ const compareComponents = (oldType, newType, setNewType, baseType) => { const knownPairs = new WeakMap(); const emptyMap = new WeakMap(); -export const hotComponentCompare = (oldType, newType, setNewType, baseType) => { +export const hotComponentCompare = (oldType, preNewType, setNewType, baseType) => { const hotActive = hotComparisonOpen(); + const newType = configuration.intergratedResolver ? resolveType(preNewType) : preNewType; let result = oldType === newType; if ( diff --git a/src/webpack/patch.js b/src/webpack/patch.js index 422058c31..c312ac364 100644 --- a/src/webpack/patch.js +++ b/src/webpack/patch.js @@ -34,6 +34,16 @@ const additional = { 'if (current!== null&¤t.type===element.type)', 'if (current!== null&&hotCompareElements(current.type,element.type,hotUpdateChild(current)))', ], + + '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);', + ], + + '16.8-type-compact': [ + 'function createFiberFromTypeAndProps(type,// React$ElementType\nkey,pendingProps,owner,mode,expirationTime){', + 'function createFiberFromTypeAndProps(type,// React$ElementType\nkey,pendingProps,owner,mode,expirationTime){type = hotResolveType(type);', + ] }; const ReactHotLoaderInjection = ` @@ -45,6 +55,9 @@ var hotUpdateChild = function (child) { } } }; +var hotResolveType = function (type) { + return type; +}; var hotCompareElements = function (oldType, newType) { return oldType === newType }; @@ -79,6 +92,9 @@ var ReactDOM = { setHotElementComparator: function (newComparator) { hotCompareElements = newComparator }, + setHotTypeResolver: function (newResolver) { + hotResolveType = newResolver; + }, `; const defaultEnd = ['var ReactDOM = {', ReactHotLoaderInjection]; @@ -92,7 +108,7 @@ const injectionEnd = { '16.4-compact': defaultEndCompact, }; -const sign = '/* 🔥 this is hot-loader/react-dom 🔥 */'; +const sign = '/* 🔥 this is hot-loader/react-dom 4.8+ 🔥 */'; function additionalTransform(source) { for (const key in additional) {