react-loader-factory-immutable
Version:
A factory for creating custom functional react loading screen components (immutableJS version).
111 lines (97 loc) • 3.29 kB
JSX
import React from 'react';
import { connect } from 'react-redux';
import equal from 'deep-equal';
function deepIncludes(arr, item) {
return arr.some(el => equal(el, item));
}
function shallowDesymbolize(obj) {
if (obj instanceof Array) {
return obj.map((el) => shallowDesymbolize(el));
} else if (obj instanceof Object) {
const res = {};
Object.getOwnPropertySymbols(obj).forEach((key) => {
const desymbolizedKey = ('__dsym__').concat(String(key));
res[desymbolizedKey] = obj[key];
});
return res;
}
return obj;
}
export default function loaderFactory(actionsList, requestStates, stateInjector,
stateMonitor) {
if (stateMonitor === undefined) {
stateMonitor = function() {
return false;
};
}
return function(WrappedComponent) {
const factoryInjector = stateInjector || function(state) {
return { activeRequests: state.get('activeRequests').toJS() };
};
const waitInjector = function(state) {
return Object.assign(factoryInjector(state), {
waitingOnStore: stateMonitor(state)
});
};
class Loader extends React.Component {
constructor(props) {
super(props);
this.currentRequests = [];
this.needsDispatch = true;
}
render() {
const { activeRequests, dispatch } = this.props;
// monitor given request states
const requestsBusy = this.needsDispatch ||
this.props.waitingOnStore ||
(function() {
if (activeRequests instanceof Array) {
return requestStates
.some(state => activeRequests.includes(state));
} else if (activeRequests instanceof Object) { // works as else if
return requestStates
.some(state => Object.keys(activeRequests).includes(state));
} else {
console.warn(
'Loader: did not receive a valid requestStates object: ',
requestStates
);
return false;
}
})();
// call actions, but throttle if repeating
actionsList.forEach(action => {
if (!deepIncludes(shallowDesymbolize(this.currentRequests),
shallowDesymbolize(action))) {
this.currentRequests.push(action);
dispatch(action);
}
});
// needsDispatch should be "true" only for the first render pass
this.needsDispatch = false;
// return function that takes a component which will be rendered when
// none of the request states is active
const Throbber = (this.props.throbber || function(props) {
return (
<div
className={ props.throbberClass || "loader layout--flex" }
>
<h1>Loading...</h1>
</div>
);
});
// in other words:
// IF requesting RENDER throbber
// ELSE render given component
if (requestsBusy) {
return (<Throbber />);
} else {
return (
<WrappedComponent { ...this.props } />
);
}
}
}
return connect(waitInjector)(Loader);
};
}