Skip to content

Commit 9bcff36

Browse files
committed
feat: cold components
1 parent 2ec2521 commit 9bcff36

File tree

7 files changed

+122
-4
lines changed

7 files changed

+122
-4
lines changed

Diff for: README.md

+24
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,30 @@ console.log(element.type instanceof Component) // true
320320

321321
This is something we did not solve yet.
322322

323+
#### Disabling type change
324+
325+
It is possible to disable React-Hot-Loader for a specific component, especially to
326+
enable common way to type comparison.
327+
328+
```js
329+
import { cold } from 'react-hot-loader';
330+
331+
cold(SomeComponent) // this component will ignored by React-Hot-Loader
332+
<SomeComponent />.type === SomeComponent // true
333+
```
334+
335+
##### Experimental
336+
337+
_Cold_ all components from node_modules. Will not work for HOC or dynamically created Components.
338+
339+
```js
340+
import { setConfig, cold } from 'react-hot-loader'
341+
setConfig({
342+
onComponentRegister: (type, name, file) =>
343+
file.indexOf('node_modules') > 0 && cold(type),
344+
})
345+
```
346+
323347
### Webpack ExtractTextPlugin
324348

325349
Webpack ExtractTextPlugin is not compatible with React Hot Loader. Please disable it in development:

Diff for: src/configuration.js

+8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
const configuration = {
2+
// Log level
23
logLevel: 'error',
4+
5+
// Allows using SFC without changes. leading to some components not updated
36
pureSFC: false,
7+
8+
// Allows SFC to be used, enables "intermediate" components used by Relay, should be disabled for Preact
49
allowSFC: true,
10+
11+
// Hook on babel component register.
12+
onComponentRegister: false,
513
}
614

715
export default configuration

Diff for: src/reactHotLoader.js

+20-2
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,15 @@ import {
77
getProxyByType,
88
getProxyById,
99
createProxyForType,
10+
isTypeBlacklisted,
1011
} from './reconciler/proxies'
12+
import configuration from './configuration'
13+
import logger from './logger'
14+
1115
import { preactAdapter } from './adapters/preact'
1216

1317
function resolveType(type) {
14-
if (!isCompositeComponent(type)) return type
18+
if (!isCompositeComponent(type) || isTypeBlacklisted(type)) return type
1519

1620
const proxy = reactHotLoader.disableProxyCreation
1721
? getProxyByType(type)
@@ -32,11 +36,25 @@ const reactHotLoader = {
3236
const id = `${fileName}#${uniqueLocalName}`
3337

3438
if (getProxyById(id)) {
35-
// component got replaced. Need to reconsile
39+
// component got replaced. Need to reconcile
3640
incrementGeneration()
3741
}
3842

43+
if (configuration.onComponentRegister) {
44+
configuration.onComponentRegister(type, uniqueLocalName, fileName)
45+
}
46+
3947
updateProxyById(id, type)
48+
49+
if (isTypeBlacklisted(type)) {
50+
logger.error(
51+
'React-hot-loader: Cold component',
52+
uniqueLocalName,
53+
'at',
54+
fileName,
55+
'has been updated',
56+
)
57+
}
4058
}
4159
},
4260

Diff for: src/reconciler/proxies.js

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import createProxy from '../proxy'
22
import { resetClassProxies } from '../proxy/createClassProxy'
33

44
let proxiesByID
5+
let blackListedProxies
56
let idsByType
67

78
let elementCount = 0
@@ -33,9 +34,13 @@ export const updateProxyById = (id, type) => {
3334
export const createProxyForType = type =>
3435
getProxyByType(type) || updateProxyById(generateTypeId(), type)
3536

37+
export const isTypeBlacklisted = type => blackListedProxies.has(type)
38+
export const blacklistByType = type => blackListedProxies.set(type, true)
39+
3640
export const resetProxies = () => {
3741
proxiesByID = {}
3842
idsByType = new WeakMap()
43+
blackListedProxies = new WeakMap()
3944
resetClassProxies()
4045
}
4146

Diff for: src/utils.dev.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getProxyByType } from './reconciler/proxies'
1+
import { blacklistByType, getProxyByType } from './reconciler/proxies'
22
import { hotComponentCompare } from './reconciler/hotReplacementRender'
33
import configuration from './configuration'
44

@@ -13,4 +13,6 @@ export const areComponentsEqual = (a, b) =>
1313
export const compareOrSwap = (oldType, newType) =>
1414
hotComponentCompare(oldType, newType)
1515

16+
export const cold = type => blacklistByType(type)
17+
1618
export const setConfig = config => Object.assign(configuration, config)

Diff for: src/utils.prod.js

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export const areComponentsEqual = (a, b) => a === b
22
export const setConfig = () => {}
3+
export const cold = () => {}

Diff for: test/utils.test.js

+61-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import React, { Component } from 'react'
22
import reactHotLoader from '../src/reactHotLoader'
3-
import { areComponentsEqual } from '../src/utils.dev'
3+
import { areComponentsEqual, cold } from '../src/utils.dev'
4+
import configuration from '../src/configuration'
5+
import logger from '../src/logger'
46

57
reactHotLoader.patch(React)
68

9+
jest.mock('../src/logger')
10+
711
describe('utils (dev)', () => {
812
describe('areComponentsEqual', () => {
913
const createClasses = () => {
@@ -63,4 +67,60 @@ describe('utils (dev)', () => {
6367

6468
describe('function based', () => testSuite(createStateless))
6569
})
70+
71+
describe('cold', () => {
72+
it('should disable RHL for "cold" Components', () => {
73+
const Component1 = () => <div>42</div>
74+
const Component2 = () => <div>42</div>
75+
76+
reactHotLoader.register(Component1, 'Class1', 'util.dev')
77+
reactHotLoader.register(Component2, 'Class2', 'util.dev')
78+
79+
cold(Component1)
80+
81+
const element1 = <Component1 />
82+
const element2 = <Component2 />
83+
84+
expect(Component1 === element1.type).toBe(true)
85+
expect(Component2 === element2.type).toBe(false)
86+
87+
expect(areComponentsEqual(Component1, element1.type)).toBe(true)
88+
expect(areComponentsEqual(Component2, element2.type)).toBe(true)
89+
})
90+
91+
it('should components by fileName', () => {
92+
const Component1 = () => <div>42</div>
93+
const Component2 = () => <div>42</div>
94+
95+
configuration.onComponentRegister = (type, name, file) => {
96+
if (file === 'oneFile') cold(type)
97+
}
98+
99+
reactHotLoader.register(Component1, 'Class1', 'oneFile')
100+
reactHotLoader.register(Component2, 'Class2', 'anotherFile')
101+
102+
const element1 = <Component1 />
103+
const element2 = <Component2 />
104+
105+
expect(Component1 === element1.type).toBe(true)
106+
expect(Component2 === element2.type).toBe(false)
107+
108+
expect(areComponentsEqual(Component1, element1.type)).toBe(true)
109+
expect(areComponentsEqual(Component2, element2.type)).toBe(true)
110+
})
111+
112+
it('should report on cold update', () => {
113+
const Component1 = () => <div>42</div>
114+
cold(Component1)
115+
reactHotLoader.register(Component1, 'Cold', 'Winter')
116+
117+
expect(logger.error).toHaveBeenCalledWith(
118+
`React-hot-loader: Cold component`,
119+
'Cold',
120+
'at',
121+
'Winter',
122+
'has been updated',
123+
)
124+
})
125+
})
66126
})

0 commit comments

Comments
 (0)