Update buildReducers

This commit is contained in:
Kai Moseley 2016-12-01 14:32:22 +00:00
parent f0db8af726
commit 62f6adaf75
4 changed files with 93 additions and 19 deletions

View File

@ -22,10 +22,6 @@ const example = {
})
})
}),
test1: Types.reducer(Types.string),
test2: Types.reducer(Types.arrayOf({
foo5: Types.number,
})),
};
const test = buildReducers(example);

View File

@ -1,30 +1,92 @@
//@flow
import type { ShapeStructure } from './structure';
//==============================
// Flow imports
//==============================
import type { StructureType, PrimitiveType, ShapeStructure } from './structure';
import type { PartialReducer, Selectors } from './reducers';
import { combineReducers } from 'redux';
import { reduce, find } from 'lodash';
import { createReducer } from './reducers';
import { PROP_TYPES } from './structure';
export function buildReducers(structure: ShapeStructure) {
const tmp = combineReducers(reduce(structure, (memo, propValue, propName) => {
const { structure } = propValue();
type ProcessStructure = (
memo: PartialReducer,
propValue: StructureType|PrimitiveType,
propName: string,
) => PartialReducer;
const containsReducers = !!find(structure, v => v().type === PROP_TYPES._reducer);
type BuildReducers = (name: string, structure: ShapeStructure, options: {
baseSelector: Selectors,
locationString: string,
}) => PartialReducer;
const partial = {
...memo,
[propName]: containsReducers ? buildReducers(structure) : createReducer(propValue),
export function buildReducers(name: string, structure: ShapeStructure, {
baseSelector = state => state[name],
locationString = name,
}: {
baseSelector: Selectors,
locationString: string,
}){
//Build up the reducers, actions, and selectors for this level. Due to recursion,
//these objects will be assigned to a property in the parent object, or simply
//returned to the call site for use in the rest of the application.
const temp = reduce(structure, processStructure, {
reducers: {},
actionsObject: {},
selectorsObject: {},
});
//The Redux 'combineReducers' helper function is used here to save a little bit of boilerplate.
//This helper, if you're not aware, ensures that the correct store properties are passed to the
//reducers assigned to those properties.
return { ...temp, reducers: combineReducers(temp.reducers) };
function processStructure<ProcessStructure>(memo, propValue, propName) {
//Get the structure from the propValue. In the case of 'StructureType' properties, this
//will be some form of shape (or primitives in the case of arrays). At this point we
//are only interested in whether or not the structure contains reducers, as that
//has an impact on how we proceed with regards to calls.
const { structure: propStructure} = propValue();
const containsReducers = !!find(propStructure, v => v().type === PROP_TYPES._reducer);
//Create the child reducer. Depending on whether or not the current structure level contains
//child reducers, we will either recursively call buildReducers, or we will call the
//createReducer function, which will create the correct reducer for the given structure
//(which can be either object, array, or primitive).
let childReducer = containsReducers ? buildReducers(propName, structure, {
locationString: `${locationString}.${propName}`,
baseSelector: state => baseSelector(state)[propName],
}) : createReducer(propValue);
//As the object is built up, we want to assign the reducers/actions created
//by the child to a location on the reducers/actions object which will match up
//to their location. Selectors are created at this level, as the child does not
//need to know where it is located within the grand scheme of things.
return {
reducers: {
...memo.reducers,
[propName]: childReducer.reducers,
},
actionsObject: {
...memo.actionsObject,
[propName]: childReducer.actionsObject,
},
selectorsObject: {
...memo.selectorsObject,
[propName]: state => baseSelector(state)[propName],
},
};
return partial;
}, {}));
return tmp;
}
}

View File

@ -1,4 +1,9 @@
//@flow
//==============================
// Flow imports
//==============================
import type { ObjectReducer, ObjectAction, ObjectSelector } from './reducers/objectReducer';
import {
PROP_TYPES,
} from './structure';
@ -8,6 +13,15 @@ import { primitiveReducer } from './reducers/primitiveReducer';
import { createObjectReducer } from './reducers/objectReducer';
import { arrayReducer } from './reducers/arrayReducer';
export type Selectors = ObjectSelector;
export type Actions = ObjectAction;
export type Reducers = ObjectReducer;
export type PartialReducer = {
reducers: { [key: string]: Reducers},
actionsObject: { [key: string]: Actions },
selectorsObject?: { [key: string]: Selectors },
};
function determineReducerType(reducerDescriptor) {
const { structure } = reducerDescriptor();
const { type } = structure();

View File

@ -31,6 +31,7 @@ export type ObjectReducerOptions = {
behaviorsConfig: ObjectReducerBehaviorsConfig,
locationString: string,
};
export type ObjectSelector = (state: Object) => Object;
//==============================
// JS imports
@ -61,9 +62,10 @@ export function createObjectReducer(reducerShape: StructureType, {
behaviorsConfig = {},
locationString
}: ObjectReducerOptions) {
const completeBehaviorsConfig = { ...DEFAULT_OBJECT_BEHAVIORS, ...behaviorsConfig };
return {
reducer: createReducer(reducerShape, createReducerBehaviors(behaviorsConfig, locationString)),
actions: createActions(behaviorsConfig, locationString),
reducers: createReducer(reducerShape, createReducerBehaviors(completeBehaviorsConfig, locationString)),
actionsObject: createActions(completeBehaviorsConfig, locationString),
};
}