简体   繁体   English

使用React Loadable封装在HOC中的代码拆分路由组件

[英]Code splitting route components wrapped in a HOC with React Loadable

I am running into problems using React Loadable with route based code splitting using Webpack 3.11. 我在使用React Loadable和使用Webpack 3.11进行基于路由的代码拆分时遇到了问题。

When I try to render my app on the server my async modules immediately resolve without waiting for the promise. 当我尝试在服务器上呈现我的应用程序时,我的异步模块会立即解析,而无需等待承诺。 Thus the SSR output becomes <div id="root"></div> . 因此,SSR输出变为<div id="root"></div>

App.js: App.js:

const App = () => (
  <Switch>
    {routes.map((route, index) => (
      <Route key={index} path={route.path} render={routeProps => {
        const RouteComponent = route.component
        return <RouteComponent {...routeProps} />
      }} />
    ))}
  </Switch>
)

I've defined my async route components with React Loadable like this: 我已经用React Loadable定义了异步路由组件,如下所示:

Routes.js Routes.js

function Loading ({ error }) {
  if (error) {
    return 'Oh nooess!'
  } else {
    return <h3>Loading...</h3>
  }
}

const Article = Loadable({
  loader: () => import(/* webpackChunkName: "Article" */ '../components/contentTypes/Article'),
  loading: Loading
})

const Page = Loadable({
  loader: () => import(/* webpackChunkName: "Page" */ '../components/contentTypes/Page'),
  loading: Loading,
  render (loaded, props) {
    let Component = WithSettings(loaded.default)
    return <Component {...props}/>
  }
})

export default [
  {
    path: `/:projectSlug/:env${getEnvironments()}/article/:articleSlug`,
    component: Article,
    exact: true
  },
  {
    path: `/:projectSlug/:env${getEnvironments()}/:menuSlug?/:pageSlug?`,
    component: Page
  }
]

WithSettings.js WithSettings.js

export default (WrappedComponent: any) => {
  class WithSettings extends React.Component<WithSettingsProps, WithSettingsState> {
    static displayName = `WithSettings(${WrappedComponent.displayName || WrappedComponent.name || 'Component'})`

    state = {
      renderWidth: 1200
    }

    componentDidMount () {
      this.loadSettings({ match: { params: { projectSlug: '', env: '' } } })
      window.addEventListener('resize', this.onResize)
      this.onResize()
    }

    componentWillUnmount () {
      if (isClient) {
        window.removeEventListener('resize', this.onResize)
      }
    }

    componentDidUpdate (oldProps) {
      this.loadSettings(oldProps)
    }

    onResize = () => {
      this.setState({ renderWidth: this.getLayoutWidth() })
    }

    getLayoutWidth () {
      return (document.body && document.body.offsetWidth) || 0
    }

    loadSettings (oldProps) {
      const { settings, request, getNewSettings } = this.props
      const { projectSlug: oldProjectSlug, env: oldEnv } = oldProps.match.params
      const { projectSlug: newProjectSlug, env: newEnv } = this.props.match.params

      if (
        (
          oldProjectSlug !== newProjectSlug ||
          oldEnv !== newEnv
        ) ||
        (
          settings === undefined ||
          (request.networkStatus === 'ready')
        )
      ) {
        getNewSettings()
      }
    }

    render () {
      const { settings, request, history, location, match } = this.props
      const { renderWidth } = this.state

      if (!settings || !request || request.networkStatus === 'loading') {
        return <div />
      }

      if (request.networkStatus === 'failed') {
        return <ErrorBlock {...getErrorMessages(match.params, 'settings')[4044]} fullscreen match={match} />
      }

      return (
        <WrappedComponent
          settings={settings}
          settingsRequest={request}
          history={history}
          location={location}
          match={match}
          renderWidth={renderWidth}
        />
      )
    }
  }

  hoistNonReactStatic(WithSettings, WrappedComponent)

  return connect(mapStateToProps, mapDispatchToProps)(WithSettings)
}

I've managed to narrow it down to the WithSettings HOC that I am using to wrap my route components in. If I don't use the WithSettings HOC (as with the Article route) then my SSR output waits for the async import to complete, and the server generated html includes markup related to the route (good!). 我已经成功地缩小它的WithSettings我使用包在我的路线组件HOC。如果我不使用WithSettings HOC(与第二条路线),那么我的SSR输出等待异步进口完成,并且服务器生成的html包含与路由相关的标记(好!)。 If I do use the HOC (as with the Page route) then the module immediately resolves and my SSR output turns into <div id="root"></div because it no longer waits for the dynamic import to complete before rendering. 如果我确实使用HOC(与Page路由一样),则该模块会立即解析,并且我的SSR输出变为<div id="root"></div因为它不再等待动态导入完成才渲染。 Problem is: I need the WithSettings HOC in all my routes as it fetches required data from the server that I need to render the app. 问题是:我需要在所有路由中使用WithSettings HOC,因为它会从服务器中获取渲染应用程序所需的数据。

Can anyone tell me how I can use a HOC and use React Loadable's async component for route components so that it works on the server? 谁能告诉我如何使用HOC 并将 React Loadable的async组件用于路由组件,从而使其在服务器上工作?

Managed to figure it out. 设法弄清楚了。

It was due to my HOC. 这是由于我的HOC。 In the render method it would return <div /> when settings or request where undefined or request.networkStatus is loading . 在render方法中,当settingsrequest loading未定义或request.networkStatus时,它将返回<div /> It turns out this tripped up server side rendering. 事实证明,这跳了服务器端渲染。 Removing this block was enough to make it work. 删除该块足以使其正常工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM