Merge pull request #6768 from tchak/refactor-js-imports

refactor(js): use dynamic import
This commit is contained in:
Paul Chavard 2021-12-21 18:10:46 +00:00 committed by GitHub
commit 52750f1800
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 140 additions and 77 deletions

View file

@ -1,24 +0,0 @@
import React, { Suspense, lazy } from 'react';
import PropTypes from 'prop-types';
const Loader = () => <div className="spinner left" />;
function LazyLoad({ component: Component, ...props }) {
return (
<Suspense fallback={<Loader />}>
<Component {...props} />
</Suspense>
);
}
LazyLoad.propTypes = {
component: PropTypes.object
};
export default function Loadable(loader) {
const LazyComponent = lazy(loader);
return function PureComponent(props) {
return <LazyLoad component={LazyComponent} {...props} />;
};
}

View file

@ -1,3 +0,0 @@
import Loadable from '../components/Loadable';
export default Loadable(() => import('../components/Chartkick'));

View file

@ -1,3 +0,0 @@
import Loadable from '../components/Loadable';
export default Loadable(() => import('../components/ComboAdresseSearch'));

View file

@ -1,5 +0,0 @@
import Loadable from '../components/Loadable';
export default Loadable(() =>
import('../components/ComboAnnuaireEducationSearch')
);

View file

@ -1,3 +0,0 @@
import Loadable from '../components/Loadable';
export default Loadable(() => import('../components/ComboCommunesSearch'));

View file

@ -1,3 +0,0 @@
import Loadable from '../components/Loadable';
export default Loadable(() => import('../components/ComboDepartementsSearch'));

View file

@ -1,5 +0,0 @@
import Loadable from '../components/Loadable';
export default Loadable(() =>
import('../components/ComboMultipleDropdownList')
);

View file

@ -1,3 +0,0 @@
import Loadable from '../components/Loadable';
export default Loadable(() => import('../components/ComboPaysSearch'));

View file

@ -1,3 +0,0 @@
import Loadable from '../components/Loadable';
export default Loadable(() => import('../components/ComboRegionsSearch'));

View file

@ -1,3 +0,0 @@
import Loadable from '../components/Loadable';
export default Loadable(() => import('../components/MapEditor'));

View file

@ -1,3 +0,0 @@
import Loadable from '../components/Loadable';
export default Loadable(() => import('../components/MapReader'));

View file

@ -1,3 +0,0 @@
import Loadable from '../components/Loadable';
export default Loadable(() => import('../components/Trix'));

View file

@ -1,3 +0,0 @@
import Loadable from '../components/Loadable';
export default Loadable(() => import('../components/TypesDeChampEditor'));

View file

