reduxsauce-crud
Adapted from reduxsauce
Usage
yarn add https://github.com/AlexHenkel/reduxsauce-crud.git
Overview
This library intends to be a powered version of reduxsauce, so the available functions are the same:
- createReducer
- createTypes
- createActions
Check the oficial docs before using this library
The features added here intend to reduce boilerplate while working with CRUD systems to have a nice DRY code on redux files. This will increase scalability and maintainability as there's only one file to maintain.
We assume that the following pattern is used to handle API request through redux:
- Add the following props to handle the state while requesting some data to API:
fetching
: Flag to provide a nice loading message while request is in processresults
: The array or object with the data fetched from APIerror
: Flag to handle API errors in the UI, and even get error messages directly from API
- Create the following action creators and reducers:
FETCH_REQUEST
: Will setfetching
flag to trueFETCH_SUCCESS
: Will setfetching
to false and store the data from APIFETCH_ERROR
: Will setfetching
to false and the error from APIFETCH_RESET
: Reset to initial state to clear results or error
As this is a very repetitive process, we intend to abstract the CRUD requests, as they will be used in the same way across the process. (And for special cases, there's always a way to create custom actions)
Note: This library is optimized to be used along redux-observable-crud to handle ansync requests with Redux
createActions
Originally, this function has a second parameter options. So we made available a new default option called defaultActions to generate CRUD ActionCreators and Types
src/Data/Redux/UsersRedux.js
import { createActions } from 'reduxsauce-polymer'
export const UsersRedux = createActions({}, {
prefix: 'USERS_',
defaultActions: {
get: true,
getOne: true,
create: true,
update: true,
remove: true,
reset: true,
},
})
const { Types, Creators } = UsersRedux
export const UsersTypes = Types
export default Creators
src/Components/Users.jsx
import UsersActions from '../../Data/Redux/UsersRedux'
...
const mapDispatchToProps = dispatch => ({
getUsers: () => dispatch(UsersActions.getRequest()),
})
The following action creators are available. Only new methods are explained
- get:
getRequest(data, force)
data
: Allows you to pass optional dataforce
: Force an API call, since the default behavior is to look for local data
getSuccess(results)
getFailure(error)
getReset()
- getOne:
getOneRequest(id, data, force)
id
: Specific Id to be fetched from APIdata
: Allows you to pass optional dataforce
: Force an API call, since the default behavior is to look for local data
getOneSuccess(id, result, noResolve)
result
: This is assumed to be an objectnoResolve
: When true, preventsfetching
flag to be set to false
getOneFailure(error)
getOneReset()
:getOneCreateFrom(newElement, property, customFilter = null)
: Handle a situation wheregetOne
result have nested array, related to another entity. To allow sync, this function appends a new instance to a given path of agetOne
result.newElement
: The element to appendproperty
: The key ofgetOne.result
where thenewElement
belongscustomFilter(result, newElement)
: It comparesgetOne.result
andnewElement
and return true if they are related, therefore,newElement
added.
getOneUpdateFrom(newElement, property)
: Same asgetOneCreateFrom
but to update an element. In order to find thenewElement
in the existing object, it's compared by id.newElement
: The element to appendproperty
: The key ofgetOne.result
where thenewElement
belongscustomFilter(result, newElement)
: It comparesgetOne.result
andnewElement
and return true if they are related, therefore,newElement
added.
getOneRemoveFrom(id, property)
: Same asgetOneCreateFrom
but to remove an element. It only look for the id in theproperty
path and remove it if it's found.newElement
: The element to appendproperty
: The key ofgetOne.result
where thenewElement
belongscustomFilter(result, newElement)
: It comparesgetOne.result
andnewElement
and return true if they are related, therefore,newElement
added.
getOneFromState(id, path)
: Allows you to select an object from any path in the state. This intends to "save" an innecesary API callid
: Specific Id to be looked atpath
: Path in state of the data we are going to extract for
- create:
createRequest(data)
createSuccess(result)
: Append the new element toget.result
createFailure(error)
createReset()
- update:
updateRequest(id, data)
updateSuccess(result)
: Try to find the element inget.results
andgetOne.result
to update those as well.updateFailure(error)
updateReset()
- remove:
removeRequest(id, data)
removeSuccess(result)
: Try to find the element inget.results
andgetOne.result
to remove them as well.removeFailure(error)
removeReset()
- reset:
reset()
: Reset everything to the initial state
createState
Add a second parameter to set default Actions initial state.
src/Data/Redux/UsersRedux.js
import { createActions } from 'reduxsauce-polymer'
export const INITIAL_STATE = createState({}, {
get: true,
getOne: true,
getOneInitial: {
/**
* This will set a default value on
* `state.users.getOne.result.sessions`, so array operations
* are always available without checking if any data exists!!
**/
sessions: []
},
create: true,
update: true,
remove: true,
})
src/Components/Users.jsx
...
const mapStateToProps = state => ({
loading: state.users.get.fetching,
users: state.users.get.results,
sessions: state.users.getOne.result.sessions,
})
This will be the complete tree available for users:
├── users
│ ├── get
│ │ ├── fetching: false
│ │ ├── error: null
│ │ ├── results: []
│ ├── getOne
│ │ ├── fetching: false
│ │ ├── error: null
│ │ ├── result:
│ │ │ ├── sessions * (Specific for this example)
│ ├── create
│ │ ├── fetching: false
│ │ ├── error: null
│ │ ├── success: false
│ ├── update
│ │ ├── fetching: false
│ │ ├── error: null
│ │ ├── success: false
│ ├── remove
│ │ ├── fetching: false
│ │ ├── pending: false
│ │ ├── error: null
│ │ ├── success: false
createReducer
Add a third parameter of options. Here all the reducers are created to be available as actionCreators mentioned earlier. They need the Types generated also by createActions()
.
src/Data/Redux/UsersRedux.js
import { createActions } from 'reduxsauce-polymer'
export const reducer = createReducer(INITIAL_STATE, {}, {
defaultActions: {
get: true,
getOne: true,
create: true,
update: true,
remove: true,
reset: true,
},
Types,
})
src/Data/Redux/index.js
/* ------------- Assemble The Reducers ------------- */
const rootReducer = combineReducers({
users: require('./UsersRedux').reducer,
})