import React from 'react' import { getDisplayName } from './utils' let currentChunks = new Set() export default function dynamicComponent (p, o) { let promise let options if (p instanceof SameLoopPromise) { promise = p options = o || {} } else { // Now we are trying to use the modules and render fields in options to load modules. if (!p.modules || !p.render) { const errorMessage = 'Options to `next/dynamic` should contains `modules` and `render` fields.' throw new Error(errorMessage) } if (o) { const errorMessage = 'Include options in the first argument which contains `modules` and `render` fields.' throw new Error(errorMessage) } options = p } return class DynamicComponent extends React.Component { constructor (...args) { super(...args) this.LoadingComponent = options.loading ? options.loading : () => (
loading...
) this.ssr = options.ssr === false ? options.ssr : true this.state = { AsyncComponent: null, asyncElement: null } this.isServer = typeof window === 'undefined' // This flag is used to load the bundle again, if needed this.loadBundleAgain = null // This flag keeps track of the whether we are loading a bundle or not. this.loadingBundle = false if (this.ssr) { this.load() } } load () { if (promise) { this.loadComponent() } else { this.loadBundle(this.props) } } loadComponent () { promise.then((AsyncComponent) => { // Set a readable displayName for the wrapper component const asyncCompName = getDisplayName(AsyncComponent) if (asyncCompName) { DynamicComponent.displayName = `DynamicComponent for ${asyncCompName}` } if (this.mounted) { this.setState({ AsyncComponent }) } else { if (this.isServer) { registerChunk(AsyncComponent.__webpackChunkName) } this.state.AsyncComponent = AsyncComponent } }) } loadBundle (props) { this.loadBundleAgain = null this.loadingBundle = true // Run this for prop changes as well. const modulePromiseMap = options.modules(props) const moduleNames = Object.keys(modulePromiseMap) let remainingPromises = moduleNames.length const moduleMap = {} const renderModules = () => { if (this.loadBundleAgain) { this.loadBundle(this.loadBundleAgain) return } this.loadingBundle = false DynamicComponent.displayName = 'DynamicBundle' const asyncElement = options.render(props, moduleMap) if (this.mounted) { this.setState({ asyncElement }) } else { this.state.asyncElement = asyncElement } } const loadModule = (name) => { const promise = modulePromiseMap[name] promise.then((Component) => { if (this.isServer) { registerChunk(Component.__webpackChunkName) } moduleMap[name] = Component remainingPromises-- if (remainingPromises === 0) { renderModules() } }) } moduleNames.forEach(loadModule) } componentDidMount () { this.mounted = true if (!this.ssr) { this.load() } } componentWillReceiveProps (nextProps) { if (promise) return this.setState({ asyncElement: null }) if (this.loadingBundle) { this.loadBundleAgain = nextProps return } this.loadBundle(nextProps) } render () { const { AsyncComponent, asyncElement } = this.state const { LoadingComponent } = this if (asyncElement) return asyncElement if (AsyncComponent) return (