Add new array methods, and remove unnecessary validation calls
This commit is contained in:
parent
efa441f62e
commit
1cd4cbf6bf
|
@ -99,9 +99,14 @@ describe('reducers', () => {
|
|||
'toast': {
|
||||
reducer: 'foo',
|
||||
action: 'bar',
|
||||
validate: true,
|
||||
}
|
||||
}, 'location')).toEqual({
|
||||
'location.toast': 'foo',
|
||||
'location.toast': {
|
||||
reducer: 'foo',
|
||||
action: 'bar',
|
||||
validate: true,
|
||||
},
|
||||
})
|
||||
});
|
||||
});
|
||||
|
|
|
@ -97,7 +97,7 @@ export function createReducerBehaviors(behaviorsConfig: { [key: string]: { reduc
|
|||
//the location string/name combination, so will match up 1:1.
|
||||
return reduce(behaviorsConfig, (memo, behavior, name) => ({
|
||||
...memo,
|
||||
[`${locationString}.${name}`]: behavior.reducer,
|
||||
[`${locationString}.${name}`]: behavior,
|
||||
}), {});
|
||||
}
|
||||
|
||||
|
|
|
@ -64,6 +64,33 @@ describe('arrayReducer', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('push', () => {
|
||||
const { push } = DEFAULT_ARRAY_BEHAVIORS;
|
||||
it('should push the payload onto the end of the array', () => {
|
||||
expect(push.reducer([1,2,3], 4)).toEqual([1,2,3,4]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('pop', () => {
|
||||
const { pop } = DEFAULT_ARRAY_BEHAVIORS;
|
||||
it('should remove the last element from the array', () => {
|
||||
expect(pop.reducer([1,2,3])).toEqual([1,2]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('unshift', () => {
|
||||
const { unshift } = DEFAULT_ARRAY_BEHAVIORS;
|
||||
it('should add the payload to the beginning of the array', () => {
|
||||
expect(unshift.reducer([1,2,3], 4)).toEqual([4,1,2,3]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('shift', () => {
|
||||
const { shift } = DEFAULT_ARRAY_BEHAVIORS;
|
||||
it('should remove the first element of the array', () => {
|
||||
expect(shift.reducer([1,2,3])).toEqual([2,3]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('applyValidation', () => {
|
||||
|
@ -82,7 +109,7 @@ describe('arrayReducer', () => {
|
|||
});
|
||||
|
||||
describe('createReducer', () => {
|
||||
const arrayStructure = Types.arrayOf(Types.number());
|
||||
const arrayStructure = Types.arrayOf(Types.number(), [1,2,3,4]);
|
||||
const reducer = createReducer(arrayStructure, createReducerBehaviors(DEFAULT_ARRAY_BEHAVIORS, 'string'));
|
||||
|
||||
it('should call the correct behavior', () => {
|
||||
|
@ -92,6 +119,12 @@ describe('arrayReducer', () => {
|
|||
index: 0,
|
||||
})).toEqual([4,2,3]);
|
||||
});
|
||||
|
||||
it('do not trigger validation if not required', () => {
|
||||
expect(reducer([1,2], {
|
||||
type: 'string.reset',
|
||||
})).toEqual([1,2,3,4]);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
|
@ -71,6 +71,12 @@ describe('ObjectReducer', () => {
|
|||
payload: { foo: 'toast'}
|
||||
})).toEqual({ foo: 'toast', bar: { baz: 5 }});
|
||||
});
|
||||
|
||||
it('do not trigger validation if not required', () => {
|
||||
expect(reducer({ foo: 'foo', bar: { baz: 6 }}, {
|
||||
type: 'string.reset',
|
||||
})).toEqual({ foo: 'foo', bar: { baz: 5 }});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
|
@ -18,6 +18,7 @@ export type ArrayReducerBehaviorsConfig = {
|
|||
[key: string]: {
|
||||
action?: (value: any) => any,
|
||||
reducer: ArrayReducerBehavior,
|
||||
validate: boolean,
|
||||
}
|
||||
};
|
||||
export type ArrayReducerBehaviors = {
|
||||
|
@ -69,32 +70,61 @@ export const DEFAULT_ARRAY_BEHAVIORS: ArrayReducerBehaviorsConfig = {
|
|||
if (!checkIndex(index, payload, 'updateAtIndex')) return state;
|
||||
if (payload === undefined) return console.warn('Undefined was passed when updating index. Update not performed') || state;
|
||||
return updateAtIndex(state, payload, index);
|
||||
}
|
||||
},
|
||||
validate: true,
|
||||
},
|
||||
resetAtIndex: {
|
||||
reducer(state, payload, initialState, index) {
|
||||
if (!checkIndex(index, payload, 'resetAtIndex')) return state;
|
||||
return updateAtIndex(state, initialState, index);
|
||||
}
|
||||
},
|
||||
validate: false,
|
||||
},
|
||||
removeAtIndex: {
|
||||
reducer(state, payload, initialState, index) {
|
||||
if (!checkIndex(index, payload, 'removeAtIndex')) return state;
|
||||
return removeAtIndex(state, index);
|
||||
}
|
||||
},
|
||||
validate: false,
|
||||
},
|
||||
//Whole array behaviors.
|
||||
replace: {
|
||||
reducer(state, payload) {
|
||||
if(!isArray(payload)) return console.warn('An array must be provided when replacing an array') || state;
|
||||
return payload;
|
||||
}
|
||||
},
|
||||
validate: true,
|
||||
},
|
||||
reset: {
|
||||
reducer(state, payload, initialState) {
|
||||
return initialState;
|
||||
}
|
||||
},
|
||||
validate: false,
|
||||
},
|
||||
push: {
|
||||
reducer(state, payload) {
|
||||
return [...state, payload];
|
||||
},
|
||||
validate: true,
|
||||
},
|
||||
pop: {
|
||||
reducer(state) {
|
||||
return state.slice(0, -1);
|
||||
},
|
||||
validate: false,
|
||||
},
|
||||
unshift: {
|
||||
reducer(state, payload) {
|
||||
return [payload, ...state];
|
||||
},
|
||||
validate: true,
|
||||
},
|
||||
shift: {
|
||||
reducer(state) {
|
||||
return state.slice(1);
|
||||
},
|
||||
validate: false,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -123,7 +153,12 @@ export function createReducer(arrayTypeDescription: ArrayStructureType, behavior
|
|||
//Validating the payload of an array is more tricky, as we do not know ahead of time if the
|
||||
//payload should be an object, primitive, or an array. However, we can still validate here based on the
|
||||
//payload type passed.
|
||||
return behaviors[type](state, applyValidation(arrayTypeDescription, payload), initialValue, index);
|
||||
return behaviors[type].reducer(
|
||||
state,
|
||||
behaviors[type].validate ? applyValidation(arrayTypeDescription, payload) : payload,
|
||||
initialValue,
|
||||
index
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import type { StructureType } from '../structure';
|
|||
export type ShapeReducerAction = {
|
||||
type: string,
|
||||
payload: Object,
|
||||
validate: boolean,
|
||||
};
|
||||
export type ShapeReducer = (state: Object, action: ShapeReducerAction) => Object;
|
||||
export type ShapeReducerBehavior = (state: {}, payload: Object | void, initialState: {}) => Object;
|
||||
|
@ -55,18 +56,21 @@ export const DEFAULT_SHAPE_BEHAVIORS: ShapeReducerBehaviorsConfig = {
|
|||
reducer(state, payload) {
|
||||
if (!isObject(payload)) return state;
|
||||
return { ...state, ...payload };
|
||||
}
|
||||
},
|
||||
validate: true,
|
||||
},
|
||||
reset: {
|
||||
reducer(state, payload, initialState) {
|
||||
return initialState;
|
||||
}
|
||||
},
|
||||
validate: false,
|
||||
},
|
||||
replace: {
|
||||
reducer(state, payload) {
|
||||
if (!payload) return state;
|
||||
return payload;
|
||||
}
|
||||
},
|
||||
validate: true,
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -102,7 +106,11 @@ export function createReducer(objectStructure: StructureType, behaviors: ShapeRe
|
|||
|
||||
//Sanitize the payload using the reducer shape, then apply the sanitized
|
||||
//payload to the state using the behavior linked to this action type.
|
||||
return behaviors[type](state, validateShape(objectStructure, payload), initialState);
|
||||
return behaviors[type].reducer(
|
||||
state,
|
||||
behaviors[type].validate ? validateShape(objectStructure, payload) : payload,
|
||||
initialState
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ export type PrimitiveReducerBehaviorsConfig = {
|
|||
[key: string]: {
|
||||
action?: (value: mixed) => mixed,
|
||||
reducer: PrimitiveReducerBehavior,
|
||||
validate: boolean,
|
||||
}
|
||||
};
|
||||
export type PrimitiveReducerBehaviors = {
|
||||
|
@ -52,12 +53,14 @@ export const DEFAULT_PRIMITIVE_BEHAVIORS: PrimitiveReducerBehaviorsConfig = {
|
|||
reducer(state, payload) {
|
||||
if (payload === undefined) return state;
|
||||
return payload;
|
||||
}
|
||||
},
|
||||
validate: true,
|
||||
},
|
||||
reset: {
|
||||
reducer(state, payload, initialState) {
|
||||
return initialState;
|
||||
}
|
||||
},
|
||||
validate: false,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -76,6 +79,7 @@ export function createPrimitiveReducer(primitiveType: PrimitiveType, {
|
|||
|
||||
|
||||
function createReducer(primitiveType: PrimitiveType, behaviors: PrimitiveReducerBehaviors): PrimitiveReducer {
|
||||
//Calculate and validate the initial state of the reducer
|
||||
const initialState: mixed = validatePrimitive(primitiveType, primitiveType().defaultValue);
|
||||
return (state = initialState, { type, payload }: PrimitiveReducerAction) => {
|
||||
//If the action type does not match any of the specified behaviors, just return the current state.
|
||||
|
@ -83,7 +87,11 @@ function createReducer(primitiveType: PrimitiveType, behaviors: PrimitiveReducer
|
|||
|
||||
//Sanitize the payload using the reducer shape, then apply the sanitized
|
||||
//payload to the state using the behavior linked to this action type.
|
||||
return behaviors[type](state, validatePrimitive(primitiveType, payload), initialState);
|
||||
return behaviors[type].reducer(
|
||||
state,
|
||||
behaviors[type].validate ? validatePrimitive(primitiveType, payload) : payload,
|
||||
initialState
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue