Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic loading of react components

I need to load a react component dynamically.

I get name of the component to load as a string from user. I'm using webpack.

How do I load the component dynamically instead of having a static import statement. It seems Require.Ensure doesn't evaluate expressions . What I want to achieve is something like this.

require.ensure([ "./widgets/" + componentName ] ,(require) => {     let Component = require("./widgets/" + componentName);    }); 

But this doesn't seem to work.

like image 563
madCode Avatar asked Apr 30 '16 08:04

madCode


People also ask

How do you dynamically load component in 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.

What is lazy () in React?

The React. lazy function lets you render a dynamic import as a regular component.

What is suspense and lazy in React?

Suspense is a component for wrapping lazy components. You can wrap multiple lazy components at different hierarchy levels with a single Suspense component. The Suspense component takes a fallback prop that accepts the React elements you want rendered as placeholder content while all the lazy components get loaded.

What is dynamic rendering React?

Dynamic rendering is the process of serving content based on the user agent that calls it. This means serving a client-side rendered version of your site for human users and a separate, server-side version for search engines.


2 Answers

Basically it boils down to pre-creating all the chunks you will ever need. Then you just need a way to dynamically refer to them. Here's the solution I based mine on:

http://henleyedition.com/implicit-code-splitting-with-react-router-and-webpack

and here's what I do since I don't use React Router (side note: i dont find it to be a good match for redux or animations):

//loader: {   test: (folder)\/.*\.js,   include: path.resolve(__dirname, 'src')   loader: ['lazy?bundle', 'babel'] }  //dynamic usage within React component: const My_COMPONENTS = {    ComponentA: require('./folder/ComponentA'),    ComponentB: require('./folder/ComponentB'), }  class ParentComponent extends React.Component {     componentDidMount() {         My_COMPONENTS[this.props.name](component => this.setState({component}));     }      render() {        return <this.state.component />;     } } 

So the result is you are dynamically rendering a component, but from a static pre-determined set of possibilities--all while, only sending no more to the client than the chunks the visitor is actually interested in.

ALSO, here's a component I have that does this well:

import React from 'react'; import Modal from './widgets/Modal';  export default class AjaxModal extends React.Component {   constructor(props, context) {     super(props, context);     this.state = {       Content: null     };   }    componentDidMount() {     if(this.props.show) {       this.loadContent();     }   }    componentWillReceiveProps({show}) {     if(show && !this.state.Content) {       this.loadContent(1200); //dont interfere with animation     }   }    loadContent(ms=0) {     setTimeout(() => {       this.props.requestLazyBundle(({default: Content}) => {         this.setState({Content});       });     }, ms);   }    render() {     let {Content} = this.state;      return (       <Modal title={this.props.title} {...this.props} loading={!Content}>         {Content ? <Content /> : null}       </Modal>     );   } } 

pass pass an async require bundler function as this.props.requestLazybundle, like this:

render() {    let requestLazyBundle = require('bundle?lazy&name=AnotherComponent!../content/AnotherComponent');    return (     <AjaxModal title='Component Name' {...props} requestLazyBundle={requestLazyBundle} />   ); } 
like image 57
faceyspacey.com Avatar answered Sep 21 '22 02:09

faceyspacey.com


Please have a look at this gist page that I have provided for the full source code https://gist.github.com/SamanShafigh/a0fbc2483e75dc4d6f82ca534a6174d4

So let assume you have 4 components called D1, D2, D3. What you need is to create a dependency injection and a dependency container mechanism. This is a very simple implementation

Imagine you have a config file like this that defines your components

export default [   {     name:'D1',     path:'D1'   },   {     name:'D2',     path:'D2'   },   {     name:'D3',     path:'D3' }]; 

Then you can have a component container something like this

import componentsConfig from 'ComponentsConfig';  let components = {};  for (var i = 0; i < componentsConfig.length; i++) {   let componentConfig = componentsConfig[i];   // Check if component is not already loaded then load it   if (components[componentConfig.name] === undefined) {     components[componentConfig.name] = require(`${componentConfig.path}`).default;   } }  export default components; 

Finally in the place you want to load your components you can use your component container to load your components dynamically or in other word you can inject your components

import React, { Component } from 'react'; import ComponentContainer from './ComponentContainer';  class App extends Component {   render() {     let components = ['D1', 'D2', 'D3'];      return (       <div>         <h2>Dynamic Components Loading</h2>         {components.map((componentId) => {           let Component = ComponentContainer[componentId];           return <Component>{componentId}</Component>;         })}       </div>     );   } }  export default App; 
like image 32
Saman Avatar answered Sep 22 '22 02:09

Saman