11import React from 'react'
22
3- let currentChunks = [ ]
3+ let currentChunks = new Set ( )
4+
5+ export default function dynamicComponent ( p , o ) {
6+ let promise
7+ let options
8+
9+ if ( p instanceof SameLoopPromise ) {
10+ promise = p
11+ options = o || { }
12+ } else {
13+ // Now we are trying to use the modules and render fields in options to load modules.
14+ if ( ! p . modules || ! p . render ) {
15+ const errorMessage = 'Options to `next/dynamic` should contains `modules` and `render` fields.'
16+ throw new Error ( errorMessage )
17+ }
18+
19+ if ( o ) {
20+ const errorMessage = 'Include options in the first argument which contains `modules` and `render` fields.'
21+ throw new Error ( errorMessage )
22+ }
23+
24+ options = p
25+ }
426
5- export default function dynamicComponent ( promise , options = { } ) {
627 return class DynamicComponent extends React . Component {
728 constructor ( ...args ) {
829 super ( ...args )
930
1031 this . LoadingComponent = options . loading ? options . loading : ( ) => ( < p > loading...</ p > )
1132 this . ssr = options . ssr === false ? options . ssr : true
1233
13- this . state = { AsyncComponent : null }
34+ this . state = { AsyncComponent : null , asyncElement : null }
1435 this . isServer = typeof window === 'undefined'
1536
37+ // This flag is used to load the bundle again, if needed
38+ this . loadBundleAgain = null
39+ // This flag keeps track of the whether we are loading a bundle or not.
40+ this . loadingBundle = false
41+
1642 if ( this . ssr ) {
43+ this . load ( )
44+ }
45+ }
46+
47+ load ( ) {
48+ if ( promise ) {
1749 this . loadComponent ( )
50+ } else {
51+ this . loadBundle ( this . props )
1852 }
1953 }
2054
@@ -30,33 +64,95 @@ export default function dynamicComponent (promise, options = {}) {
3064 this . setState ( { AsyncComponent } )
3165 } else {
3266 if ( this . isServer ) {
33- currentChunks . push ( AsyncComponent . __webpackChunkName )
67+ registerChunk ( AsyncComponent . __webpackChunkName )
3468 }
3569 this . state . AsyncComponent = AsyncComponent
3670 }
3771 } )
3872 }
3973
74+ loadBundle ( props ) {
75+ this . loadBundleAgain = null
76+ this . loadingBundle = true
77+
78+ // Run this for prop changes as well.
79+ const modulePromiseMap = options . modules ( props )
80+ const moduleNames = Object . keys ( modulePromiseMap )
81+ let remainingPromises = moduleNames . length
82+ const moduleMap = { }
83+
84+ const renderModules = ( ) => {
85+ if ( this . loadBundleAgain ) {
86+ this . loadBundle ( this . loadBundleAgain )
87+ return
88+ }
89+
90+ this . loadingBundle = false
91+ DynamicComponent . displayName = 'DynamicBundle'
92+ const asyncElement = options . render ( props , moduleMap )
93+ if ( this . mounted ) {
94+ this . setState ( { asyncElement } )
95+ } else {
96+ this . state . asyncElement = asyncElement
97+ }
98+ }
99+
100+ const loadModule = ( name ) => {
101+ const promise = modulePromiseMap [ name ]
102+ promise . then ( ( Component ) => {
103+ if ( this . isServer ) {
104+ registerChunk ( Component . __webpackChunkName )
105+ }
106+ moduleMap [ name ] = Component
107+ remainingPromises --
108+ if ( remainingPromises === 0 ) {
109+ renderModules ( )
110+ }
111+ } )
112+ }
113+
114+ moduleNames . forEach ( loadModule )
115+ }
116+
40117 componentDidMount ( ) {
41118 this . mounted = true
42119 if ( ! this . ssr ) {
43- this . loadComponent ( )
120+ this . load ( )
44121 }
45122 }
46123
124+ componentWillReceiveProps ( nextProps ) {
125+ if ( promise ) return
126+
127+ this . setState ( { asyncElement : null } )
128+
129+ if ( this . loadingBundle ) {
130+ this . loadBundleAgain = nextProps
131+ return
132+ }
133+
134+ this . loadBundle ( nextProps )
135+ }
136+
47137 render ( ) {
48- const { AsyncComponent } = this . state
138+ const { AsyncComponent, asyncElement } = this . state
49139 const { LoadingComponent } = this
50- if ( ! AsyncComponent ) return ( < LoadingComponent { ...this . props } /> )
51140
52- return < AsyncComponent { ...this . props } />
141+ if ( asyncElement ) return asyncElement
142+ if ( AsyncComponent ) return ( < AsyncComponent { ...this . props } /> )
143+
144+ return ( < LoadingComponent { ...this . props } /> )
53145 }
54146 }
55147}
56148
149+ export function registerChunk ( chunk ) {
150+ currentChunks . add ( chunk )
151+ }
152+
57153export function flushChunks ( ) {
58- const chunks = currentChunks
59- currentChunks = [ ]
154+ const chunks = Array . from ( currentChunks )
155+ currentChunks . clear ( )
60156 return chunks
61157}
62158
0 commit comments