Skip to content

Commit 3c21ffa

Browse files
V4.0.0 (#176)
* #129 * Change getDerivedStateFromProps to componentDidUpdate * Change getDerivedStateFromProps to componentDidUpdate * HMR now works * All errors fixed. All tests run successfull. Before polishing. * Polish * Moving chunk name resolution back to slashes * new 4v branch * merges and bumping version * adding codeowners * usesBabelPlugin added back * feat: Updated RUC to react 16.3 and up Moving away from deprecated willMount and to the non-deprecated lifecycle BREAKING CHANGE: Only Compatible with React 16.3 and up * fix: ignore babel rename by default * flow types * updated yarn locks
1 parent 3eeaa58 commit 3c21ffa

File tree

7 files changed

+2308
-917
lines changed

7 files changed

+2308
-917
lines changed

.github/CODEOWNERS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# babel-plugin-universal-import maintainers
2+
* @ScriptedAlchemy

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ node_modules
44
*.log
55
.idea
66
.DS_Store
7+
.history

package.json

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
{
22
"name": "react-universal-component",
3-
"version": "0.0.0-development",
3+
"version": "4.0.0-alpha.4",
44
"description": "A higher order component for loading components with promises",
55
"main": "dist/index.js",
66
"typings": "index.d.ts",
77
"author": "James FaceySpacey Gillmore <[email protected]> (http://www.faceyspacey.com)",
88
"contributors": [
9-
"Zack Jackson <zackary.l.jackson@gmail.com> (https://github.com/ScriptedAlchemy)"
9+
"Zack Jackson <zack@ScriptedAlchemy.com> (https://github.com/ScriptedAlchemy)"
1010
],
1111
"license": "MIT",
1212
"bugs": {
@@ -65,15 +65,15 @@
6565
"jest": "^20.0.4",
6666
"lint-staged": "^7.2.0",
6767
"prettier": "^1.3.1",
68-
"react": "^16.4.1",
69-
"react-test-renderer": "^16.4.1",
68+
"react": "^16.4.2",
69+
"react-test-renderer": "^16.4.2",
7070
"rimraf": "^2.5.4",
7171
"semantic-release": "^6.3.6",
7272
"slash": "^1.0.0",
7373
"travis-github-status": "^1.6.3"
7474
},
7575
"peerDependencies": {
76-
"react": "*"
76+
"react": "^16.3.0"
7777
},
7878
"config": {
7979
"commitizen": {
@@ -93,6 +93,7 @@
9393
},
9494
"dependencies": {
9595
"hoist-non-react-statics": "^2.2.1",
96-
"prop-types": "^15.5.10"
96+
"prop-types": "^15.5.10",
97+
"react-hot-loader": "^4.3.6"
9798
}
9899
}

src/helpers.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import hoist from 'hoist-non-react-statics'
2+
import UniversalComponent from './index'
3+
4+
export const __update = (
5+
props,
6+
state,
7+
isInitialized,
8+
isMount = false,
9+
isSync = false,
10+
isServer = false
11+
) => {
12+
if (!isInitialized) return state
13+
if (!state.error) {
14+
state.error = null
15+
}
16+
return __handleAfter(props, state, isMount, isSync, isServer)
17+
}
18+
19+
/* eslint class-methods-use-this: ["error", { "exceptMethods": ["__handleAfter"] }] */
20+
export const __handleAfter = (props, state, isMount, isSync, isServer) => {
21+
const { mod, error } = state
22+
23+
if (mod && !error) {
24+
hoist(UniversalComponent, mod, {
25+
preload: true,
26+
preloadWeak: true
27+
})
28+
29+
if (props.onAfter) {
30+
const { onAfter } = props
31+
const info = { isMount, isSync, isServer }
32+
onAfter(info, mod)
33+
}
34+
}
35+
else if (error && props.onError) {
36+
props.onError(error)
37+
}
38+
39+
return state
40+
}

src/index.js

Lines changed: 131 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
import React from 'react'
33
import PropTypes from 'prop-types'
44
import hoist from 'hoist-non-react-statics'
5+
import { Context } from 'vm'
56
import req from './requireUniversalModule'
6-
77
import type {
88
Config,
99
ConfigFunc,
@@ -19,6 +19,7 @@ import {
1919
createDefaultRender,
2020
isServer
2121
} from './utils'
22+
import { __update } from './helpers'
2223

2324
export { CHUNK_NAMES, MODULE_IDS } from './requireUniversalModule'
2425
export { default as ReportChunks } from './report-chunks'
@@ -48,7 +49,7 @@ export default function universal<Props: Props>(
4849
...options
4950
} = opts
5051

51-
const render = userRender || createDefaultRender(Loading, Err)
52+
const renderFunc = userRender || createDefaultRender(Loading, Err)
5253

5354
const isDynamic = hasBabelPlugin || testBabelPlugin
5455
options.isDynamic = isDynamic
@@ -58,7 +59,7 @@ export default function universal<Props: Props>(
5859

5960
return class UniversalComponent extends React.Component<void, Props, *> {
6061
/* eslint-disable react/sort-comp */
61-
_mounted: boolean
62+
_initialized: boolean
6263
_asyncOnly: boolean
6364

6465
state: State
@@ -112,103 +113,22 @@ export default function universal<Props: Props>(
112113
report: PropTypes.func
113114
}
114115

115-
constructor(props: Props, context: {}) {
116-
super(props, context)
117-
this.state = { error: null }
118-
}
119-
120-
componentWillMount() {
121-
this._mounted = true
122-
123-
const { addModule, requireSync, requireAsync, asyncOnly } = req(
124-
asyncModule,
125-
options,
126-
this.props
127-
)
128-
129-
let mod
130-
131-
try {
132-
mod = requireSync(this.props, this.context)
133-
}
134-
catch (error) {
135-
return this.update({ error })
136-
}
137-
138-
this._asyncOnly = asyncOnly
139-
const chunkName = addModule(this.props) // record the module for SSR flushing :)
140-
141-
if (this.context.report) {
142-
this.context.report(chunkName)
143-
}
144-
145-
if (mod || isServer) {
146-
this.handleBefore(true, true, isServer)
147-
this.update({ mod }, true, true, isServer)
148-
return
149-
}
150-
151-
this.handleBefore(true, false)
152-
this.requireAsync(requireAsync, this.props, true)
153-
}
154-
155-
componentWillUnmount() {
156-
this._mounted = false
157-
}
158-
159-
componentWillReceiveProps(nextProps: Props) {
160-
if (isDynamic || this._asyncOnly) {
161-
const { requireSync, requireAsync, shouldUpdate } = req(
162-
asyncModule,
163-
options,
164-
nextProps,
165-
this.props
166-
)
167-
168-
if (shouldUpdate(nextProps, this.props)) {
169-
let mod
170-
171-
try {
172-
mod = requireSync(nextProps, this.context)
173-
}
174-
catch (error) {
175-
return this.update({ error })
176-
}
177-
178-
this.handleBefore(false, !!mod)
179-
180-
if (!mod) {
181-
return this.requireAsync(requireAsync, nextProps)
182-
}
183-
184-
const state = { mod }
185-
186-
if (alwaysDelay) {
187-
if (loadingTransition) this.update({ mod: null }) // display `loading` during componentWillReceiveProps
188-
setTimeout(() => this.update(state, false, true), minDelay)
189-
return
190-
}
191-
192-
this.update(state, false, true)
193-
}
194-
else if (isHMR()) {
195-
const mod = requireSync(nextProps, this.context)
196-
this.setState({ mod: () => null }) // HMR /w Redux and HOCs can be finicky, so we
197-
setTimeout(() => this.setState({ mod })) // toggle components to insure updates occur
198-
}
199-
}
200-
}
201-
202-
requireAsync(requireAsync: RequireAsync, props: Props, isMount?: boolean) {
203-
if (this.state.mod && loadingTransition) {
204-
this.update({ mod: null }) // display `loading` during componentWillReceiveProps
116+
requireAsyncInner(
117+
requireAsync: RequireAsync,
118+
props: Props,
119+
state: State,
120+
context: Context,
121+
isMount?: boolean
122+
) {
123+
if (!state.mod && loadingTransition) {
124+
this.update({ mod: null, props }) // display `loading` during componentWillReceiveProps
205125
}
206126

207127
const time = new Date()
208128

209-
requireAsync(props, this.context)
129+
requireAsync(props, context)
210130
.then((mod: ?any) => {
211-
const state = { mod }
131+
const state = { mod, props, context }
212132

213133
const timeLapsed = new Date() - time
214134
if (timeLapsed < minDelay) {
@@ -218,7 +138,7 @@ export default function universal<Props: Props>(
218138

219139
this.update(state, isMount)
220140
})
221-
.catch(error => this.update({ error }))
141+
.catch(error => this.update({ error, props, context }))
222142
}
223143

224144
update = (
@@ -227,7 +147,7 @@ export default function universal<Props: Props>(
227147
isSync?: boolean = false,
228148
isServer?: boolean = false
229149
) => {
230-
if (!this._mounted) return
150+
if (!this._initialized) return
231151
if (!state.error) state.error = null
232152

233153
this.handleAfter(state, isMount, isSync, isServer)
@@ -271,11 +191,124 @@ export default function universal<Props: Props>(
271191

272192
this.setState(state)
273193
}
194+
// $FlowFixMe
195+
init(props, context) {
196+
const { addModule, requireSync, requireAsync, asyncOnly } = req(
197+
asyncModule,
198+
options,
199+
props
200+
)
201+
202+
let mod
203+
204+
try {
205+
mod = requireSync(props, context)
206+
}
207+
catch (error) {
208+
return __update(props, { error, props, context }, this._initialized)
209+
}
210+
211+
this._asyncOnly = asyncOnly
212+
const chunkName = addModule(props) // record the module for SSR flushing :)
213+
214+
if (context.report) {
215+
context.report(chunkName)
216+
}
217+
218+
if (mod || isServer) {
219+
this.handleBefore(true, true, isServer)
220+
return __update(
221+
props,
222+
{ asyncOnly, props, mod, context },
223+
this._initialized,
224+
true,
225+
true,
226+
isServer
227+
)
228+
}
229+
230+
this.handleBefore(true, false)
231+
this.requireAsyncInner(
232+
requireAsync,
233+
props,
234+
{ props, asyncOnly, mod, context },
235+
context,
236+
true
237+
)
238+
return { mod, asyncOnly, context, props }
239+
}
240+
241+
constructor(props: Props, context: {}) {
242+
super(props, context)
243+
this.state = this.init(this.props, this.context)
244+
// $FlowFixMe
245+
this.state.error = null
246+
}
247+
248+
static getDerivedStateFromProps(nextProps, currentState) {
249+
const { requireSync, shouldUpdate } = req(
250+
asyncModule,
251+
options,
252+
nextProps,
253+
currentState.props
254+
)
255+
if (isHMR() && shouldUpdate(currentState.props, nextProps)) {
256+
const mod = requireSync(nextProps, currentState.context)
257+
return { ...currentState, mod }
258+
}
259+
return null
260+
}
261+
262+
componentDidMount() {
263+
this._initialized = true
264+
}
265+
266+
componentDidUpdate(prevProps: Props) {
267+
if (isDynamic || this._asyncOnly) {
268+
const { requireSync, requireAsync, shouldUpdate } = req(
269+
asyncModule,
270+
options,
271+
this.props,
272+
prevProps
273+
)
274+
275+
if (shouldUpdate(this.props, prevProps)) {
276+
let mod
277+
278+
try {
279+
mod = requireSync(this.props, this.context)
280+
}
281+
catch (error) {
282+
return this.update({ error })
283+
}
284+
285+
this.handleBefore(false, !!mod)
286+
287+
if (!mod) {
288+
return this.requireAsyncInner(requireAsync, this.props, { mod })
289+
}
290+
291+
const state = { mod }
292+
293+
if (alwaysDelay) {
294+
if (loadingTransition) this.update({ mod: null }) // display `loading` during componentWillReceiveProps
295+
setTimeout(() => this.update(state, false, true), minDelay)
296+
return
297+
}
298+
299+
this.update(state, false, true)
300+
}
301+
}
302+
}
303+
304+
componentWillUnmount() {
305+
this._initialized = false
306+
}
274307

275308
render() {
276309
const { isLoading, error: userError, ...props } = this.props
277310
const { mod, error } = this.state
278-
return render(props, mod, isLoading, userError || error)
311+
return renderFunc(props, mod, isLoading, userError || error)
279312
}
280313
}
281314
}

src/requireUniversalModule.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ const getConfig = (
227227
chunkName: options.chunkName || 'default',
228228
resolve: options.resolve || '',
229229
path: options.path || '',
230-
load
230+
load,
231+
ignoreBabelRename: true
231232
}
232233
}

0 commit comments

Comments
 (0)