react-loader-factory-immutable
v1.4.2
Published
A factory for creating custom functional react loading screen components (immutableJS version).
Downloads
25
Maintainers
Readme
react-loader-factory-immutable
A factory for producing Redux-driven loading screens. (ImmutableJS version.)
For a version that doesn't use ImmutableJS, check out react-loader-factory.
Example
npm install --save react-loader-factory-immutable
Say you have an asynchronous request that provides data through Redux to a pure functional component. You'd like to display a loader while waiting for the request, but you don't want to pollute your beautiful pure function.
With react-loader-factory, you can do this instead:
import React from 'react';
import { myAsyncAction } from '../actions';
import loaderFactory from 'react-loader-factory-immutable';
import ChildComponent from './ChildComponent';
const actionsList = [myAsyncAction()];
const monitoredStates = ['ASYNC_REQUEST'];
const loaderWrapper = loaderFactory(actionsList, monitoredStates);
const LoadingChild = loaderWrapper(ChildComponent);
const containingComponent = props => {
// Do whatever you need to do with your usual containing component
const childProps = { someProps: 'props' };
return <LoadingChild { ...childProps } />;
}
You'll also need a reducer that tracks which requests are active. Something like this:
import Immutable from 'immutable';
export function activeRequests(state = Immutable.List([]), action) {
// regex that tests for an API action string ending with _REQUEST
const reqReg = new RegExp(/^[A-Z]+\_REQUEST$/g);
// regex that tests for a API action string ending with _SUCCESS
const sucReg = new RegExp(/^[A-Z]+\_SUCCESS$/g);
// if a _REQUEST comes in, add it to the activeRequests list
if (reqReg.test(action.type)) {
return state.push(action.type);
}
// if a _SUCCESS comes in, delete its corresponding _REQUEST
if (sucReg.test(action.type)) {
const reqType = action.type.split('_')[0].concat('_REQUEST');
const deleteInd = state.indexOf(reqType);
if (deleteInd !== -1) {
return state.delete(deleteInd);
}
}
return state;
}
As long as none of the requests specified in monitoredStates
have come back
with a SUCCESS
(or whatever you use to specify a successful request), the
loader will continue to display its default throbber, or a throbber
prop you
pass into the returned loading component.
The guts
loaderFactory(actionsList, monitoredStates)
returns a higher-order component that connects to the Redux store and monitors theactiveRequests
state branch for values it's been told to monitor. It expectsactiveRequests
to have a.some()
method to test with. It also takes responsibility for dispatching the Redux actions specified inactionsList
exactly once.If any of its monitored active requests are present, it displays
this.props.throbber
or a default<div>
with a classthis.props.throbberClass
(orloader layout--flex
if none is specified).If there are no more active requests the wrapped component cares about, the throbber component gets out of the way and returns the originally wrapped component, with all props passed through.
Optional state injector
You can also pass in a function as the third argument to loaderFactory
that
accepts state
as its sole argument. The function should return an object with
an array of strings called activeRequests
. If any of monitoredStates
are
present in the array, the throbber will be rendered instead of the dependent
content.
const actionsList = [myAsyncAction()];
const monitoredStates = ['ASYNC_REQUEST'];
const loaderWrapper = loaderFactory(actionsList, monitoredStates, function(state) {
return { activeRequests: state.get('customKey') };
});
Optional state dependency
If you have even more granular requirements for what the state needs to be
before the loader should pass the application through, pass in a boolean
function of state
as the fourth argument to loaderFactory
. When this
function returns true
and the state injector function above return true,
the loader will render the wrapped component.
Why a factory?
The factory pattern is needed to set up the connect()
call that hooks the
component up to your Redux store. There's no way for a component to dynamically
connect()
itself when evaluated, so the factory pattern gives you that
convenience.
Things like this
- react-loader: Stateful single component version without any particular connection to Redux.
- React Higher Order Components in depth: My reference for HOCs.