https://github.com/klen/redux-code
Yet another creators library
https://github.com/klen/redux-code
redux redux-helpers redux-thunk
Last synced: 2 months ago
JSON representation
Yet another creators library
- Host: GitHub
- URL: https://github.com/klen/redux-code
- Owner: klen
- License: mit
- Created: 2017-09-28T12:55:06.000Z (almost 9 years ago)
- Default Branch: develop
- Last Pushed: 2023-03-07T07:52:16.000Z (over 3 years ago)
- Last Synced: 2025-07-07T11:44:42.640Z (about 1 year ago)
- Topics: redux, redux-helpers, redux-thunk
- Language: TypeScript
- Homepage:
- Size: 1.01 MB
- Stars: 1
- Watchers: 2
- Forks: 0
- Open Issues: 5
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
README
# redux-code
[](https://github.com/klen/redux-code/actions/workflows/test.yml)
[](https://github.com/klen/redux-code/actions/workflows/build.yml)
[](https://badge.fury.io/js/redux-code)
A simple set of tools to make using Redux easier
Features:
* [createReducer](#createreducer) -- A utility to create reducers (with immer support)
* [createActions](#createactions) -- A utility to create actions (with thunk, promises support)
* [skipMiddleware](#skipmiddleware) -- A Redux middleware to add ability to skip actions
* [persistReducer, persistStore](#persistReducer) -- Make state persistent
## Installation
```bash
npm install --save redux-code
```
## Usage
### createActions
A helper to build actions creator.
```javascript
import {createActions} from 'redux-code'
const actions = createActions('prefix/', {
// Basic action creator without payload
action1: undefined,
// Basic action creator with static payload
action2: true,
// Basic action creator with dynamic payload
action3: (num) => num,
// You may return other actions as well
random: () => (Math.random() < 0.5 ? actions.action1() : actions.action2()),
})
// Call creators to build actions
expect(actions.action1()).toEqual({ type: 'prefix/action1' })
expect(actions.action2()).toEqual({ type: 'prefix/action2', payload: true })
expect(actions.action3(42)).toEqual({ type: 'prefix/action3', payload: 42 })
expect([actions.action1(), actions.action2()]).toContainEqual(actions.random())
// Get action types
expect(actions.action1.type).toBe('prefix/action1')
expect(actions.action2.type).toBe('prefix/action2')
expect(actions.action3.type).toBe('prefix/action3')
```
You may also pass an array of strings to create simple actions
```javascript
const actions = createActions('prefix/', ['init', 'update'])
expect(actions.init()).toEqual({type: 'prefix/init'})
expect(actions.update(42)).toEqual({type: 'prefix/update', payload: 42})
```
#### Promises and thunks
`Redux-Code` supports `redux-thunk` actions as well as async actions.
```javascript
import {SKIP} from 'redux-code'
const actions = createActions('thunk:', {
// Just an update action
update: (payload) => payload,
// Use redux-thunk (return function)
init: () => (dispatch, getState) => {
const state = getState()
if (!state.inited) dispatch(actions.update({ inited: true }))
},
// Promises also supported
load: async () => {
// Just pretend we have an IO call here
const res = await Promise.resolve(Math.random() * 100 + 1)
return res
}
})
store.dispatch(actions.init())
await store.dispatch(actions.load())
```
#### Mixins
You may create and use mixins with different actions.
```javascript
const UpdateMixin = {
update: (payload) => payload
}
const DisableMixin = {
// Pay attention that we don't use arrow function here to allow redux-code bind this to a created actions
disable: async function(dispatch) {
this.update({disabled: true})
await self.save()
}
}
const usersActions = createActions('users/', UpdateMixin, DisableMixin, {
save: async () => {
// ...
}
})
const commentsActions = createActions('comments/', UpdateMixin, DisableMixin, ['another1', 'another2'], {
save: async () => {
// ...
}
})
expect(usersActions.disable.type).toEqual('users/disable')
expect(commentsActions.disable.type).toEqual('comments/disable')
```
### createReducer
#### Basic Usage
Simplifying Reducers with `createReducer`:
```javascript
import { createReducer } from 'redux-code'
const initial = 42
// instead this
const oldReducer(state=initial, action) {
switch(action.type) {
case 'increment':
return state + 1
case 'decrement':
return state - 1
default:
return state
}
}
// Write this
const newReducer = createReducer(initial, {
// Map actions types to handlers
increment: (state, action) => state + 1,
decrement: (state, action) => state - 1,
})
```
Internally `createReducer` uses [Immer](https://github.com/mweststrate/immer)
library, which lets you write code that "mutates" some data, but actually
applies the updates immutably.
##### Create reducer without immer
The library provides method `createBaseReducer` which one doesn't use `immer`
internally.
```javascript
import { createBaseReducer } from 'redux-code'
const initial = 42
const reducer = createBaseReducer(initial, {
// Map actions types to handlers
increment: (state, action) => state + 1,
decrement: (state, action) => state - 1,
})
```
#### Using with `createActions`
Sure you may use `createActions` with the `createReducer` together
```javascript
const actions = createActions('counter/', { increment: undefined, decrement: undefined })
const reducer = createReducer(initial, {
[actions.increment]: (state, action) => state + 1,
// For typescript use `action.type`
[actions.decrement.type]: (state, action) => state - 1,
})
```
#### Mixins
You may create and use mixins with your reducers.
```javascript
// Common Reducer, supports reset (reset state) and update (update state with the given payload) actions
const CommonMixinCreator = (initial, actions) => ({
[actions.reset.type]: (state) => initial,
[actions.update.type]: (state, { payload }) => ({ ...state, ...payload }),
})
const actions = createActions('users/', {
reset: true,
update: (payload) => payload,
load: () => {
// ...
}
})
const initial = {}
// Create a reducer with your mixin
const reducer = createReducer(initial, CommonMixinCreator(initial, actions), {
// Only define new actions
[actions.load.type]: (state) => ({ ...state, loaded: Date.now() }),
})
let state = reducer(initial, actions.update({ data: 42 }))
expect(state).toEqual({ data: 42 })
state = reducer(state, actions.reset())
expect(state).toBe(initial)
```
### skipMiddleware
You may use `skipMiddleware` with `SKIP` action to (surprise) skip some actions.
Just return SKIP from actions creators every where you want to skip.
```javascript
import { SKIP, skipMiddleware } from 'redux-code'
// .. Add skipMiddleware to your store
const initial = {}
const actions = createActions('example/', {
// The actions is skipable by an condition
conditionalAction: () => {
// Skip the action
if (Math.round() < 0.5) return SKIP
// Or do it as well
return true
}
})
```
### Mixins
The package includes some mixins for actions and reducers
#### Common
```javascript
import {commonActions, commonReducer} from 'redux-code'
const actions = createActions('example/', commonActions, {
// .. your other action creators here
})
const reducer = createReducer(initial, commonReducer(actions, initial), {
// .. your other actions handlers here
})
// commonActions, commonReducer supports such actions as:
// * reset -- to reset your reducer state
// actions.reset()
// * update -- to update your reducer state from the provided payload
// actions.update({'updates': 'here'})
```
#### Entities
```javascript
import {entitiesActions, entitiesReducer} from 'redux-code'
const actions = createActions('base/', entitiesActions, {
// .. your other action creators here
})
const reducer = createReducer(initial, entitiesReducer(actions), {
// .. your other actions handlers here
})
// entitiesActions supports such actions as:
// * addOne -- to add an entity
// * addMany -- to add many entities
// * updateOne -- to update an entity
// * updateMany -- to update many entities
// * upsertOne -- to add/update an entity
// * upsertMany -- to add/update many entities
// * setOne -- to add/set an entity
// * setMany -- to add/set many entities
// * setAll -- to replace all entities
// * removeOne -- to remove an entity
// * removeMany -- to remove many entities
// * removeAll -- to remove all entities
```
### persistReducer
```javascript
import {commonActions, commonReducer, persistReducer, persistStore} from 'redux-code'
import {localStorage} from 'redux-core/persist'
const actions = createActions('example/', commonActions, {
// .. your other action creators here
})
const reducer = persistReducer(
{key: 'storeKey', storage: localStorage},
createReducer(initial, commonReducer(actions, initial), {
// .. your other actions handlers here
})
)
const store = createStore(reducer, {})
const persistor = persistStore(store)
// State will be persisted in localStorage
store.dispatch(actions.update({value: 42}))
// Pause persistence for the storeKey
persistor.pause()
// Resume persistence for the storeKey
persistor.persist()
// Purge data
persistor.purge()
```
## License
This project is licensed under the MIT license, Copyright (c) 2017 Kirill Klenov. For more information see `LICENSE.md`.
## Acknowledgements
[Dan Abramov](https://github.com/gaearon) for Redux