Skip to content

Commit

Permalink
compat mode. fixes #832, breaks #775
Browse files Browse the repository at this point in the history
  • Loading branch information
theKashey committed Jan 29, 2018
1 parent 6508461 commit d911605
Show file tree
Hide file tree
Showing 9 changed files with 104 additions and 9 deletions.
16 changes: 16 additions & 0 deletions docs/Troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,3 +311,19 @@ import * as React from 'react' // React is now patched
import React from 'react'
import { hot } from 'react-hot-loader' // React is now patched
```

#### React-hot-loader: an instance were returned from a render function. Configuration change required

React-hot-loader runs in "compatibility" mode by default. This disables some workarounds you possible might need.

* **Enable** compat mode if you are using something like `react-async-bootstrapper`(react-async-component)
* **Disable** compat mode if you are using `Relay`.

There is **no** way to use them simultaneously.

Buy default RHL will produce more simply and compatible code, but non-compact mode is absolutely legit for React 15+.

```js
import { setConfig } from 'react-hot-loader'
setConfig({ compat: false })
```
14 changes: 10 additions & 4 deletions examples/styled-components/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,27 @@ const SmallText = emoStyled('div')`
const indirect = {
element: () => (
<SmallText>
hidden2 <Counter />
hidden <Counter />
</SmallText>
),
}

const Instanced = () => new Counter()

const aNumber = 10

const App = () => (
<h1>
<BigText>1.Hello, world!! {aNumber} </BigText>
<BigText>1.Hello, world {aNumber} </BigText>
<br />
<SmallText>2.Hello, world---.</SmallText>
<SmallText>2.Hello, world.</SmallText>
<br />
<Counter />
Counter: <Counter /> <br />
Hidden:
<indirect.element />
<br />
Instanced: <Instanced />
<br />
</h1>
)

Expand Down
2 changes: 1 addition & 1 deletion examples/styled-components/src/Counter.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class Counter extends React.Component {
}

render() {
return this.state.count
return <span>1:{this.state.count}</span>
}
}

Expand Down
1 change: 1 addition & 0 deletions packages/react-hot-loader/src/patch.dev.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react'
import reactHotLoader from './reactHotLoader'
import './utils.dev'

reactHotLoader.patch(React)

Expand Down
1 change: 1 addition & 0 deletions packages/react-hot-loader/src/reactHotLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ const reactHotLoader = {

config: {
logLevel: 'error',
compat: true,
},
}

Expand Down
4 changes: 2 additions & 2 deletions packages/react-hot-loader/src/utils.dev.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { setConfig as setProxyConfig } from 'react-stand-in'
import { getProxyByType } from './reconciler/proxies'
import reactHotLoader from './reactHotLoader'
import logger from './logger'
import { setConfig as setProxyConfig } from 'react-stand-in'

setProxyConfig({ logger })
setProxyConfig({ logger, reactHotLoader: reactHotLoader.config })

export const areComponentsEqual = (a, b) =>
getProxyByType(a) === getProxyByType(b)
Expand Down
23 changes: 23 additions & 0 deletions packages/react-hot-loader/test/AppContainer.dev.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { mount } from 'enzyme'
import { mapProps } from 'recompose'
import AppContainer from '../lib/AppContainer.dev'
import RHL from '../lib/reactHotLoader'
import { setConfig } from '../lib/utils.dev'

describe(`AppContainer (dev)`, () => {
beforeEach(() => {
Expand Down Expand Up @@ -1304,9 +1305,30 @@ describe(`AppContainer (dev)`, () => {
expect(wrapper.contains(<div>second</div>)).toBe(true)
})

it('should throw in compat mode', () => {
class CurrentApp extends Component {
render() {
return <div>42</div>
}
}

const IndeterminateComponent = (props, context) =>
new CurrentApp(props, context)

expect(() =>
mount(
<AppContainer>
<IndeterminateComponent n={42} />
</AppContainer>,
).toThrow(),
)
})

it('support indeterminateComponent', () => {
const spy = jest.fn()

setConfig({ compat: false })

const AnotherComponent = () => <div>old</div>

class App extends React.Component {
Expand Down Expand Up @@ -1357,6 +1379,7 @@ describe(`AppContainer (dev)`, () => {
expect(wrapper.text()).toBe('hey 44 new')
expect(spy).toHaveBeenCalledTimes(0) // never gets called

setConfig({ compat: true })
// How it should work
// expect(wrapper.text()).toBe('ho 45 new');
// expect(spy).toHaveBeenCalledTimes(2);
Expand Down
26 changes: 24 additions & 2 deletions packages/react-stand-in/src/createClassProxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
safeDefineProperty,
proxyClassCreator,
} from './utils'
import config from './config'
import { inject, checkLifeCycleMethods, mergeComponents } from './inject'

const has = Object.prototype.hasOwnProperty
Expand Down Expand Up @@ -86,6 +87,21 @@ function createClassProxy(InitialComponent, proxyKey, wrapResult = identity) {
result = CurrentComponent.prototype.render.call(this)
}

if (isReactComponentInstance(result)) {
console.error(
'React-hot-loader: ',
InitialComponent,
'returned an instance',
result,
'as result.',
'You have to disable compat mode to let React-hot-loader handle this.',
'setConfig({compat: false}))',
)
throw new Error(
'React-hot-loader: an instance were returned from a render function. Configuration change required',
)
}

return wrapResult(result)
}

Expand All @@ -102,8 +118,14 @@ function createClassProxy(InitialComponent, proxyKey, wrapResult = identity) {
let ProxyFacade
let ProxyComponent = null

if (!isFunctionalComponent) {
ProxyComponent = proxyClassCreator(InitialComponent, postConstructionAction)
if (
!isFunctionalComponent ||
(config.reactHotLoader && config.reactHotLoader.compat)
) {
ProxyComponent = proxyClassCreator(
isFunctionalComponent ? Component : InitialComponent,
postConstructionAction,
)

defineProxyMethods(ProxyComponent)

Expand Down
26 changes: 26 additions & 0 deletions packages/react-stand-in/test/consistency.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const createFixtures = () => ({
__reactstandin__regenerateByEval(key, code) {
this[key] = eval(code)
}

/* eslint-enable */

render() {
Expand All @@ -35,6 +36,7 @@ const createFixtures = () => ({
__reactstandin__regenerateByEval(key, code) {
this[key] = eval(code)
}

/* eslint-enable */

render() {
Expand All @@ -53,6 +55,7 @@ const createFixtures = () => ({
__reactstandin__regenerateByEval(key, code) {
this[key] = eval(code)
}

/* eslint-enable */

render() {
Expand Down Expand Up @@ -214,6 +217,29 @@ describe('consistency', () => {

expect(Proxy.prototype instanceof Bar).toBe(true)
})

it('should return Class for Stateless component', () => {
const StatelessComponent = () => 42
const proxy = createProxy(StatelessComponent)
const Proxy = proxy.get()

expect(Proxy.prototype instanceof React.Component).toBe(false)
const instance = Proxy() // this is function
expect(instance.render()).toBe(42)
})

it('should return Instance for Stateless component in non compact mode', () => {
setConfig({ reactHotLoader: { compat: true } })
const StatelessComponent = () => 42
const proxy = createProxy(StatelessComponent)
const Proxy = proxy.get()

expect(Proxy.prototype instanceof React.Component).toBe(true)
expect(() => Proxy()).toThrow() // this is class
const instance = new Proxy()
expect(instance.render()).toBe(42)
setConfig({ reactHotLoader: {} })
})
})
})

Expand Down

0 comments on commit d911605

Please sign in to comment.