From 33c54f50c548c69a49e74bfa62f7b5b0d4f03ecc Mon Sep 17 00:00:00 2001 From: Anton Korzunov Date: Mon, 23 Sep 2019 19:04:21 +1000 Subject: [PATCH] fix: autodetect underupdated state and trigger an automatic update, fixes #1342 --- src/configuration.js | 3 +++ src/hot.dev.js | 31 +++++++++++++++++++++----- src/reactHotLoader.js | 5 ++--- src/reconciler/hotReplacementRender.js | 5 ++--- 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/src/configuration.js b/src/configuration.js index a1416ca62..95f19840a 100644 --- a/src/configuration.js +++ b/src/configuration.js @@ -54,6 +54,9 @@ const configuration = { // Global error overlay ErrorOverlay: undefined, + + // react hot dom features enabled + IS_REACT_MERGE_ENABLED: false, }; export const internalConfiguration = { diff --git a/src/hot.dev.js b/src/hot.dev.js index 85f71413b..4bed1da5c 100644 --- a/src/hot.dev.js +++ b/src/hot.dev.js @@ -8,6 +8,7 @@ import { isOpened as isModuleOpened, hotModule, getLastModuleOpened } from './gl import logger from './logger'; import { clearExceptions, logException } from './errorReporter'; import { createQueue } from './utils/runQueue'; +import { enterHotUpdate, getHotGeneration } from './global/generation'; /* eslint-disable camelcase, no-undef */ const requireIndirect = typeof __webpack_require__ !== 'undefined' ? __webpack_require__ : require; @@ -48,6 +49,29 @@ const makeHotExport = (sourceModule, moduleId) => { } const module = hotModule(moduleId); + const deepUpdate = () => { + // force flush all updates + runInRenderQueue(() => { + enterHotUpdate(); + const gen = getHotGeneration(); + module.instances.forEach(inst => inst.forceUpdate()); + + let runLimit = 0; + const checkTailUpdates = () => { + setTimeout(() => { + if (getHotGeneration() !== gen) { + console.warn('React-Hot-Loader has detected a stale state. Updating...'); + deepUpdate(); + } else if (++runLimit < 5) { + checkTailUpdates(); + } + }, 16); + }; + + checkTailUpdates(); + }); + }; + // require all modules runInRequireQueue(() => { try { @@ -58,12 +82,7 @@ const makeHotExport = (sourceModule, moduleId) => { console.error('React-Hot-Loader: error detected while loading', moduleId); console.error(e); } - }).then(() => { - // force flush all updates - runInRenderQueue(() => { - module.instances.forEach(inst => inst.forceUpdate()); - }); - }); + }).then(deepUpdate); }; if (sourceModule.hot) { diff --git a/src/reactHotLoader.js b/src/reactHotLoader.js index dd741a483..e4d50b6b1 100644 --- a/src/reactHotLoader.js +++ b/src/reactHotLoader.js @@ -57,7 +57,6 @@ const hookWrapper = hook => { const noDeps = () => []; const reactHotLoader = { - IS_REACT_MERGE_ENABLED: false, signature(type, key, getCustomHooks = noDeps) { addSignature(type, { key, getCustomHooks }); return type; @@ -75,7 +74,7 @@ const reactHotLoader = { const proxy = getProxyById(id); if (proxy && proxy.getCurrent() !== type) { - if (!reactHotLoader.IS_REACT_MERGE_ENABLED) { + if (!configuration.IS_REACT_MERGE_ENABLED) { if (isTypeBlacklisted(type) || isTypeBlacklisted(proxy.getCurrent())) { logger.error('React-hot-loader: Cold component', uniqueLocalName, 'at', fileName, 'has been updated'); } @@ -146,7 +145,7 @@ const reactHotLoader = { configuration.ignoreSFC = configuration.ignoreSFCWhenInjected; - reactHotLoader.IS_REACT_MERGE_ENABLED = true; + configuration.IS_REACT_MERGE_ENABLED = true; configuration.showReactDomPatchNotification = false; configuration.integratedComparator = true; diff --git a/src/reconciler/hotReplacementRender.js b/src/reconciler/hotReplacementRender.js index 44b822235..b3b26a63d 100644 --- a/src/reconciler/hotReplacementRender.js +++ b/src/reconciler/hotReplacementRender.js @@ -13,7 +13,6 @@ import { isLazyType, isForwardType, } from '../internal/reactUtils'; -import reactHotLoader from '../reactHotLoader'; import logger from '../logger'; import configuration, { internalConfiguration } from '../configuration'; import { areSwappable } from './utils'; @@ -142,7 +141,7 @@ const mergeInject = (a, b, instance) => { } if (flatB.length === 0 && flatA.length === 1 && typeof flatA[0] !== 'object') { // terminal node - } else if (!reactHotLoader.IS_REACT_MERGE_ENABLED) { + } else if (!configuration.IS_REACT_MERGE_ENABLED) { logger.warn(`React-hot-loader: unable to merge `, a, 'and children of ', instance); stackReport(); } @@ -312,7 +311,7 @@ const hotReplacementRender = (instance, stack) => { } if (!stackChild.type[PROXY_KEY]) { - if (!reactHotLoader.IS_REACT_MERGE_ENABLED) { + if (!configuration.IS_REACT_MERGE_ENABLED) { if (isTypeBlacklisted(stackChild.type)) { logger.warn('React-hot-loader: cold element got updated ', stackChild.type); }