React - import component dynamically
In this short article, we would like to show how to import components dynamically in React.
Quick solution:
import React from 'react';
const MyComponent = React.lazy(() => import('./Mycomponent'));
Custom component import logic
This section shows, how to create custom logic that loads indicated component when the first time is used.
That kind of component attaching can be useful when:
- we don't want to load some parts until it are used,
- component logic is located on a different server,
- when we use Server-Side Rendering (SSR), the attached component uses BOM (Browser Object Model, like:
window,location,navigator, etc.) and we want to avoid internal problems withundefinedBOM objects in compilation with NodeJS (SSR in Gatsby/Preact has problems with JS libraries that callswindow,location,nagator, etc.).
Note:
The below example shows how to createuseComponenthook that let us to load component withasyncimport - be sure that building tool is configured to compile separated bundle for that imports.
useComponent.jsx file:
import { useState } from 'react';
let componentPromise = null;
let componentModule = null;
// The method runs component importing.
//
const importComponent = () => {
if (componentModule) {
return Promise.resolve(componentModule);
}
if (componentPromise) {
return componentPromise;
}
componentPromise = import('/path/to/my/component') // change it to something
.then((module) => {
componentModule = module;
return module;
});
return componentPromise;
};
// Uncomment the below line to start module loading as soon as it possible.
//
// prepareComponent();
const useComponent = () => {
const [component, setComponent] = useState(componentModule);
if (component === undefined) {
importComponent()
.then(setComponent) // we just wait until component is ready
.catch(console.error); // we want to see loading exceptions in console
}
return component;
};
export default useComponent;
Usage example:
import React from 'react';
import useComponent from './useComponent';
const App = () => {
const Component = useComponent();
return (
<div>
{Component ? <Component.default /> : <span>Component is not imported yet!</span>}
</div>
);
};
export default App;
Note: change
Component.defaultto proper export name if it is necessary.
Example import names:
| static import | dynamic import |
| import MyComponent from './my-component'; | const Component = useComponent(); const MyComponent = Component.default; |
| import {MyComponent1, MyComponent2} from './my-component'; |
const Component = useComponent(); |
Universal component import logic
In this section, we would like to show how to modify the above logic to import different components with the same logic.
useComponent.jsx file:
import { useState } from 'react';
const statuses = { }; // keeps information about imported modules
// The method runs component importing.
//
export const importComponent = (id, execute) => {
const status = statuses[id] ?? (statuses[id] = {});
if (status.module) {
return Promise.resolve(status.module);
}
if (status.promise) {
return status.promise;
}
status.promise = execute()
.then((module) => {
status.module = module;
return module;
});
return status.promise;
};
const useComponent = (id, execute) => {
const [component, setComponent] = useState(() => {
const status = statuses[id];
return status ? status.module : undefined;
});
if (component === undefined) {
importComponent(execute)
.then(setComponent) // we just wait until component is ready
.catch(console.error); // we want to see loading exceptions in console
}
return component;
};
export default useComponent;
Usage example:
import React from 'react';
import useComponent, {importComponent} from './useComponent';
// Uncomment below line to start module loading as soon as it possible.
//
// importComponent('MyComponent', () => import('/path/to/my/component'));
const App = () => {
const Component = useComponent('MyComponent', () => import('/path/to/my/component')); // change it to something
return (
<div>
{Component ? <Component.default /> : <span>Component is not imported yet!</span>}
</div>
);
};
export default App;
Note:
Some building tools resolves
import('/path/to/component/module')paths that makes impossible to use variables with imports, e.g.import(componentPath)- Webpack has that. This is reasone why we should callimport()with string literal.