redux-motive
v0.5.0
Published
Simplify writing action creators, reducers and effects - without breaking redux.
Downloads
23
Maintainers
Readme
Redux Motive
Simplify writing action creators, reducers and effects - without breaking redux.
const { reducer, ...actionCreators } = ReduxMotive({
config: {},
sync: {
// Sync function, combines Action Creator and Reducer
addTodo (state, todo) {
return assign({}, state, { todos: [ ...state.todos, todo ] })
},
},
async: {
// Async function, combines Action Creator and Effect
async createTodo (motive, text, isDone) {
const todo = await api('/todo', {text, isDone})
motive.addTodo(todo)
}
},
})
Install
yarn add redux-motive
Requirements
Add redux-thunk
to your store's middleware. See redux-thunk
docs for more details.
yarn add redux-thunk
import thunk from 'redux-thunk'
const store = createStore(reducers, applyMiddleware(thunk))
Preamble
In UI development, our motive's for using redux are predictable.
- Reduce an Action to change the state now, to rerender the UI soon.
- Reduce the lifecycle of side effects, from an Action, to change state over time, to rerender the UI as the side effects progress.
Redux is great for splitting data-flow concerns into small concepts, but it can introduce indirection to a developers code, and at times this becomes the source of errors.
Motive removes indirection, by combining the purpose of a data-flow function to be both an Action Creator and a Reducer, or an Action Creator and an Effect.
Comparison
Generate action creators and a reducer with Motive.
const { reducer, ...actionCreators } = ReduxMotive({
sync: {
// Sync function, combines Action Creator and Reducer
addTodo (state, todo) {
return assign({}, state, { todos: [ ...state.todos, todo ] })
},
},
async: {
// Async function, combines Action Creator and Effect
async createTodo (motive, text, isDone) {
const todo = await api('/todo', {text, isDone})
motive.addTodo(todo)
}
},
})
Write action types, action creators and reducers with common redux boilerplate.
const ADD_TODO = '@@MOTIVE/ADD_TODO'
const CREATE_TODO_START = '@@MOTIVE/CREATE_TODO_START'
const CREATE_TODO_END = '@@MOTIVE/CREATE_TODO_END'
const CREATE_TODO_ERROR = '@@MOTIVE/CREATE_TODO_ERROR'
const reducer = (state, action) => {
switch (action.type) {
case ADD_TODO:
return assign({}, state, { todos: [ ...state.todos, todo ] })
case CREATE_TODO_START:
return assign({}, state, { progressing: true })
case CREATE_TODO_END:
return assign({}, state, { progressing: false })
case CREATE_TODO_ERROR:
return assign({}, state, { error: action.payload, progressing: false })
}
}
const actionCreators = {
addTodo (todo) {
return { type: ADD_TODO, payload: { todo } }
},
createTodo (text, isDone) {
return (dispatch) => {
dispatch({ type: CREATE_TODO_START })
api('/todo', {text, isDone})
.then(todo => {
dispatch(actionCreators.addTodo(todo))
dispatch({ type: CREATE_TODO_END })
})
.catch(err => {
dispatch({ type: CREATE_TODO_ERROR, payload: err })
})
}
}
}
Summary
Inferring common redux patterns into ReduxMotive
allows for less coding.
- Action Creators often pass their params to Reducers in the Action;
ReduxMotive
always does behind the scenes. - The progress of an effect's lifecycle in
ReduxMotive
is reduced to state at common stages: start, end or error. - Dispatching actions from the end of effects is guaranteed;
ReduxMotive
providesdispatch
-bound Action Creators in an effect's first parameter.
API
ReduxMotive( { config, sync, async } )
The returned object can be used to provide a reducer
to the Redux.
Additionally, every function configured for sync
and async
are accessible as dispatchable Action Creators.
const motive = ReduxMotive({
config: {}
sync: {
todo () {},
},
async: {
async fetchTodo () {}
}
});
console.log(motive);
// {
// reducer, Reducer function, wrapping all configured sync fns
// todo, An Action Creator generated from sync.todo
// fetchTodo An Action Creator generated from async.fetchTodo
// }
Configuring
Initial state, default handlers for state/end/error, and optional prefix for action types.
ReduxMotive({
// Default config values
config: {
prefix: '',
initialState: {},
handlers: {
start: (state) => assign({}, state, { progressing: true }),
end: (state) => assign({}, state, { progressing: false }),
error: (state, error) => assign({}, state, { progressing: false, error })
},
}
})
A collection of functions that combine the principles of an Action Creator and a Reducer.
They should:
- Always return new state
- Should not call any "side effects"
const { todo } = ReduxMotive({
sync: {
todo (state, isDone) {
return { ...state, isDone }
}
}
})
dispatch( todo(true) )
Combination of an Action Creator and an Effect.
Function that is given a motive
Object and any additional arguments from the generated Action Creator.
Expected to dispatch new Actions from invoke side effects (like server API calls).
Should return a Promise. The async
function keyword can be used.
motive
Object
dispatch
getState
- Action Creators returned by
ReduxMotive
, bound todispatch
ReduxMotive({
// ...
async: {
async fetchTodo (motive) {
const todo = await api();
motive.todo(todo.isDone)
}
}
})
Lifecycles for an Async Function
Refer to the Comparison for when 'lifecycle' stages are actioned and reduced.
The stages can be overridden:
- In the
config
- Per (asynchronous) function
ReduxMotive({
config: {
handlers: { /* ... */ }
},
async: {
fetchTodo: {
handlers: {
start (state) { /* ... */ },
end (state) { /* ... */ },
error (state) { /* ... */ }
},
async effect (motive) {
const todo = await api();
motive.todo(todo.isDone)
}
}
}
})
Action Types
Action types for each Action Creators are available as properties, which is useful when attempting to match the types in a explicit way.
console.log(motive.todo.ACTION_TYPE)
// @@MOTIVE/<PREFIX>/TODO_SYNC
console.log(motive.fetchTodo.ACTION_TYPE_START)
// @@MOTIVE/<PREFIX>/SYNC_TODO_START
console.log(motive.fetchTodo.ACTION_TYPE_END)
// @@MOTIVE/<PREFIX>/SYNC_TODO_END
console.log(motive.fetchTodo.ACTION_TYPE_ERROR)
// @@MOTIVE/<PREFIX>/SYNC_TODO_ERROR
You don't need to use these if you're dispatching the generated Action Creators.
Alternatives & inspirations
Library | Description --- | --- redux-schemas | Similar redux util library, making different API choices, but with more utility. freactal | Unidirection store for React, with a concise api for async actions and selectors.
License
Licensed under the MIT License, Copyright © 2017-present Lochlan Bunn.