Write your actions and reducers without pain
import { createSymbiote } from 'redux-symbiote'
const initialState = {
error: null,
accounts: [],
loading: false,
}
export const { actions, reducer } = createSymbiote(initialState, {
accounts: {
loading: {
start: () => ({ loading: true }),
failed: (error) => ({ loading: false, error }),
finish: (accounts) => ({ loading: false, accounts }),
},
},
})
function createSymbiote(
initialState,
actionsConfig,
?actionTypePrefix = ''
)
If action handler returns function
, symbiote call it with previous state. On other hand, symbiote merges result of handler call with previous state
createSymbiote(initialState, {
actionType: actionHandler,
nestedType: {
actionType: nestedActionHandler,
}
})
Example:
export const { actions, reducer } = createSymbiote({ value: 1, data: 'another' }, {
increment: () => (prevState) => ({ value: prevState.value + 1 }),
decrement: () => (prevState) => ({ value: prevState.value - 1 }),
setValue: (value) => ({ value }),
setData: (data) => ({ data }),
concatData: (data) => (prevState) => ({ data: data + prevState.data }),
})
dispatch(actions.increment()) // { type: 'increment' }
dispatch(actions.setValue(4)) // { type: 'setValue', payload: [4] }
dispatch(actions.decrement()) // { type: 'decrement' }
dispatch(actions.setData('bar')) // { type: 'setData', payload: ['bar'] }
dispatch(actions.concatData('foo ')) // { type: 'concatData', payload: ['foo '] }
// State here { value: 3, data: 'foo bar' }
Nested example
export const { actions, reducer } = createSymbiote({ value: 1, data: 'another' }, {
value: {
increment: () => (prevState) => ({ value: prevState.value + 1 }),
decrement: () => (prevState) => ({ value: prevState.value - 1 }),
},
data: {
set: (data) => ({ data }),
concat: (data) => (prevState) => ({ data: data + prevState.data }),
},
})
dispatch(actions.value.increment()) // { type: 'value/increment' }
dispatch(actions.value.decrement()) // { type: 'value/decrement' }
dispatch(actions.data.set('bar')) // { type: 'data/set', payload: ['bar'] }
dispatch(actions.data.concat('foo ')) // { type: 'data/concat', payload: ['foo '] }
You can use action as action type in classic reducer or in handleAction(s)
in redux-actions
import { handleActions } from 'redux-actions'
import { createSymbiote } from 'redux-symbiote'
const { actions } = createSymbiote(initialState, {
foo: {
bar: {
baz: (arg1, arg2) => ({ data: arg1, atad: arg2 }),
},
},
})
const reducer = handleActions({
[actions.foo.bar.baz]: (state, { payload: [arg1, arg2] }) => ({
...state,
data: arg1,
atad: arg2,
}),
}, initialState)
createSymbiote
returns object with actions
and reducer
.
Created reducer already handle created actions. You don't need handle actions from symbiote.
// accounts.js
export const { actions, reducer } = createSymbiote(initialState, {
// actions map
})
// reducer.js
import { reducer as accounts } from '../accounts/symbiote'
// another imports
export const reducer = combineReducers({
accounts,
// another reducers
})
Redux recommends create constants, action creators and reducers separately.
const ACCOUNTS_LOADING_START = 'ACCOUNTS_LOADING_START'
const ACCOUNTS_LOADING_FAILED = 'ACCOUNTS_LOADING_FAILED'
const ACCOUNTS_LOADING_FINISH = 'ACCOUNTS_LOADING_FINISH'
export function loadingStart() {
return {
type: ACCOUNTS_LOADING_START,
}
}
export function loadingFailed(error) {
return {
type: ACCOUNTS_LOADING_START,
error,
}
}
export function loadingFinish(accounts) {
return {
type: ACCOUNTS_LOADING_START,
payload: {
accounts,
},
}
}
const initialState = {
error: null,
accounts: [],
loading: false,
}
export function accountsReducer(state = initialState, action) {
switch (action.type) {
case ACCOUNTS_LOADING_START:
return Object.assign({}, state, {
loading: true,
})
case ACCOUNTS_LOADING_FAILED:
return Object.assign({}, state, {
loading: false,
error: action.error,
})
case ACCOUNTS_LOADING_FINISH:
return Object.assign({}, state, {
loading: false,
accounts: action.accounts,
})
}
return state
}
So much boilerplate.
Let's see at redux-actions.
import { createActions, handleActions, combineActions } from 'redux-actions'
export const actions = createActions({
accounts: {
loading: {
start: () => ({ loading: true }),
failed: (error) => ({ loading: false, error }),
finish: (accounts) => ({ loading: false, accounts }),
},
},
}).accounts
const initialState = {
error: null,
accounts: [],
loading: false,
}
export const accountsReducer = handleActions({
[combineActions(actions.loading.start, actions.loading.failed, actions.loading.finish)]:
(state, { payload: { loading } }) => ({ ...state, loading }),
[actions.loading.failed]: (state, { payload: { error } }) => ({ ...state, error }),
[actions.loading.finish]: (state, { payload: { accounts } }) => ({ ...state, accounts }),
}, initialState)