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.
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.
The React. lazy function lets you render a dynamic import as a regular component.
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.
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.
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} /> ); }
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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With