@ -2,7 +2,6 @@ import '../shared/polyfills';
import Rails from '@rails/ujs';
import * as ActiveStorage from '@rails/activestorage';
import 'whatwg-fetch'; // window.fetch polyfill
import ReactRailsUJS from 'react_ujs';
import '../shared/page-update-event';
import '../shared/activestorage/ujs';
@ -48,6 +47,38 @@ import {
showNewAccountPasswordConfirmation
} from '../new_design/fc-fusion';
import {
registerReactComponents,
Loadable
} from '../shared/register-react-components';
registerReactComponents({
Chartkick: Loadable(() => import('../components/Chartkick')),
ComboAdresseSearch: Loadable(() =>
import('../components/ComboAdresseSearch')
),
ComboAnnuaireEducationSearch: Loadable(() =>
import('../components/ComboAnnuaireEducationSearch')
),
ComboCommunesSearch: Loadable(() =>
import('../components/ComboCommunesSearch')
),
ComboDepartementsSearch: Loadable(() =>
import('../components/ComboDepartementsSearch')
),
ComboMultipleDropdownList: Loadable(() =>
import('../components/ComboMultipleDropdownList')
),
ComboPaysSearch: Loadable(() => import('../components/ComboPaysSearch')),
ComboRegionsSearch: Loadable(() =>
import('../components/ComboRegionsSearch')
),
MapEditor: Loadable(() => import('../components/MapEditor')),
MapReader: Loadable(() => import('../components/MapReader')),
Trix: Loadable(() => import('../components/Trix')),
TypesDeChampEditor: Loadable(() => import('../components/TypesDeChampEditor'))
});
// This is the global application namespace where we expose helpers used from rails views
const DS = {
fire: (eventName, data) => Rails.fire(document, eventName, data),
@ -69,7 +100,3 @@ ActiveStorage.start();
// Expose globals
window.DS = window.DS || DS;
// eslint-disable-next-line no-undef, react-hooks/rules-of-hooks
ReactRailsUJS.useContext(require.context('loaders', true));
addEventListener('ds:page:update', ReactRailsUJS.handleMount);

View file

@ -0,0 +1,108 @@
import React, { Suspense, lazy, createElement } from 'react';
import { render } from 'react-dom';
// This attribute holds the name of component which should be mounted
// example: `data-react-class="MyApp.Items.EditForm"`
const CLASS_NAME_ATTR = 'data-react-class';
// This attribute holds JSON stringified props for initializing the component
// example: `data-react-props="{\"item\": { \"id\": 1, \"name\": \"My Item\"} }"`
const PROPS_ATTR = 'data-react-props';
// A unique identifier to identify a node
const CACHE_ID_ATTR = 'data-react-cache-id';
const CLASS_NAME_SELECTOR = `[${CLASS_NAME_ATTR}]`;
// helper method for the mount and unmount methods to find the
// `data-react-class` DOM elements
function findDOMNodes(searchSelector) {
const [selector, parent] = getSelector(searchSelector);
return parent.querySelectorAll(selector);
}
function getSelector(searchSelector) {
switch (typeof searchSelector) {
case 'undefined':
return [CLASS_NAME_SELECTOR, document];
case 'object':
return [CLASS_NAME_SELECTOR, searchSelector];
case 'string':
return [
['', ' ']
.map(
(separator) => `${searchSelector}${separator}${CLASS_NAME_SELECTOR}`
)
.join(', '),
document
];
}
}
class ReactComponentRegistry {
#cache = {};
#components;
constructor(components) {
this.#components = components;
}
getConstructor(className) {
return this.#components[className];
}
mountComponents(searchSelector) {
const nodes = findDOMNodes(searchSelector);
for (const node of nodes) {
const className = node.getAttribute(CLASS_NAME_ATTR);
const ComponentClass = this.getConstructor(className);
const propsJson = node.getAttribute(PROPS_ATTR);
const props = propsJson && JSON.parse(propsJson);
const cacheId = node.getAttribute(CACHE_ID_ATTR);
if (!ComponentClass) {
const message = `Cannot find component: "${className}"`;
console?.log(
`%c[react-rails] %c${message} for element`,
'font-weight: bold',
'',
node
);
throw new Error(
`${message}. Make sure your component is available to render.`
);
} else {
let component = this.#cache[cacheId];
if (!component) {
this.#cache[cacheId] = component = createElement(
ComponentClass,
props
);
}
render(component, node);
}
}
}
}
const Loader = () => <div className="spinner left" />;
export function Loadable(loader) {
const LazyComponent = lazy(loader);
return function PureComponent(props) {
return (
<Suspense fallback={<Loader />}>
<LazyComponent {...props} />
</Suspense>
);
};
}
export function registerReactComponents(components) {
const registry = new ReactComponentRegistry(components);
addEventListener('ds:page:update', () => registry.mountComponents());
}

View file

@ -36,7 +36,6 @@
"react-popper": "^2.2.5",
"react-query": "^3.9.7",
"react-sortable-hoc": "^1.11.0",
"react_ujs": "^2.6.1",
"trix": "^1.2.3",
"use-debounce": "^5.2.0",
"webpack": "^4.46.0",

View file

@ -12394,13 +12394,6 @@ react@^17.0.1:
loose-envify "^1.1.0"
object-assign "^4.1.1"
react_ujs@^2.6.0, react_ujs@^2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/react_ujs/-/react_ujs-2.6.1.tgz#a202a33c95c9e2bb18ab56926b7e79f3325ec855"
integrity sha512-9M33/A8cubStkZ2cpJSimcTD0RlCWiqXF6e90IQmMw/Caf/W0dtAzOtHtiQE3JjLbt/nhRR7NLPxMfnlm141ig==
dependencies:
react_ujs "^2.6.0"
read-cache@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774"