redux-elements
v1.12.3
Published
Opinionated framework for redux state elements
Downloads
136
Maintainers
Readme
Redux-elements
Redux-elements is an opinionated framework for holding normalized, log-based state in redux. It comes with the following features:
- Automatically defined actions to update the data
- Automatically defined auto-caching selectors based on reselect
- Can automatically sync with an API module
- Supports roll-back and "time travel" of actions, efficiently recalculating store state
Redux-elements uses immutable.js, reselect and redux-saga. It is compatible with react-redux.
Docs
Check out the documentation pages.
Installation
You can install this repository via npm.
npm i redux-elements
Automatically defined actions
No need to endlessly redefine simple CRUD actions.
Redux-elements supports the following actions out of the box:
- add: Adds one or more items to the data
- set: Wipes existing data and replaces it with one or more elements
- clear: Wipes existing data, resets this element to initial (empty) state
- update: Updates one or more rows in the data
- upsert: Update or create one or more rows in the data
- patch: Update select columns of one or more rows in the data
- delete: Delete one or more rows in the data
Example:
const Session = new StateElement("session","");
const User = new StateElement("user","");
const store = createStore(combineReducers({
sessions: Session.reducer,
users: User.reducer
}));
store.dispatch(
Session.changeState.set([
{
id: "sid1",
userId: "uid1"
},
{
id: "sid2",
userId: "uid1"
}
])
);
store.dispatch(
Session.changeState.upsert([
{
id: "sid3",
userId: "uid3"
},
{
id: "sid2",
userId: "newUserId"
}
])
)
store.getState()
/* {
sessions: [
{
id: "sid1",
userId: "uid1"
},
{
id: "sid2",
userId: "newUserId"
},
{
id: "sid3",
userId: "uid3"
}
],
users: []
}
*
Auto-caching selectors
Redux-elements automatically exposes chainable, optimally caching selectors, supporting database-style joins.
Example:
User.select("duplicateNames")
.fromState(state)
.keyBy("firstName") // autocaching
.filter(listOfUsers => listOfUsers.length>=2, 2) // caching by parameter `2`
.map(listOfUsers => listOfUsers.first(), "first") // caching by parameter `"first"`
User.select("duplicateNames")
.fromState(state)
.keyBy("firstName") // uses cached result
.filter(listOfUsers => listOfUsers.length>=2, 2) // uses cached result
.map(listOfUsers => listOfUsers.last(), "last") // calculated since caching parameters not equal
See documentation for more details.
Roll-back and time-travel of actions
Redux-elements is built for eager execution of actions in the frontend and subsequent sync with a stream-processing backend, e.g. Apache Kafka.
This means that Redux-Elements needs to support roll-back and reordering of actions. An example is the following chain of events:
- A frontend using Redux-elements dispatches and eagerly executes action
a1
, changing a part of the store. The frontend sendsa1
via API to the backend. - Another frontend or part of the backend dispatches action
a0
, which arrives at the backend before actiona1
. - Action
a0
is processed by the backend - the state in the backend is therefore defined by the actions sequence[a0]
. Actiona0
is sent to all frontends. - Action
a1
arrives in the backend and is processed there - the "true" state is defined by the action sequence[a0, a1]
. Actiona1
(and its order in the action sequence) is sent back to all frontends. - Action
a0
arrives in the frontend using Redux-elements. It needs to be inserted before all actions which are currently "local only" - i.e. don't have a response from backend yet. You can use the Redux-elementsTimeMachine
object for this, wrapping the action withTimeMachine.makeActionInsertBefore(a0, "id of a1")
. Redux-elements efficiently recalculates the state froma0
, leaving all previous (still valid) actions untouched. - Action
a1
arrives back in the frontend using Redux-elements. Since it's already processed at the right position in the action sequence, nothing needs to be done except noting its "true" position in the backend sequence of actions.
import { createStore } from "redux";
import { TimeMachine } from "redux-elements";
// ...
const tm = new TimeMachine({
users: userReducer,
projects: projectReducer
})
const store = createStore(tm.reducer)
// ...
const action = tm.createRollbackAction("actionId5");
store.dispatch(action) // rolls back action with id "actionId5"
// ...
const actionNormal = {
type: "ADD_USER",
payload: { ... },
actionId: "something"
}
const actionTimeTraveller = tm.makeActionInsertBefore(actionNormal, "actionId6");
store.dispatch(actionTimeTraveller) // action is executed before action with id "actionId6"
License
Redux-elements is published under MIT license.