Uncaught Error: Cannot Find Module When Using Dynamic Import for JavaScript
Asked Answered
J

1

8

I'm using Create-React-App and am looking to use the dynamic import() supported by webpack 2.0 to import a module based on a variable string.

I've looked at the official proposal (https://github.com/tc39/proposal-dynamic-import) and it seems possible to do something like this:

import(`./language-packs/${navigator.language}.js`)

But it breaks when I try something similar.

AppRoutes.js

import LazyLoad from 'services/LazyLoad';

export class AppRoutes extends React.Component {
  render() {
    return (
      <Switch>
        <Route
          exact path="/"
          render={(matchProps) => (
            <LazyLoad
              absoluteModulePath='pages/default/HomePage'
              getComponent={() => import('pages/default/HomePage')}
              {...matchProps}
            />
          )}
        />
      </Switch>
    );
  }
}

export default AppRoutes;

pages/default/HomePage/index.js

import React from 'react';

export const HomePage = () => {
  return (
    <div>
      I'm the default HomePage
    </div>
  );
}

export default HomePage;

BROKEN services/LazyLoad/index.js

import React from 'react';

export class LazyLoad extends React.Component {
  ...

  componentDidMount() {
    import(this.props.absoluteModulePath)  // Critical dependency: the request of a dependency is an expression
      .then(module => module.default)
      .then(AsyncModule => this.setState({AsyncModule}))
  }

  ...
}

export default LazyLoad;

Error:

enter image description here

But when I change the LazyLoader to

WORKING services/LazyLoad/index.js

import React from 'react';

export class LazyLoad extends React.Component {
  ...

  componentDidMount() {
    this.props.getComponent()
      .then(module => module.default)
      .then(AsyncModule => this.setState({AsyncModule}))
  }

  ...
}

export default LazyLoad;

it works.

enter image description here

The absolute paths is something built into create-react-app with the help of environment variables.

.env

NODE_PATH=src/

I require dynamically loading modules this way to build a proof of concept for multi-tenancy. How can I fix the broken LazyLoad such that I can pass a string as a prop and have the LazyLoad component dynamically load the component from that string prop?

Jugendstil answered 24/5, 2017 at 18:50 Comment(5)
Have you tried using './pages/default/HomePage' as a path string instead of pages/default/HomePage?Piers
Just tried it now with ./pages and ../pages and ../../pages and it gives me the same error.Jugendstil
Hmmm, maybe without the period, /pages..., otherwise I don't know. For bundle splitting I use require.ensure within React-Router's getComponent. Don't have experience with lazy-loading via import, sorry.Piers
Have you found that require.ensure works with interpolated strings or expressions rather than plain strings?Jugendstil
Yes I just tried it with a string literal that evaluates correctly. Where are you trying to source this string from? A build environment variable? On client side based on an API call? There's a good example of this here: GithubPiers
D
18

Only partially dynamic statement are allowed for import().

In your AppRoutes.js you could do this:

...
<LazyLoad
    modulePath='HomePage'
    getComponent={() => import('pages/default/HomePage')}
    {...matchProps}
/>

then in your LazyLoad Component you do:

componentDidMount() {
  import(`pages/default/${this.props.modulePath}/index.js`)
    .then(module => module.default)
    .then(AsyncModule => this.setState({AsyncModule}))
}

Fully dynamic statements, such as import(foo), will fail because webpack requires at least some file location information.The import() must contain at least some information about where the module is located, so bundling can be limited to a specific directory or set of files.

https://webpack.js.org/api/module-methods/#import-

Directional answered 12/7, 2017 at 23:58 Comment(1)
Just in case someone wonders, the same rule applies for dynamic require statements.Corycorybant

© 2022 - 2024 — McMap. All rights reserved.