Simplify React components loader

This commit is contained in:
Paul Chavard 2019-08-14 12:11:36 +01:00
parent 39afa6659c
commit 2cf415dc41
10 changed files with 107 additions and 147 deletions

View file

@ -0,0 +1,8 @@
import React from 'react';
import Loadable from 'react-loadable';
const loading = () => <div className="spinner left" />;
export default function(loader) {
return Loadable({ loader, loading });
}

View file

@ -0,0 +1,62 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { library } from '@fortawesome/fontawesome-svg-core';
import {
faArrowCircleDown,
faArrowDown,
faArrowsAltV,
faArrowUp,
faPlus,
faTrash
} from '@fortawesome/free-solid-svg-icons';
import Flash from './Flash';
import OperationsQueue from './OperationsQueue';
import TypeDeChamps from './components/TypeDeChamps';
library.add(
faArrowCircleDown,
faArrowDown,
faArrowsAltV,
faArrowUp,
faPlus,
faTrash
);
class TypesDeChampEditor extends Component {
constructor(props) {
super(props);
const defaultTypeDeChampAttributes = {
type_champ: 'text',
types_de_champ: [],
private: props.isAnnotation,
libelle: `${
props.isAnnotation ? 'Nouvelle annotation' : 'Nouveau champ'
} ${props.typeDeChampsTypes[0][0]}`
};
this.state = {
flash: new Flash(props.isAnnotation),
queue: new OperationsQueue(props.baseUrl),
defaultTypeDeChampAttributes,
typeDeChampsTypes: props.typeDeChampsTypes,
directUploadUrl: props.directUploadUrl,
isAnnotation: props.isAnnotation
};
}
render() {
return (
<TypeDeChamps state={this.state} typeDeChamps={this.props.typeDeChamps} />
);
}
}
TypesDeChampEditor.propTypes = {
baseUrl: PropTypes.string,
directUploadUrl: PropTypes.string,
isAnnotation: PropTypes.bool,
typeDeChamps: PropTypes.array,
typeDeChampsTypes: PropTypes.array
};
export default TypesDeChampEditor;

View file

@ -1,66 +1,3 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { library } from '@fortawesome/fontawesome-svg-core';
import {
faArrowDown,
faArrowsAltV,
faArrowUp,
faArrowCircleDown,
faPlus,
faTrash
} from '@fortawesome/free-solid-svg-icons';
import Loadable from '../Loadable';
import Flash from './Flash';
import OperationsQueue from './OperationsQueue';
import TypeDeChamps from './components/TypeDeChamps';
library.add(
faArrowDown,
faArrowsAltV,
faArrowUp,
faArrowCircleDown,
faPlus,
faTrash
);
class TypesDeChampEditor extends Component {
constructor(props) {
super(props);
const defaultTypeDeChampAttributes = {
type_champ: 'text',
types_de_champ: [],
private: props.isAnnotation,
libelle: `${
props.isAnnotation ? 'Nouvelle annotation' : 'Nouveau champ'
} ${props.typeDeChampsTypes[0][0]}`
};
this.state = {
flash: new Flash(props.isAnnotation),
queue: new OperationsQueue(props.baseUrl),
defaultTypeDeChampAttributes,
typeDeChampsTypes: props.typeDeChampsTypes,
directUploadUrl: props.directUploadUrl,
isAnnotation: props.isAnnotation
};
}
render() {
return (
<TypeDeChamps state={this.state} typeDeChamps={this.props.typeDeChamps} />
);
}
}
TypesDeChampEditor.propTypes = {
baseUrl: PropTypes.string,
directUploadUrl: PropTypes.string,
isAnnotation: PropTypes.bool,
typeDeChamps: PropTypes.array,
typeDeChampsTypes: PropTypes.array
};
export function createReactUJSElement(props) {
return React.createElement(TypesDeChampEditor, props);
}
export default TypesDeChampEditor;
export default Loadable(() => import('./TypesDeChampEditor'));

View file

@ -6,9 +6,7 @@ import '@rails/actiontext';
import 'whatwg-fetch'; // window.fetch polyfill
import Chartkick from 'chartkick';
import Highcharts from 'highcharts';
import ReactUJS from '../shared/react-ujs';
import reactComponents from '../shared/react-components';
import ReactRailsUJS from 'react_ujs';
import '../shared/page-update-event';
import '../shared/activestorage/ujs';
@ -61,8 +59,18 @@ Rails.start();
Turbolinks.start();
ActiveStorage.start();
const loader = new ReactUJS(reactComponents);
loader.start();
// If Turbolinks is imported via Webpacker (and thus not available globally),
// ReactRailsUJS will be unable to locate it.
// https://github.com/reactjs/react-rails#event-handling
// eslint-disable-next-line no-undef
ReactRailsUJS.useContext(require.context('components', true));
// Add Turbolinks to the global namespace:
window.Turbolinks = Turbolinks;
// Remove previous event handlers and add new ones:
ReactRailsUJS.detectEvents();
// (Optional) Clean up global namespace:
delete window.Turbolinks;
// Expose globals
window.DS = window.DS || DS;

View file

@ -1,8 +0,0 @@
export default function reactComponents(className) {
switch (className) {
case 'TypesDeChampEditor':
return import('components/TypesDeChampEditor').then(
mod => mod.createReactUJSElement
);
}
}

View file

@ -1,61 +0,0 @@
import ReactDOM 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';
// This attribute holds which method to use between: ReactDOM.hydrate, ReactDOM.render
const RENDER_ATTR = 'data-hydrate';
function findDOMNodes() {
return document.querySelectorAll(`[${CLASS_NAME_ATTR}]`);
}
export default class ReactUJS {
constructor(loadComponent) {
this.loadComponent = loadComponent;
}
async mountComponents() {
const nodes = findDOMNodes();
for (let node of nodes) {
const className = node.getAttribute(CLASS_NAME_ATTR);
const createReactUJSElement = await this.loadComponent(className).catch(
() => null
);
if (!createReactUJSElement) {
const message = "Cannot find component: '" + className + "'";
// eslint-disable-next-line no-console
console.error(
'%c[react-rails] %c' + message + ' for element',
'font-weight: bold',
'',
node
);
throw new Error(
message + '. Make sure your component is available to render.'
);
} else {
const propsJson = node.getAttribute(PROPS_ATTR);
const props = propsJson && JSON.parse(propsJson);
const hydrate = node.getAttribute(RENDER_ATTR);
if (hydrate && typeof ReactDOM.hydrate === 'function') {
ReactDOM.hydrate(createReactUJSElement(props), node);
} else {
ReactDOM.render(createReactUJSElement(props), node);
}
}
}
}
start() {
addEventListener('ds:page:update', () => this.mountComponents());
}
}