We use react and react-loadable.
During our application initialization, we are verifying that the component.preload
method is existing for each <Route />
we define.
If the method is missing, we display a warning that show that the component should be loadable.
We use webpack 4, is there a way to automatically wrap the component, so we don't have to do it manually?
This is how a component look like:
/** MyComponent.js: page component */
export default () => <div>Hello world</div>;
This is the same component wrapped in a react-loadable component:
/**
* preconfigured react-loadable
* See https://github.com/jamiebuilds/react-loadable#how-do-i-avoid-repetition)
*/
import MyLoadable from '@scopped/react-loadable';
/** loadable component */
export default MyLoadable({
loader: () => import('./MyComponent'), /** import page component */
});
<Route />
are declared in node_modules
and from within different packages. <Resource />
(from react-admin) instead of <Route />
I'm not sure this is the right way to do it but maybe you can write some kind of webpack loader that would preprocess your files, find <Route />
patterns in your files, identify the path of the components they render and transform them into loadable components with that information.
This is a bit hacky but it should work (Only with imports but you can tweak it as you want to match your requirements):
Webpack config:
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: [
"babel-loader", // Rest of your loaders
path.resolve(__dirname, 'path/to/your/loader.js')
]
}
}
loader.js:
module.exports = function (source) {
const routeRegex = new RegExp(/<Route.*component={(.*)}.*\/>/g);
let matches;
let components = [];
while (matches = routeRegex.exec(source)) {
components.push(matches[1]); // Get all the component import names
}
// Replace all import lines by a MyLoadable lines
components.forEach((component) => {
const importRegex = new RegExp(`import ${component} from '(.*)'`);
const path = importRegex.exec(source)[1];
source = source.replace(importRegex, `
const ${component} = MyLoadable({
loader: () => import('${path}')
});
`);
});
source = `
import MyLoadable from './MyLoadable';
${source}
`;
return source;
};
This is definitely hacky but if you stick to convention this could work. It transforms this kind of file:
import Page1 from './Page1';
import Page2 from './Page2';
export default () => (
<Switch>
<Route path='/page1' component={Page1} />
<Route path='/page2' component={Page2} />
</Switch>
);
into this file:
import MyLoadable from './MyLoadable;
const Page1 = MyLoadable({
loader: () => import('./Page1')
});
const Page2 = MyLoadable({
loader: () => import('./Page2')
});
export default () => (
<Switch>
<Route path='/page1' component={Page1} />
<Route path='/page2' component={Page2} />
</Switch>
);
This example has some problems (the path to MyLoadable
should be absolute, it works only when Page components are imported, loadable components are not in a separate file and this could lead to duplicates, ...) but you get the idea