diff --git a/package.json b/package.json index bdb1324..2c6c1e5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redux-scc", - "version": "1.5.1", + "version": "1.6.0", "description": "Redux store chunk creator", "main": "./lib/index.js", "files": [ @@ -53,6 +53,8 @@ "lodash.keys": "^4.2.0", "lodash.map": "^4.6.0", "lodash.omit": "^4.5.0", + "lodash.findindex": "^4.6.0", + "lodash.isequal": "^4.5.0", "redux": "4.0.0" } } diff --git a/readme.md b/readme.md index 7b4cdb5..265fcaa 100644 --- a/readme.md +++ b/readme.md @@ -108,6 +108,7 @@ Like all other types, you can also use a custom type to create a reducer. - resetAtIndex(value: any, index: number): Reset the value for an array element, at the specified index, with the value provided. - removeAtIndex(index: number): Remove the element from the array at the specified index. - push(value: any): Add the value to the end of the array. +- pushOrRemove(value: any): Push the value if it doesn't exist in the array or remove the element if it exists. - pop(): Remove the last element of the array. - shift(value: any): Add the value to the beginning of the array. - unshift(): Remove the first element of the array. diff --git a/src/reducers/__tests__/arrayReducer.test.js b/src/reducers/__tests__/arrayReducer.test.js index 849f8c7..c906061 100644 --- a/src/reducers/__tests__/arrayReducer.test.js +++ b/src/reducers/__tests__/arrayReducer.test.js @@ -71,6 +71,40 @@ describe('arrayReducer', () => { }); }); + describe('pushOrRemove', () => { + const { pushOrRemove } = DEFAULT_ARRAY_BEHAVIORS; + it('should push the primitive payload onto the end of the array', () => { + expect(pushOrRemove.reducer([1,2,3], 4)).toEqual([1,2,3,4]); + }); + it('should remove the primitive payload from the array', () => { + expect(pushOrRemove.reducer([1,2,3], 2)).toEqual([1,3]); + }); + it('should push the object payload onto the end of the array', () => { + let users = [ + { 'user': 'barney', 'active': false }, + { 'user': 'fred', 'active': false } + ]; + let expected_users = [ + { 'user': 'barney', 'active': false }, + { 'user': 'fred', 'active': false }, + { 'user': 'pebbles', 'active': true } + ]; + expect(pushOrRemove.reducer(users, { 'user': 'pebbles', 'active': true })).toEqual(expected_users); + }); + it('should remove the object payload from the array', () => { + let users = [ + { 'user': 'barney', 'active': false }, + { 'user': 'fred', 'active': false }, + { 'user': 'pebbles', 'active': true } + ]; + let expected_users = [ + { 'user': 'barney', 'active': false }, + { 'user': 'pebbles', 'active': true } + ]; + expect(pushOrRemove.reducer(users, { 'user': 'fred', 'active': false })).toEqual(expected_users); + }); + }); + describe('pop', () => { const { pop } = DEFAULT_ARRAY_BEHAVIORS; it('should remove the last element from the array', () => { diff --git a/src/reducers/arrayReducer.js b/src/reducers/arrayReducer.js index d632424..2f7f130 100644 --- a/src/reducers/arrayReducer.js +++ b/src/reducers/arrayReducer.js @@ -59,6 +59,7 @@ import { createReducerBehaviors } from "../reducers"; import { updateAtIndex, removeAtIndex } from "../utils/arrayUtils"; import { PROP_TYPES } from "../structure"; import { isCombinedAction, getApplicableCombinedActions } from "./batchUpdates"; +import { findIndex, isEqual } from "lodash/fp"; const reduce = require("lodash/fp/reduce").convert({ cap: false }); @@ -136,6 +137,16 @@ export const DEFAULT_ARRAY_BEHAVIORS: ArrayReducerBehaviorsConfig = { }, validate: true }, + pushOrRemove: { + reducer(state, payload) { + let index = findIndex(isEqual(payload))(state); + if(index == -1) { + return [...state, payload]; + } + return removeAtIndex(state, index); + }, + validate: true + }, pop: { reducer(state) { return state.slice(0, -1);