From a3d5398be5d3b6f9c55b7c6f71ea11b4c34b0cb0 Mon Sep 17 00:00:00 2001 From: Anton Korzunov Date: Wed, 2 May 2018 10:38:15 +1000 Subject: [PATCH] wip: Preact integration --- examples/preact/src/App.js | 13 ++++- examples/preact/src/Counter.js | 4 +- examples/preact/src/hotLoaderSetup.js | 34 +++++++++++- src/proxy/createClassProxy.js | 73 +++++++++++++++----------- src/reconciler/hotReplacementRender.js | 19 ++++++- src/utils.dev.js | 4 ++ 6 files changed, 110 insertions(+), 37 deletions(-) diff --git a/examples/preact/src/App.js b/examples/preact/src/App.js index d96279fa4..f665b5ad5 100644 --- a/examples/preact/src/App.js +++ b/examples/preact/src/App.js @@ -3,12 +3,23 @@ import { hot } from 'react-hot-loader' import Counter from './Counter' import { Internal } from './Internal' +const Inner = ({ children }) =>
{children}
+ +const indirect = { + element: () => ( + + indirect! + + ), +} + const App = () => (

- Hello, world!!
+ Hello, world!

+

) diff --git a/examples/preact/src/Counter.js b/examples/preact/src/Counter.js index 77627f434..1d0276786 100644 --- a/examples/preact/src/Counter.js +++ b/examples/preact/src/Counter.js @@ -4,7 +4,7 @@ import { h, render, Component } from 'preact' /** @jsx h */ class Counter extends Component { - state = { count: 0 } + state = { count: Math.round(Math.random() * 10) } componentDidMount() { this.interval = setInterval( @@ -18,7 +18,7 @@ class Counter extends Component { } render() { - return
10:{this.state.count}
+ return
100:{this.state.count}:1
} } diff --git a/examples/preact/src/hotLoaderSetup.js b/examples/preact/src/hotLoaderSetup.js index 805df1c9d..97f0b0ac3 100644 --- a/examples/preact/src/hotLoaderSetup.js +++ b/examples/preact/src/hotLoaderSetup.js @@ -1,4 +1,34 @@ -import reactHotLoader from 'react-hot-loader' -import preact from 'preact' +import reactHotLoader, { compareOrSwap } from 'react-hot-loader' +import preact, { setComponentComparator } from 'preact' reactHotLoader.inject(preact, 'h') + +setComponentComparator(compareOrSwap) +//(oldC, newC) => compareOrSwap(oldC, Object.getPrototypeOf(newC))); + +// changes to preact + +/* + + +var defaultComp = (a,b) => a===b; + +var componentComparator = defaultComp; + +var compareComponents = (oldComponent,newComponent) => componentComparator(oldC,newC); + +var setComponentComparator = comp => {componentComparator = comp || defaultComp}; + + +// + +return hydrating || compareComponents(node._componentConstructor, vnode.nodeName); + +// + +isDirectOwner = c && compareComponents(dom._componentConstructor, vnode.nodeName), + + + + + */ diff --git a/src/proxy/createClassProxy.js b/src/proxy/createClassProxy.js index 037710967..c951317d0 100644 --- a/src/proxy/createClassProxy.js +++ b/src/proxy/createClassProxy.js @@ -225,40 +225,47 @@ function createClassProxy(InitialComponent, proxyKey, options) { // This function only gets called for the initial mount. The actual // rendered component instance will be the return value. - // eslint-disable-next-line func-names - ProxyFacade = function(props, context) { - const result = CurrentComponent(props, context) - - // simple SFC - if (!CurrentComponent.contextTypes) { - if (!ProxyFacade.isStatelessFunctionalProxy) { - setSFPFlag(ProxyFacade, true) - } - - return renderOptions.componentDidRender(result) - } - setSFPFlag(ProxyFacade, false) - - // This is a Relay-style container constructor. We can't do the prototype- - // style wrapping for this as we do elsewhere, so just we just pass it - // through as-is. - if (isReactComponentInstance(result)) { - ProxyComponent = null - return result - } - - // Otherwise, it's a normal functional component. Build the real proxy - // and use it going forward. + if (1) { ProxyComponent = proxyClassCreator(Component, postConstructionAction) defineProxyMethods(ProxyComponent) + ProxyFacade = ProxyComponent + } else { + // eslint-disable-next-line func-names + ProxyFacade = function(props, context) { + const result = CurrentComponent(props, context) - const determinateResult = new ProxyComponent(props, context) + // simple SFC + if (0 && !CurrentComponent.contextTypes) { + if (!ProxyFacade.isStatelessFunctionalProxy) { + setSFPFlag(ProxyFacade, true) + } - // Cache the initial render result so we don't call the component function - // a second time for the initial render. - determinateResult[CACHED_RESULT] = result - return determinateResult + return renderOptions.componentDidRender(result) + } + setSFPFlag(ProxyFacade, false) + + // This is a Relay-style container constructor. We can't do the prototype- + // style wrapping for this as we do elsewhere, so just we just pass it + // through as-is. + if (isReactComponentInstance(result)) { + ProxyComponent = null + return result + } + + // Otherwise, it's a normal functional component. Build the real proxy + // and use it going forward. + ProxyComponent = proxyClassCreator(Component, postConstructionAction) + + defineProxyMethods(ProxyComponent) + + const determinateResult = new ProxyComponent(props, context) + + // Cache the initial render result so we don't call the component function + // a second time for the initial render. + determinateResult[CACHED_RESULT] = result + return determinateResult + } } } @@ -360,7 +367,13 @@ function createClassProxy(InitialComponent, proxyKey, options) { update(InitialComponent) - proxy = { get, update } + const dereference = () => { + proxies.delete(InitialComponent) + proxies.delete(ProxyFacade) + proxies.delete(CurrentComponent) + } + + proxy = { get, update, dereference } proxies.set(InitialComponent, proxy) proxies.set(ProxyFacade, proxy) diff --git a/src/reconciler/hotReplacementRender.js b/src/reconciler/hotReplacementRender.js index 9c578b70d..eb4fefc16 100644 --- a/src/reconciler/hotReplacementRender.js +++ b/src/reconciler/hotReplacementRender.js @@ -1,6 +1,6 @@ import levenshtein from 'fast-levenshtein' import { PROXY_IS_MOUNTED, PROXY_KEY, UNWRAP_PROXY } from '../proxy' -import { getIdByType, updateProxyById } from './proxies' +import { getIdByType, getProxyByType, updateProxyById } from './proxies' import { updateInstance, getComponentDisplayName, @@ -58,7 +58,7 @@ const haveTextSimilarity = (a, b) => const equalClasses = (a, b) => { const prototypeA = a.prototype - const prototypeB = Object.getPrototypeOf(b.prototype) + const prototypeB = b.prototype // Object.getPrototypeOf(b.prototype) let hits = 0 let misses = 0 @@ -348,6 +348,21 @@ const hotReplacementRender = (instance, stack) => { } } +export const hotComponentCompare = (oldType, newType) => { + if (oldType === newType) { + return true + } + + if (isSwappable(newType, oldType)) { + getProxyByType(newType[UNWRAP_PROXY]()).dereference() + updateProxyById(oldType[PROXY_KEY], newType[UNWRAP_PROXY]()) + updateProxyById(newType[PROXY_KEY], oldType[UNWRAP_PROXY]()) + return true + } + + return false +} + export default (instance, stack) => { try { // disable reconciler to prevent upcoming components from proxying. diff --git a/src/utils.dev.js b/src/utils.dev.js index 5f8e626f9..cc5f77316 100644 --- a/src/utils.dev.js +++ b/src/utils.dev.js @@ -1,4 +1,5 @@ import { getProxyByType } from './reconciler/proxies' +import { hotComponentCompare } from './reconciler/hotReplacementRender' import configuration from './configuration' const getProxyOrType = type => { @@ -9,4 +10,7 @@ const getProxyOrType = type => { export const areComponentsEqual = (a, b) => getProxyOrType(a) === getProxyOrType(b) +export const compareOrSwap = (oldType, newType) => + hotComponentCompare(oldType, newType) + export const setConfig = config => Object.assign(configuration, config)