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': {
|
'toast': {
|
||||||
reducer: 'foo',
|
reducer: 'foo',
|
||||||
action: 'bar',
|
action: 'bar',
|
||||||
|
validate: true,
|
||||||
}
|
}
|
||||||
}, 'location')).toEqual({
|
}, '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.
|
//the location string/name combination, so will match up 1:1.
|
||||||
return reduce(behaviorsConfig, (memo, behavior, name) => ({
|
return reduce(behaviorsConfig, (memo, behavior, name) => ({
|
||||||
...memo,
|
...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', () => {
|
describe('applyValidation', () => {
|
||||||
|
@ -82,7 +109,7 @@ describe('arrayReducer', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('createReducer', () => {
|
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'));
|
const reducer = createReducer(arrayStructure, createReducerBehaviors(DEFAULT_ARRAY_BEHAVIORS, 'string'));
|
||||||
|
|
||||||
it('should call the correct behavior', () => {
|
it('should call the correct behavior', () => {
|
||||||
|
@ -92,6 +119,12 @@ describe('arrayReducer', () => {
|
||||||
index: 0,
|
index: 0,
|
||||||
})).toEqual([4,2,3]);
|
})).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'}
|
payload: { foo: 'toast'}
|
||||||
})).toEqual({ foo: 'toast', bar: { baz: 5 }});
|
})).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]: {
|
[key: string]: {
|
||||||
action?: (value: any) => any,
|
action?: (value: any) => any,
|
||||||
reducer: ArrayReducerBehavior,
|
reducer: ArrayReducerBehavior,
|
||||||
|
validate: boolean,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
export type ArrayReducerBehaviors = {
|
export type ArrayReducerBehaviors = {
|
||||||
|
@ -69,32 +70,61 @@ export const DEFAULT_ARRAY_BEHAVIORS: ArrayReducerBehaviorsConfig = {
|
||||||
if (!checkIndex(index, payload, 'updateAtIndex')) return state;
|
if (!checkIndex(index, payload, 'updateAtIndex')) return state;
|
||||||
if (payload === undefined) return console.warn('Undefined was passed when updating index. Update not performed') || state;
|
if (payload === undefined) return console.warn('Undefined was passed when updating index. Update not performed') || state;
|
||||||
return updateAtIndex(state, payload, index);
|
return updateAtIndex(state, payload, index);
|
||||||
}
|
},
|
||||||
|
validate: true,
|
||||||
},
|
},
|
||||||
resetAtIndex: {
|
resetAtIndex: {
|
||||||
reducer(state, payload, initialState, index) {
|
reducer(state, payload, initialState, index) {
|
||||||
if (!checkIndex(index, payload, 'resetAtIndex')) return state;
|
if (!checkIndex(index, payload, 'resetAtIndex')) return state;
|
||||||
return updateAtIndex(state, initialState, index);
|
return updateAtIndex(state, initialState, index);
|
||||||
}
|
},
|
||||||
|
validate: false,
|
||||||
},
|
},
|
||||||
removeAtIndex: {
|
removeAtIndex: {
|
||||||
reducer(state, payload, initialState, index) {
|
reducer(state, payload, initialState, index) {
|
||||||
if (!checkIndex(index, payload, 'removeAtIndex')) return state;
|
if (!checkIndex(index, payload, 'removeAtIndex')) return state;
|
||||||
return removeAtIndex(state, index);
|
return removeAtIndex(state, index);
|
||||||
}
|
},
|
||||||
|
validate: false,
|
||||||
},
|
},
|
||||||
//Whole array behaviors.
|
//Whole array behaviors.
|
||||||
replace: {
|
replace: {
|
||||||
reducer(state, payload) {
|
reducer(state, payload) {
|
||||||
if(!isArray(payload)) return console.warn('An array must be provided when replacing an array') || state;
|
if(!isArray(payload)) return console.warn('An array must be provided when replacing an array') || state;
|
||||||
return payload;
|
return payload;
|
||||||
}
|
},
|
||||||
|
validate: true,
|
||||||
},
|
},
|
||||||
reset: {
|
reset: {
|
||||||
reducer(state, payload, initialState) {
|
reducer(state, payload, initialState) {
|
||||||
return 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
|
//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 should be an object, primitive, or an array. However, we can still validate here based on the
|
||||||
//payload type passed.
|
//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 = {
|
export type ShapeReducerAction = {
|
||||||
type: string,
|
type: string,
|
||||||
payload: Object,
|
payload: Object,
|
||||||
|
validate: boolean,
|
||||||
};
|
};
|
||||||
export type ShapeReducer = (state: Object, action: ShapeReducerAction) => Object;
|
export type ShapeReducer = (state: Object, action: ShapeReducerAction) => Object;
|
||||||
export type ShapeReducerBehavior = (state: {}, payload: Object | void, initialState: {}) => Object;
|
export type ShapeReducerBehavior = (state: {}, payload: Object | void, initialState: {}) => Object;
|
||||||
|
@ -55,18 +56,21 @@ export const DEFAULT_SHAPE_BEHAVIORS: ShapeReducerBehaviorsConfig = {
|
||||||
reducer(state, payload) {
|
reducer(state, payload) {
|
||||||
if (!isObject(payload)) return state;
|
if (!isObject(payload)) return state;
|
||||||
return { ...state, ...payload };
|
return { ...state, ...payload };
|
||||||
}
|
},
|
||||||
|
validate: true,
|
||||||
},
|
},
|
||||||
reset: {
|
reset: {
|
||||||
reducer(state, payload, initialState) {
|
reducer(state, payload, initialState) {
|
||||||
return initialState;
|
return initialState;
|
||||||
}
|
},
|
||||||
|
validate: false,
|
||||||
},
|
},
|
||||||
replace: {
|
replace: {
|
||||||
reducer(state, payload) {
|
reducer(state, payload) {
|
||||||
if (!payload) return state;
|
if (!payload) return state;
|
||||||
return payload;
|
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
|
//Sanitize the payload using the reducer shape, then apply the sanitized
|
||||||
//payload to the state using the behavior linked to this action type.
|
//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]: {
|
[key: string]: {
|
||||||
action?: (value: mixed) => mixed,
|
action?: (value: mixed) => mixed,
|
||||||
reducer: PrimitiveReducerBehavior,
|
reducer: PrimitiveReducerBehavior,
|
||||||
|
validate: boolean,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
export type PrimitiveReducerBehaviors = {
|
export type PrimitiveReducerBehaviors = {
|
||||||
|
@ -52,12 +53,14 @@ export const DEFAULT_PRIMITIVE_BEHAVIORS: PrimitiveReducerBehaviorsConfig = {
|
||||||
reducer(state, payload) {
|
reducer(state, payload) {
|
||||||
if (payload === undefined) return state;
|
if (payload === undefined) return state;
|
||||||
return payload;
|
return payload;
|
||||||
}
|
},
|
||||||
|
validate: true,
|
||||||
},
|
},
|
||||||
reset: {
|
reset: {
|
||||||
reducer(state, payload, initialState) {
|
reducer(state, payload, initialState) {
|
||||||
return initialState;
|
return initialState;
|
||||||
}
|
},
|
||||||
|
validate: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -76,6 +79,7 @@ export function createPrimitiveReducer(primitiveType: PrimitiveType, {
|
||||||
|
|
||||||
|
|
||||||
function createReducer(primitiveType: PrimitiveType, behaviors: PrimitiveReducerBehaviors): PrimitiveReducer {
|
function createReducer(primitiveType: PrimitiveType, behaviors: PrimitiveReducerBehaviors): PrimitiveReducer {
|
||||||
|
//Calculate and validate the initial state of the reducer
|
||||||
const initialState: mixed = validatePrimitive(primitiveType, primitiveType().defaultValue);
|
const initialState: mixed = validatePrimitive(primitiveType, primitiveType().defaultValue);
|
||||||
return (state = initialState, { type, payload }: PrimitiveReducerAction) => {
|
return (state = initialState, { type, payload }: PrimitiveReducerAction) => {
|
||||||
//If the action type does not match any of the specified behaviors, just return the current state.
|
//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
|
//Sanitize the payload using the reducer shape, then apply the sanitized
|
||||||
//payload to the state using the behavior linked to this action type.
|
//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