Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic import in react not working when trying to import a component in another directory

Hello everyone I have been trying dynamic imports in react for rendering my components for an app created with CRA (create-react-app) and while it works perfectly for some cases but for some it returns a cannot load module error for instance I loaded a component(placed in a directory under src) dynamically in my index.js which works fine but when I try to render a child or nested component inside that also with a dynamic import approach it gives error cannot load module. Note this error occurs only if the nested component is placed outside the directory of the original parent component enough talk here is the code.

My index.js placed under src.

    import React, { Component } from 'react';
    import ReactDOM from 'react-dom';
    import './index.css';

    class Dynamic extends Component {
      constructor(props) {
        super(props);
        this.state = { module: null };
      }
      componentDidMount() {
          console.log('in comp mount')
          //alert("in comp mount")
        const { path } = this.props;
        import(`${path}`)
          .then(module => this.setState({ module: module.default }))
     }
      render() {
          console.log('in render')
         // alert("in render")
        const { module: Component } = this.state; // Assigning to new variable names @see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
        return(
          <div>
            {Component && <Component path= '../FooterComp/Footer' />}
          </div>
        )
      }
    }

ReactDOM.render(<Dynamic path='./Components/FirstComponent' />, document.getElementById('root'));

FirstComponent.js placed in Components directory under src.

import React, { Component } from 'react';
import logo from '../logo.svg';
import '../FirstApp.css';

class App extends Component {

    constructor(props) {
    super(props);
    this.state = { module: null };
  }
  componentDidMount() {
      console.log('in comp mount')
      //alert("in comp mount")
    const { path } = this.props;
    alert(path)
    import(`${path}`)
      .then(module => this.setState({ module: module.default }))
 }

  render() {
      const { module: Component } = this.state;
    return (
      <div className="App">
        <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
          <p>
            Edit <code>src/App.js</code> and save to reload.
          </p>
          <a
            className="App-link"
            href="https://reactjs.org"
            target="_blank"
            rel="noopener noreferrer"
          >
            Learn React
          </a>
        </header>
        {Component && <Component />}
      </div>
    );
  }
}

export default App;

Footer.js placed in FooterComp directory under src.

import React, { Component } from 'react';
import '../App.css';

class Footer extends Component {
    componentDidMount()
    {
        console.log('in componentDidMount of Footer')
    }
  render() {
      console.log('in render of Footer')
    return (
      <div className="App">
        <h1>Edited by Me</h1>
      </div>
    );
  }
}

export default Footer;

Why does this work when I refer my firstcomponent from my index.js but doesnt work for footer component when trying to import in my firstcomponent?

Errormessage: Error: Cannot find module '../FooterComp/Footer'

Also note that if i place Footer component in the same directory as Firstcomponent and adjust the path it works fine

like image 314
JayD Avatar asked Jan 18 '19 13:01

JayD


People also ask

How do I import component dynamically into React?

In React, dynamically importing a component is easy—you invoke React. lazy with the standard dynamic import syntax and specify a fallback UI. When the component renders for the first time, React will load that module and swap it in.

How do I import dynamic modules?

To load dynamically a module call import(path) as a function with an argument indicating the specifier (aka path) to a module. const module = await import(path) returns a promise that resolves to an object containing the components of the imported module. } = await import(path);


1 Answers

There’s limitation when using dynamic imports with variable parts.

Webpack Docs

It is not possible to use a fully dynamic import statement, such as import(foo). Because foo could potentially be any path to any file in your system or project.

The import() must contain at least some information about where the module is located. Bundling can be limited to a specific directory or set of files so that when you are using a dynamic expression - every module that could potentially be requested on an import() call is included.For example, import(./locale/${language}.json) will cause every .json file in the ./locale directory to be bundled into the new chunk. At run time, when the variable language has been computed, any file like english.json or german.json will be available for consumption.

In your case, during build time for dynamic import in FirstComponent component, bundling was limited to directory where the FirstComponent component was i.e. Components directory.

What it means is that, webpack will find all the files that are in the Components directory and then create chunks for them. Then at runtime when dynamic import is called, webpack will serve the chunk that corresponds to the value passed in.

Since you passed path= '../FooterComp/Footer' has no corresponding chunk so webpack will throw the error.

This is also same for Dynamic component. If you try to dynamically import with variable parts for the files that are outside src folder, you will get same error.

So to solve this you have couple of options

  1. place both files in same folder

i.e

'src/Components/FirstComponent.js'

'src/Components/Footer.js'

And use

// In FirstComponent.js
   componentDidMount() {
      const { path } = this.props;
      import(`${path}`)
      .then(module => this.setState({ module: module.default }))   
   }


{Component && <Component path='./Footer' />} // Index.js
  1. be more specific as possible

i.e

// In FirstComponent.js
 componentDidMount() {
      const { path } = this.props;
      import(`../FooterComp/${path}`)
      .then(module => this.setState({ module: module.default }))   
 }

And use

{Component && <Component path='Footer' />} //  In index.js
like image 199
Sahil Raj Thapa Avatar answered Oct 10 '22 15:10

Sahil Raj Thapa