Skip to content

Instantly share code, notes, and snippets.

@petemill
Created January 12, 2017 02:04
Show Gist options
  • Save petemill/a1bafcb34e28476905edffdba38d482f to your computer and use it in GitHub Desktop.
Save petemill/a1bafcb34e28476905edffdba38d482f to your computer and use it in GitHub Desktop.
Returns a class which handles running and cancelation of redux sagas when a route is entered and left
// @flow
declare var System:any;
import { getAsyncInjectors } from './asyncInjectors';
export default function LazyLoadReduxRouteFactory(store: any) {
const { injectReducer, injectSagas, removeSagas } = getAsyncInjectors(store);
return class LazyLoadReduxRoute {
reducerName: string;
getComponentModule: Function;
getReducerModule: Function;
getSagaModule: Function;
loadedSagaTasks: [];
getComponentCustom: ?Function;
onEnterCustom: ?Function;
onLeaveCustom: ?Function;
constructor({ reducerName, getComponentModule, getReducerModule, getSagaModule, getComponent, onEnter, onLeave, ...routeProps }: Object) {
//validate
if (typeof getComponentModule !== 'function') {
throw new Error('Must pass param getComponentModule as function to create a LazyLoadReduxRoute');
}
//save details to instance
this.getComponentModule = getComponentModule;
//process optional args
if (typeof getReducerModule === 'function') {
if (typeof reducerName !== 'string') {
throw new Error('When providing a reducer, must also provide a reducerName');
}
this.reducerName = reducerName;
this.getReducerModule = getReducerModule;
}
if (typeof getSagaModule === 'function') {
this.getSagaModule = getSagaModule;
}
//allow events we handled to have custom logic
if (typeof getComponent === 'function') {
this.getComponentCustom = getComponent;
}
if (typeof onEnter === 'function') {
this.onEnterCustom = onEnter;
}
if (typeof onLeave === 'function') {
this.onLeaveCustom = onLeave;
}
//expose any other route properties caller provides
Object.assign(this, routeProps);
}
async getComponent(nextState: any, cb: Function) {
//get modules in paralell
const ops = [ this.getComponentModule() ];
//reducer is optional
if (this.getReducerModule) {
ops.push(
this.getReducerModule()
.then(reducerModule => injectReducer(this.reducerName, reducerModule.default))
);
}
const [ componentModule ] = await Promise.all(ops);
cb(null, componentModule.default);
}
async onEnter(nextState: any, replace: Function) {
//should we be loading sagas
if (this.getSagaModule) {
//sanity check, should not have loaded tasks yet (or still from previous visit to page)
if (!this.loadedSagaTasks) {
//load saga module
const sagas = await this.getSagaModule();
//remember sagas and saga-tasks, for cancelling later
this.loadedSagaTasks = injectSagas(sagas.default);
}
//handle sagas not previously unloaded
else {
console.error('sagas were already loaded', { tasks: this.loadedSagaTasks });
}
}
//custom route onEnter
if (this.onEnterCustom) {
this.onEnterCustom(nextState, replace);
}
}
onLeave(prevState: any) {
//custom route onLeave
if (this.onLeaveCustom) {
this.onLeaveCustom(prevState);
}
//cancel running tasks, if any
if (this.loadedSagaTasks) {
this.loadedSagaTasks.forEach(saga => saga.cancel());
//remove reference to cancelled tasks
delete this.loadedSagaTasks;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment