Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions src/createSlice.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { createReducer } from './createReducer';

const getType = (slice, action) =>
slice ? `${slice}/${action}` : action;

export default function createSlice({
slice = '',
actions = {},
initialState,
}) {
const actionKeys = Object.keys(actions);

const reducerMap = actionKeys.reduce(
(map, action) => {
map[getType(slice, action)] = actions[action];
return map;
},
{},
);

const reducer = createReducer(initialState, reducerMap);

const actionMap = actionKeys.reduce(
(map, action) => {
map[action] = (payload) => ({
type: getType(slice, action),
payload,
});

return map;
},
{},
);

return {
actions: actionMap,
reducer,
slice,
};
}
86 changes: 86 additions & 0 deletions src/createSlice.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import createSlice from './createSlice'

describe('createSlice', () => {
describe('when slice is empty', () => {
const { actions, reducer } = createSlice({
actions: {
increment: state => state + 1,
multiply: (state, action) => state * action.payload
},
initialState: 0
})

it('should create increment action', () => {
expect(actions.hasOwnProperty('increment')).toBe(true)
})

it('should create multiply action', () => {
expect(actions.hasOwnProperty('multiply')).toBe(true)
})

it('should have the correct action for increment', () => {
expect(actions.increment()).toEqual({
type: 'increment',
payload: undefined
})
})

it('should have the correct action for multiply', () => {
expect(actions.multiply(3)).toEqual({
type: 'multiply',
payload: 3
})
})

describe('when using reducer', () => {
it('should return the correct value from reducer with increment', () => {
expect(reducer(undefined, actions.increment())).toEqual(1)
})

it('should return the correct value from reducer with multiply', () => {
expect(reducer(2, actions.multiply(3))).toEqual(6)
})
})
})

describe('when passing slice', () => {
const { actions, reducer } = createSlice({
actions: {
increment: state => state + 1
},
initialState: 0,
slice: 'cool'
})

it('should create increment action', () => {
expect(actions.hasOwnProperty('increment')).toBe(true)
})

it('should have the correct action for increment', () => {
expect(actions.increment()).toEqual({
type: 'cool/increment',
payload: undefined
})
})

it('should return the correct value from reducer', () => {
expect(reducer(undefined, actions.increment())).toEqual(1)
})
})

describe('when mutating state object', () => {
const { actions, reducer } = createSlice({
actions: {
setUserName: (state, action) => {
state.user = action.payload
}
},
initialState: { user: '' },
slice: 'user'
})

it('should set the username', () => {
expect(reducer({}, actions.setUserName('eric'))).toEqual({ user: 'eric' })
})
})
})
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export { configureStore, getDefaultMiddleware } from './configureStore'
export { createReducer } from './createReducer'
export { default as createSlice } from './createSlice';

export { default as createNextState } from 'immer'
export { combineReducers, compose } from 'redux'
Expand Down