Update reducer recursion to improve output structure

This commit is contained in:
Kai Moseley 2016-12-06 08:17:01 +00:00
parent b5e6f9024c
commit 966b7739bf
5 changed files with 32 additions and 30 deletions

View File

@ -18,32 +18,15 @@ export function buildReducer(name: string, structure: any, {
baseSelector: any, baseSelector: any,
locationString: string, locationString: string,
} = {}): PartialReducer { } = {}): PartialReducer {
const temp = reducerBuilder(name, structure, {
baseSelector,
locationString
});
return {
...temp,
reducers: {
[name]: temp.reducers,
}
}
}
function reducerBuilder(name: string, structure: any, {
baseSelector = state => state[name],
locationString = '',
}: {
baseSelector: any,
locationString: string,
} = {}): PartialReducer {
if (structure === undefined) throw new Error(`The structure must be defined for a reducer! LocationString: ${ locationString }`); if (structure === undefined) throw new Error(`The structure must be defined for a reducer! LocationString: ${ locationString }`);
//Build up the reducers, actions, and selectors for this level. Due to recursion, //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 //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. //returned to the call site for use in the rest of the application.
const temp = reduce(structure, processStructure, { const temp = reduce(structure, processStructure, {
reducers: {}, reducers: {
[name]: {},
},
actions: {}, actions: {},
selectors: {}, selectors: {},
}); });
@ -51,7 +34,9 @@ function reducerBuilder(name: string, structure: any, {
//The Redux 'combineReducers' helper function is used here to save a little bit of boilerplate. //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 //This helper, if you're not aware, ensures that the correct store properties are passed to the
//reducers assigned to those properties. //reducers assigned to those properties.
return { ...temp, reducers: combineReducers(temp.reducers) }; return { ...temp, reducers: {
[name]: combineReducers(temp.reducers)
}};
function processStructure(memo: PartialReducer, propValue: StructureType | PrimitiveType, propName: string) { function processStructure(memo: PartialReducer, propValue: StructureType | PrimitiveType, propName: string) {
//Get the structure from the propValue. In the case of 'StructureType' properties, this //Get the structure from the propValue. In the case of 'StructureType' properties, this
@ -66,22 +51,24 @@ function reducerBuilder(name: string, structure: any, {
//createReducer function, which will create the correct reducer for the given structure //createReducer function, which will create the correct reducer for the given structure
//(which can be either object, array, or primitive). //(which can be either object, array, or primitive).
let childReducer = containsReducers let childReducer = containsReducers
? reducerBuilder(propName, propStructure, { ? buildReducer(propName, propStructure, {
locationString: locationString ? `${locationString}.${propName}` : propName, locationString: locationString ? `${locationString}.${propName}` : propName,
baseSelector: (state: any) => baseSelector(state)[propName], baseSelector: (state: any) => baseSelector(state)[propName],
}) })
: createReducer(propValue, { : createReducer(propValue, {
locationString: `${locationString}.${propName}`, locationString: `${locationString}.${propName}`,
name: propName,
}); });
//As the object is built up, we want to assign the reducers/actions created //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 //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 //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. //need to know where it is located within the grand scheme of things.
return { return {
reducers: { reducers: {
...memo.reducers, ...memo.reducers,
[propName]: childReducer.reducers, ...childReducer.reducers
}, },
actions: { actions: {
...memo.actions, ...memo.actions,

View File

@ -29,6 +29,7 @@ import { createPrimitiveReducer } from './reducers/primitiveReducer';
function determineReducerType(reducerDescriptor, { function determineReducerType(reducerDescriptor, {
name,
locationString, locationString,
}) { }) {
const REDUCERS = { const REDUCERS = {
@ -42,15 +43,17 @@ function determineReducerType(reducerDescriptor, {
const { type } = structure(); const { type } = structure();
return { return {
name,
reducerFn: REDUCERS[type], reducerFn: REDUCERS[type],
reducerStructureDescriptor: structure, reducerStructureDescriptor: structure,
locationString, locationString,
}; };
} }
function callReducer({ reducerFn, reducerStructureDescriptor, locationString } = {}) { function callReducer({ name, reducerFn, reducerStructureDescriptor, locationString } = {}) {
return reducerFn(reducerStructureDescriptor, { return reducerFn(reducerStructureDescriptor, {
locationString, locationString,
name,
}); });
} }

View File

@ -30,6 +30,7 @@ export type ArrayActions = {
export type ArrayReducerOptions = { export type ArrayReducerOptions = {
behaviorsConfig: ArrayReducerBehaviorsConfig, behaviorsConfig: ArrayReducerBehaviorsConfig,
locationString: string, locationString: string,
name: string,
}; };
export type ArraySelector = (state: Object) => Array<any>; export type ArraySelector = (state: Object) => Array<any>;
@ -96,10 +97,13 @@ const DEFAULT_ARRAY_BEHAVIORS: ArrayReducerBehaviorsConfig = {
export function createArrayReducer(arrayTypeDescription: ArrayStructureType, { export function createArrayReducer(arrayTypeDescription: ArrayStructureType, {
locationString locationString,
name,
}: ArrayReducerOptions = {}) { }: ArrayReducerOptions = {}) {
return { return {
reducers: createReducer(arrayTypeDescription, createReducerBehaviors(DEFAULT_ARRAY_BEHAVIORS, locationString)), reducers: {
[name]: createReducer(arrayTypeDescription, createReducerBehaviors(DEFAULT_ARRAY_BEHAVIORS, locationString))
},
actions: createActions(DEFAULT_ARRAY_BEHAVIORS, locationString, {}), actions: createActions(DEFAULT_ARRAY_BEHAVIORS, locationString, {}),
}; };
} }

View File

@ -29,6 +29,7 @@ export type ObjectActions = {
export type ObjectReducerOptions = { export type ObjectReducerOptions = {
behaviorsConfig: ObjectReducerBehaviorsConfig, behaviorsConfig: ObjectReducerBehaviorsConfig,
locationString: string, locationString: string,
name: string,
}; };
//============================== //==============================
@ -60,10 +61,13 @@ const DEFAULT_OBJECT_BEHAVIORS: ObjectReducerBehaviorsConfig = {
}; };
export function createObjectReducer(reducerShape: StructureType, { export function createObjectReducer(reducerShape: StructureType, {
locationString locationString,
name,
}: ObjectReducerOptions = {}) { }: ObjectReducerOptions = {}) {
return { return {
reducers: createReducer(reducerShape, createReducerBehaviors(DEFAULT_OBJECT_BEHAVIORS, locationString)), reducers: {
[name]: createReducer(reducerShape, createReducerBehaviors(DEFAULT_OBJECT_BEHAVIORS, locationString)),
},
actions: createActions(DEFAULT_OBJECT_BEHAVIORS, locationString, {}), actions: createActions(DEFAULT_OBJECT_BEHAVIORS, locationString, {}),
}; };
} }

View File

@ -29,6 +29,7 @@ export type PrimitiveActions = {
export type PrimitiveReducerOptions = { export type PrimitiveReducerOptions = {
behaviorsConfig: PrimitiveReducerBehaviorsConfig, behaviorsConfig: PrimitiveReducerBehaviorsConfig,
locationString: string, locationString: string,
name: string,
}; };
//============================== //==============================
@ -55,10 +56,13 @@ const DEFAULT_PRIMITIVE_BEHAVIORS: PrimitiveReducerBehaviorsConfig = {
export function createPrimitiveReducer(primitiveType: PrimitiveType, { export function createPrimitiveReducer(primitiveType: PrimitiveType, {
locationString locationString,
name,
}: PrimitiveReducerOptions = {}) { }: PrimitiveReducerOptions = {}) {
return { return {
reducers: createReducer(primitiveType, createReducerBehaviors(DEFAULT_PRIMITIVE_BEHAVIORS, locationString)), reducers: {
[name]: createReducer(primitiveType, createReducerBehaviors(DEFAULT_PRIMITIVE_BEHAVIORS, locationString)),
},
actions: createActions(DEFAULT_PRIMITIVE_BEHAVIORS, locationString, {}), actions: createActions(DEFAULT_PRIMITIVE_BEHAVIORS, locationString, {}),
}; };
} }