An opinionated lib to create actions and reducers for Redux
Installations
npm install redux-act
Developer Guide
Typescript
Yes
Module System
CommonJS
Node Version
12.15.0
NPM Version
6.13.4
Releases
Contributors
Unable to fetch Contributors
Languages
JavaScript (97.72%)
TypeScript (2.28%)
validate.email 🚀
Verify real, reachable, and deliverable emails with instant MX records, SMTP checks, and disposable email detection.
Developer
pauldijou
Download Statistics
Total Downloads
12,105,925
Last Day
5,360
Last Week
25,566
Last Month
116,935
Last Year
1,256,801
GitHub Statistics
Apache-2.0 License
1,486 Stars
137 Commits
60 Forks
13 Watchers
4 Branches
19 Contributors
Updated on Mar 07, 2025
Bundle Size
6.17 kB
Minified
1.89 kB
Minified + Gzipped
Package Meta Information
Latest Version
1.8.0
Package Id
redux-act@1.8.0
Size
27.96 kB
NPM Version
6.13.4
Node Version
12.15.0
Published on
Mar 21, 2020
Total Downloads
Cumulative downloads
Total Downloads
12,105,925
Last Day
16.6%
5,360
Compared to previous day
Last Week
0.3%
25,566
Compared to previous week
Last Month
11.4%
116,935
Compared to previous month
Last Year
-50.4%
1,256,801
Compared to previous year
Daily Downloads
Weekly Downloads
Monthly Downloads
Yearly Downloads
redux-act
An opinionated lib to create actions and reducers for Redux. The main goal is to use actions themselves as references inside the reducers rather than string constants.
Install
1# NPM 2npm install redux-act --save 3# Yarn 4yarn add redux-act
You can also use a browser friendly compiled file or the minified version from NPM CDN (mostly for online demo / snippets).
Browser support: this lib uses String.prototype.startsWith
which is not supported by IE11. Be sure to add a polyfill if you are targeting this browser. Learn more.
Content
Usage
Even if there is a function named createAction
, it actually creates an action creator
according to Redux glossary. It was just a bit overkill to name the function createActionCreator
. If you are not sure if something is an action or an action creator, just remember that actions are plain objects while action creators are functions.
1// Import functions 2import { createStore } from 'redux'; 3import { createAction, createReducer } from 'redux-act'; 4 5// Create an action creator (description is optional) 6const add = createAction('add some stuff'); 7const increment = createAction('increment the state'); 8const decrement = createAction('decrement the state'); 9 10// Create a reducer 11// (ES6 syntax, see Advanced usage below for an alternative for ES5) 12const counterReducer = createReducer({ 13 [increment]: (state) => state + 1, 14 [decrement]: (state) => state - 1, 15 [add]: (state, payload) => state + payload, 16}, 0); // <-- This is the default state 17 18// Create the store 19const counterStore = createStore(counterReducer); 20 21// Dispatch actions 22counterStore.dispatch(increment()); // counterStore.getState() === 1 23counterStore.dispatch(increment()); // counterStore.getState() === 2 24counterStore.dispatch(decrement()); // counterStore.getState() === 1 25counterStore.dispatch(add(5)); // counterStore.getState() === 6
FAQ
-
Does it work with Redux devtools? Yes.
-
Do reducers work with combineReducers? Of course, they are just normal reducers after all. Remember that according to the
combineReducers
checks, you will need to provide a default state when creating each reducer before combining them. -
How does it work? There is not much magic. A generated id is prepended to each action type and will be used inside reducers instead of the string constants used inside Redux by default.
-
Can you show how different it is from writing classic Redux? Sure, you can check both commits to update counter example and todomvc example. You can also run both examples with
npm install && npm start
inside each folder. -
Why having two syntax to create reducers? The one with only a map of
action => reduce function
doesn't allow much. This is why the other one is here, in case you would need a small state inside the reducer, having something similar as an actor, or whatever you feel like. Also, one of the syntax is ES6 only. -
Inside a reducer, why is it
(state, payload) => newState
rather than(state, action) => newState
? You can find more info about that on thecreateReducer
API below, but basically, that's because an action is composed of metadata handled by the lib and your payload. Since you only care about that part, better to have it directly. You can switch back to the full action if necessary of course. -
Why have you done that? Aren't string constants good enough? I know that the Redux doc states that such magic isn't really good, that saving a few lines of code isn't worth hiding such logic. I can understand that. And don't get me wrong, the main goal of this lib isn't to reduce boilerplate (even if I like that it does) but to use the actions themselves as keys for the reducers rather than strings which are error prone. You never know what the new dev on your project might do... Maybe (s)he will not realize that the new constant (s)he just introduced was already existing and now everything is broken and a wormhole will appear and it will be the end of mankind. Let's prevent that!
Advanced usage
1import { createStore } from 'redux'; 2import { createAction, createReducer } from 'redux-act'; 3 4// You can create several action creators at once 5// (but that's probably not the best way to do it) 6const [increment, decrement] = ['inc', 'dec'].map(createAction); 7 8// When creating action creators, the description is optional 9// it will only be used for devtools and logging stuff. 10// It's better to put something but feel free to leave it empty if you want to. 11const replace = createAction(); 12 13// By default, the payload of the action is the first argument 14// when you call the action. If you need to support several arguments, 15// you can specify a function on how to merge all arguments into 16// an unique payload. 17let append = createAction('optional description', (...args) => args.join('')); 18 19// There is another pattern to create reducers 20// and it works fine with ES5! (maybe even ES3 \o/) 21const stringReducer = createReducer(function (on) { 22 on(replace, (state, payload) => payload); 23 on(append, (state, payload) => state += payload); 24 // Warning! If you use the same action twice, 25 // the second one will override the previous one. 26}, 'missing a lette'); // <-- Default state 27 28// Rather than binding the action creators each time you want to use them, 29// you can do it once and for all as soon as you have the targeted store 30// assignTo: mutates the action creator itself 31// bindTo: returns a new action creator assigned to the store 32const stringStore = createStore(stringReducer); 33replace.assignTo(stringStore); 34append = append.bindTo(stringStore); 35 36// Now, when calling actions, they will be automatically dispatched 37append('r'); // stringStore.getState() === 'missing a letter' 38replace('a'); // stringStore.getState() === 'a' 39append('b', 'c', 'd'); // stringStore.getState() === 'abcd' 40 41// If you really need serializable actions, using string constant rather 42// than runtime generated id, just use a uppercase description (with eventually some underscores) 43// and it will be use as the id of the action 44const doSomething = createAction('STRING_CONSTANT'); 45doSomething(1); // { type: 'STRING_CONSTANT', payload: 1} 46 47// Little bonus, if you need to support metadata around your action, 48// like needed data but not really part of the payload, you add a second function 49const metaAction = createAction('desc', arg => arg, arg => 'so meta!'); 50 51// Metadata will be the third argument of the reduce function 52createReducer({ 53 [metaAction]: (state, payload, meta) => payload 54});
API
createAction([description], [payloadReducer], [metaReducer])
Parameters
- description (string, optional): used by logging and devtools when displaying the action. If this parameter is uppercase only, with underscores and numbers, it will be used as the action type without any generated id. You can use this feature to have serializable actions you can share between client and server.
- payloadReducer (function, optional): transform multiple arguments as a unique payload.
- metaReducer (function, optional): transform multiple arguments as a unique metadata object.
Usage
Returns a new action creator. If you specify a description, it will be used by devtools. By default, createAction
will return a function and its first argument will be used as the payload when dispatching the action. If you need to support multiple arguments, you need to specify a payload reducer in order to merge all arguments into one unique payload.
1// Super simple action 2const simpleAction = createAction(); 3// Better to add a description 4const betterAction = createAction('This is better!'); 5// Support multiple arguments by merging them 6const multipleAction = createAction((text, checked) => ({text, checked})) 7// Again, better to add a description 8const bestAction = createAction('Best. Action. Ever.', (text, checked) => ({text, checked})) 9// Serializable action (the description will be used as the unique identifier) 10const serializableAction = createAction('SERIALIZABLE_ACTION_42');
action creator
An action creator is basically a function that takes arguments and return an action which has the following format:
type
: generated id + your description.payload
: the data passed when calling the action creator. Will be the first argument of the function except if you specified a payload reducer when creating the action.meta
: if you have provided a metaReducer, it will be used to create a metadata object assigned to this key. Otherwise, it'sundefined
.error
: a boolean indicating if the action is an error according to FSA.
1const addTodo = createAction('Add todo'); 2addTodo('content'); 3// return { type: '[1] Add todo', payload: 'content' } 4 5const editTodo = createAction('Edit todo', (id, content) => ({id, content})); 6editTodo(42, 'the answer'); 7// return { type: '[2] Edit todo', payload: {id: 42, content: 'the answer'} } 8 9const serializeTodo = createAction('SERIALIZE_TODO'); 10serializeTodo(1); 11// return { type: 'SERIALIZE_TODO', payload: 1 }
An action creator has the following methods:
getType()
Return the generated type that will be used by all actions from this action creator. Useful for compatibility purposes.
assignTo(store | dispatch)
Remember that you still need to dispatch those actions. If you already have one or more stores, you can assign the action using the assignTo
function. This will mutate the action creator itself. You can pass one store or one dispatch function or an array of any of both.
1let action = createAction(); 2let action2 = createAction(); 3const reducer = createReducer({ 4 [action]: (state) => state * 2, 5 [action2]: (state) => state / 2, 6}); 7const store = createStore(reducer, 1); 8const store2 = createStore(reducer, -1); 9 10// Automatically dispatch the action to the store when called 11action.assignTo(store); 12action(); // store.getState() === 2 13action(); // store.getState() === 4 14action(); // store.getState() === 8 15 16// You can assign the action to several stores using an array 17action.assignTo([store, store2]); 18action(); 19// store.getState() === 16 20// store2.getState() === -2
bindTo(store | dispatch)
If you need immutability, you can use bindTo
, it will return a new action creator which will automatically dispatch its action.
1// If you need more immutability, you can bind them, creating a new action creator 2const boundAction = action2.bindTo(store); 3action2(); // Not doing anything since not assigned nor bound 4// store.getState() === 16 5// store2.getState() === -2 6boundAction(); // store.getState() === 8
assigned() / bound() / dispatched()
Test the current status of the action creator.
1const action = createAction(); 2action.assigned(); // false, not assigned 3action.bound(); // false, not bound 4action.dispatched(); // false, test if either assigned or bound 5 6const boundAction = action.bindTo(store); 7boundAction.assigned(); // false 8boundAction.bound(); // true 9boundAction.dispatched(); // true 10 11action.assignTo(store); 12action.assigned(); // true 13action.bound(); // false 14action.dispatched(); // true
raw(...args)
When an action creator is either assigned or bound, it will no longer only return the action object but also dispatch it. In some cases, you will need the action without dispatching it (when batching actions for example). In order to achieve that, you can use the raw
method which will return the bare action. You could say that it is exactly the same as the action creator would behave it if wasn't assigned nor bound.
1const action = createAction().bindTo(store); 2action(1); // store has been updated 3action.raw(1); // return the action, store hasn't been updated
asError(...args)*
By default, if your payload is an instance of Error
, the action will be tagged as an error. But if you need to use any other kind of payload as an error payload, you can always use this method. It will apply the same payload reducer by setting the error
to true
.
1const actionCreator = createAction(value => { 2 if (value > 10) { return new Error('Must be less than 10') } 3 return { value: value } 4}) 5 6const goodAction = actionCreator(5) 7goodAction.error // false 8 9const badAction = actionCreator(20) 10badAction.error // true 11 12const forcedBadAction = actionCreator.asError(1) 13forcedBadAction.error // true
createReducer(handlers, [defaultState])
Parameters
- handlers (object or function): if
object
, a map of action to the reduce function. Iffunction
, take two attributes: a function to register actions and another one to unregister them. See below. - defaultState (anything, optional): the initial state of the reducer. Must not be empty if you plan to use this reducer inside a
combineReducers
.
Usage
Returns a new reducer. It's kind of the same syntax as the Array.prototype.reduce
function. You can specify how to reduce as the first argument and the accumulator, or default state, as the second one. The default state is optional since you can retrieve it from the store when creating it but you should consider always having a default state inside a reducer, especially if you want to use it with combineReducers
which make such default state mandatory.
There are two patterns to create a reducer. One is passing an object as a map of action creators
to reduce functions
. Such functions have the following signature: (previousState, payload) => newState
. The other one is using a function factory. Rather than trying to explaining it, just read the following examples.
1const increment = createAction(); 2const add = createAction(); 3 4// First pattern 5const reducerMap = createReducer({ 6 [increment]: (state) => state + 1, 7 [add]: (state, payload) => state + payload 8}, 0); 9 10// Second pattern 11const reducerFactory = createReducer(function (on, off) { 12 on(increment, (state) => state + 1); 13 on(add, (state, payload) => state + payload); 14 // 'off' remove support for a specific action 15 // See 'Adding and removing actions' section 16}, 0);
reducer
Like everything, a reducer is just a function. It takes the current state and an action payload and return the new state. It has the following methods.
options({ payload: boolean, fallback: [handler] })
Since an action is an object with a type
, a payload
(which is your actual data) and eventually some metadata
, all reduce functions directly take the payload as their 2nd argument and the metadata as the 3rd by default rather than the whole action since all other properties are handled by the lib and you shouldn't care about them anyway. If you really need to use the full action, you can change the behavior of a reducer. Returns the reducer itself for chaining.
1const add = createAction(); 2const sub = createAction(); 3const reducer = createReducer({ 4 [add]: (state, action) => state + action.payload, 5 [sub]: (state, action) => state - action.payload 6}, 0); 7 8reducer.options({ 9 payload: false 10});
You can read a more detailed explanation here.
If you specify a fallback
handler, which has the exact same signature as any action handler inside the reducer, it will be called anytime you dispatch an action which is not handled by the reducer.
1const action = createAction(); 2const reducer = createReducer({}, 0); 3reducer.options({ 4 // action is not handled, so fallback will be called 5 fallback: (state, payload) => state + payload, 6}); 7const store = createStore(reducer); 8store.getState(); // 0 9store.dispatch(action(5)); 10store.getState(); // 5 11store.dispatch(action(-10)); 12store.getState(); // -5
has(action creator)
Test if the reducer has a reduce function for a particular action creator or a string type.
1const add = createAction(); 2const sub = createAction(); 3const reducer = createReducer({ 4 [add]: (state, action) => state + action.payload 5}, 0); 6 7reducer.has(add); // true 8reducer.has(sub); // false 9reducer.has(add.getType()); // true
on(action creator | action creator[], reduce function) off(action creator | action creator[])
You can dynamically add and remove actions. See the adding and removing actions section for more infos. You can use either a redux-act
action creator or a raw string type. You can also use array of those, it will apply the on
or off
function to all elements.
They both return the reducer itself so you can chain them.
assignAll(actionCreators, stores)
Parameters
- actionCreators (object or array): which action creators to assign. If it's an object, it's a map of name -> action creator, useful when importing several actions at once.
- stores (object or array): the target store(s) when dispatching actions. Can be only one or several inside an array.
Usage
A common pattern is to export a set of action creators as an object. If you want to bind all of them to a store, there is this super small helper. You can also use an array of action creators. And since you can bind to one or several stores, you can specify either one store or an array of stores.
1// actions.js 2export const add = createAction('Add'); 3export const sub = createAction('Sub'); 4 5// reducer.js 6import * as actions from './actions'; 7export default createReducer({ 8 [actions.add]: (state, payload) => state + payload, 9 [actions.sub]: (state, payload) => state - payload 10}, 0); 11 12// store.js 13import * as actions from './actions'; 14import reducer from './reducer'; 15 16const store = createStore(reducer); 17assignAll(actions, store); 18 19export default store;
bindAll(actionCreators, stores)
Parameters
- actionCreators (object or array): which action creators to bind. If it's an object, it's a map of name -> action creator, useful when importing several actions at once.
- stores (object or array): the target store(s) when dispatching actions. Can be only one or several inside an array.
Usage
Just like assignAll
, you can bind several action creators at once.
1import { bindAll } from 'redux-act'; 2import store from './store'; 3import * as actions from './actions'; 4 5export bindAll(actions, store);
batch(actions)
Parameters
- actions (objects | array): wrap an array of actions inside another action and will reduce them all at once when dispatching it. You can also call this function with several actions as arguments.
:warning: Warning Does not work with assigned and bound actions by default since those will be dispatched immediately when called. You will need to use the raw
method for such actions. See usage below.
Usage
Useful when you need to run a sequence of actions without impacting your whole application after each one but rather after all of them are done. For example, if you are using @connect
from react-redux
, it is called after each action by default. Using batch
, it will be called only when all actions in the array have been reduced.
batch
is an action creator like any other created using createAction
. You can assign or bind it if you want, especially if you only have one store. You can even use it inside reducers. It is enabled by default, but you can remove it and put it back.
1import { createAction, createReducer, batch } from 'redux-act'; 2 3// Basic actions 4const inc = createAction(); 5const dec = createAction(); 6 7const reducer = createReducer({ 8 [inc]: state => state + 1, 9 [dec]: state => state - 1, 10}, 0); 11 12const store = createStore(reducer); 13// actions as arguments 14store.dispatch(batch(inc(), inc(), dec(), inc())); 15// actions as an array 16store.dispatch(batch([inc(), inc(), dec(), inc()])); 17store.getState(); // 4 18 19// Assigned actions 20inc.assignTo(store); 21dec.assignTo(store); 22 23// You still need to dispatch the batch action 24// You will need to use the 'raw' function on the action creators to prevent 25// the auto-dipatch from the assigned action creators 26store.dispatch(batch(inc.raw(), dec.raw(), dec.raw())); 27store.dispatch(batch([inc.raw(), dec.raw(), dec.raw()])); 28store.getState(); // 2 29 30// Let's de-assign our actions 31inc.assignTo(undefined); 32dec.assignTo(undefined); 33 34// You can bind batch 35const boundBatch = batch.bindTo(store); 36boundBatch(inc(), inc()); 37store.getState(); // 4 38 39// You can assign batch 40batch.assignTo(store); 41batch(dec(), dec(), dec()); 42store.getState(); // 1 43 44// You can remove batch from a reducer 45reducer.off(batch); 46batch(dec(), dec()); 47store.getState(); // 1 48 49// You can put it back 50reducer.on(batch, (state, payload) => payload.reduce(reducer, state)); 51batch(dec(), dec()); 52store.getState(); // -1
disbatch(store | dispatch, [actions])
Parameters
- store | dispatch (object, which is a Redux store, or a dispatch function): add a
disbatch
function to the store if it is the only parameter. Just likedispatch
but for several actions which will be batched as a single one. - actions (array, optional): the array of actions to dispatch as a batch of actions.
Usage
1// All samples will display both syntax with and without an array 2// They are exactly the same 3import { disbatch } from 'redux-act'; 4import { inc, dec } from './actions'; 5 6// Add 'disbatch' directly to the store 7disbatch(store); 8store.disbatch(inc(), dec(), inc()); 9store.disbatch([inc(), dec(), inc()]); 10 11// Disbatch immediately from store 12disbatch(store, inc(), dec(), inc()); 13disbatch(store, [inc(), dec(), inc()]); 14 15// Disbatch immediately from dispatch 16disbatch(store.dispatch, inc(), dec(), inc()); 17disbatch(store.dispatch, [inc(), dec(), inc()]);
asError(action)
Parameters
- action (object): a standard Redux action (with a
type
property)
Set the error
property to true
.
1import { createAction, asError } from 'redux-act'; 2 3const goodAction = createAction(); 4goodAction.error; // false 5 6const badAction = asError(goodAction); 7badAction.error; // true
types
This is mostly internal stuff and is exposed only to help during development and testing.
As you know it, each action has a type. redux-act
will ensure that each action creator type is unique. If you are not using serializable actions, you are good to go as all types will be dynamically generated and unique. But if you do use them, by default, nothing prevent you from creating two action creators with the same type. redux-act
will throw if you call createAction
with an already used type, and that is good, except when running tests.
During testing, you might need to reset all types, start as fresh, to prevent redux-act
to throw between tests. To do so, you have a small API to manage types stored by redux-act
.
1import { types } from 'redux-act'; 2 3// Add a type and prevent any action creator from using it from now on 4types.add('MY_TYPE'); 5types.add('MY_TYPE_BIS'); 6 7// Remove a type, you can use it again in an action creator 8types.remove('MY_TYPE_BIS'); 9 10// Test if a type is already used 11types.has('MY_TYPE'); // true 12types.has('MY_TYPE_BIS'); // false 13 14// Check if a type is already used, 15// will throw TypeError if so 16types.check('MY_TYPE') // throw TypeError 17types.check('MY_TYPE_BIS') // do nothing (return undefined) 18 19// Return all used types 20types.all(); // [ 'MY_TYPE' ] 21 22// Remove all types 23types.clear(); 24 25// Disable all type checking meaning you can now have several actions 26// with the same type. This is needed for HMR (Hot Module Replacement) 27// but never never never enable it in production 28types.disableChecking(); 29 30// Set back type checking 31types.enableChecking();
Cookbook
Compatibility
redux-act
is fully compatible with any other Redux library, it just uses type
string after all.
:warning: Warning It's important to remember that all redux-act
actions will store data inside the payload
property and that reducers will automatically extract it by default.
1// Mixing basic and redux-act actions inside a reducer 2import { createAction, createReducer } from 'redux-act'; 3 4// Standard Redux action using a string constant 5const INCREMENT_TYPE = 'INCREMENT'; 6const increment = () => ({ type: INCREMENT_TYPE }); 7 8const decrement = createAction('decrement'); 9 10const reducer = createReducer({ 11 [INCREMENT_TYPE]: (state) => state + 1, 12 [decrement]: (state) => state - 1, 13}, 0); 14 15reducer.has(INCREMENT_TYPE); // true 16reducer.has(decrement); // true
1// Using redux-act actions inside a basic reducer 2import { createAction } from 'redux-act'; 3 4// Standard Redux action using a string constant 5const INCREMENT_TYPE = 'INCREMENT'; 6const increment = () => ({ type: INCREMENT_TYPE }); 7 8const decrement = createAction('decrement'); 9 10function reducer(state = 0, action) { 11 switch (action.type) { 12 case INCREMENT_TYPE: 13 return state + 1; 14 break; 15 case decrement.getType(): 16 return state - 1; 17 break; 18 default: 19 return state; 20 } 21};
Adding and removing actions
Using the handlers object.
1const handlers = {}; 2const reducer = createReducer(handlers, 0); 3const store = createStore(reducer); 4const increment = createAction().assignTo(store); 5 6handlers[increment] = (state) => state + 1; 7 8increment(); // store.getState() === 1 9increment(); // store.getState() === 2 10 11delete(handlers[increment]); 12 13increment(); // store.getState() === 2 14increment(); // store.getState() === 2
Using the on
and off
functions of the reducer. Those functions will be available whatever pattern you used to create the reducer.
1const reducer = createReducer({}, 0); 2const store = createStore(reducer); 3const increment = createAction().assignTo(store); 4 5reducer.on(increment, (state) => state + 1); 6 7increment(); // store.getState() === 1 8increment(); // store.getState() === 2 9 10reducer.off(increment); 11 12increment(); // store.getState() === 2 13increment(); // store.getState() === 2
Using the on
and off
functions of the function factory when creating the reducer.
1const store = createStore(()=> 0)); 2const increment = createAction().assignTo(store); 3const reducer = createReducer(function (on, off) { 4 on(increment, state => { 5 // Just for fun, we will disable increment when reaching 2 6 // (but we will still increment one last time) 7 if (state === 2) { 8 off(increment); 9 } 10 return state + 1; 11 }); 12}, 0); 13 14store.replaceReducer(reducer); 15 16increment(); // store.getState() === 1 17increment(); // store.getState() === 2 18increment(); // store.getState() === 3 19increment(); // store.getState() === 3 20increment(); // store.getState() === 3
Async actions
1import {createStore, applyMiddleware} from 'redux'; 2import thunkMiddleware from 'redux-thunk'; 3import {createAction, createReducer} from 'redux-act'; 4 5const start = createAction(); 6const success = createAction(); 7 8const reducer = createReducer({ 9 [start]: (state) => ({ ...state, running: true }), 10 [success]: (state, result) => ({ running: false, result }) 11}, { 12 running: false, 13 result: false 14}); 15 16// 1) You can use the same way as the Redux samples 17// using thunk middleware 18const createStoreWithMiddleware = applyMiddleware( 19 thunkMiddleware 20)(createStore); 21 22const store = createStoreWithMiddleware(reducer); 23 24function fetch() { 25 // We don't really need the dispatch 26 // but here it is if you don't bind your actions 27 return function (dispatch) { 28 // state: { running: false, result: false } 29 dispatch(start()); 30 // state: { running: true, result: false } 31 return new Promise(resolve => { 32 // Here, you should probably do a real async call, 33 // like, you know, XMLHttpRequest or Global.fetch stuff 34 setTimeout(() => 35 resolve(1) 36 , 5); 37 }).then(result=> 38 dispatch(success(result)) 39 // state: { running: false, result: 1 } 40 ); 41 }; 42} 43 44store.dispatch(fetch()).then(() => { 45 // state: { running: false, result: 1 } 46}); 47 48// 2) You can enjoy the redux-act binding 49// and directly call the actions 50const store = createStore(reducer); 51 52start.assignTo(store); 53success.assignTo(store); 54 55function fetch() { 56 // state: { running: false, result: false } 57 start(); 58 // state: { running: true, result: false } 59 return new Promise(resolve => { 60 // Here, you should probably do a real async call, 61 // like, you know, XMLHttpRequest or Global.fetch stuff 62 setTimeout(() => 63 resolve(1) 64 , 5); 65 }).then(result=> 66 success(result) 67 // state: { running: false, result: 1 } 68 ); 69} 70 71fetch().then(() => { 72 // state: { running: false, result: 1 } 73});
Enable or disable batch
Since batch
is an action creator like any other, you can add and remove it from any reducer.
1import { createReducer, batch } from 'redux-act'; 2const reducer = createReducer({}); 3 4// Reducer no longer support batched actions 5reducer.off(batch); 6 7// Put it back using the reducer itself as the reduce function 8reducer.on(batch, (state, payload) => payload.reduce(reducer, state)); 9 10// Be careful if 'payload' option is false 11reducer.options({ payload: false }); 12reducer.on(batch, (state, action) => action.payload.reduce(reducer, state));
TypeScript
We've built some basic typings around this API that will help TypeScript identify potential issues in your code.
You can use any of the existing methods to create reducers and TypeScript will work (as a superset of Javascript) but that kind of defeats some of the benefits of TypeScript. For this reason, the following is the recommended way to create a reducer.
1import { createReducer, createAction } from 'redux-act'; 2 3const defaultState = { 4 count: 0, 5 otherProperties: any, 6 ... 7}; 8 9const add = createAction<number>('Increase count'); 10 11const reducer = createReducer<typeof defaultState>({}, defaultState); 12 13reducer.on(add, (state, payload) => ({ 14 ...state, 15 count: state.count + payload 16}));
Using the reducer.on()
API, TypeScript will identify the payload set on add
and provide that type as payload. This can be really handy once your code starts scaling up.
Caveats
Due to some limitations on TypeScript typings, action creators have some limitations but you can create typed action creators assuming you have no payload reducer.
1import { createAction } from 'redux-act'; 2 3const action = createAction<boolean>('Some type'); 4const emptyAction = createAction('Another type'); 5const otherAction = createAction<boolean>('Other action', (arg1, arg2) => ({ arg1, arg2 }));
action
and emptyAction
will provide typing support, making sure action
is provided a boolean as first and only argument, or emptyAction
is not provided any argument at all.
otherAction
, on the otherhand, will be able to be called with any arguments, regardless of what the payload reducer expects.
Loggers
redux-act
provides improved logging with some loggers, mostly for batched actions.
Missing your favorite one? Please open an issue or a pull request, it will be added as soon as possible.
Redux Logger
1import { applyMiddleware, createStore } from 'redux'; 2import createLogger from 'redux-logger'; 3import { loggers } from 'redux-act'; 4 5// Init logger 6const logger = createLogger({ 7 ...loggers.reduxLogger, 8 // You can add you own options after that 9}); 10 11// Same as 12const logger = createLogger({ 13 actionTransformer: loggers.reduxLogger.actionTransformer, 14 logger: loggers.reduxLogger.logger, 15 // You can add you own options after that 16}); 17 18// Create the store 19const store = applyMiddleware(logger)(createStore)(/* reducer */);
Thanks
A big thank to both @gaearon for creating Redux and @AlexGalays for creating fluxx which I took a lot of inspiration from.
Tests
If you need to run the tests, just use npm test
or npm run coverage
if you want to generate the coverage report.
License
This software is licensed under the Apache 2 license, quoted below.
Copyright 2015 Paul Dijou (http://pauldijou.fr).
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

No vulnerabilities found.
Reason
no binaries found in the repo
Reason
license file detected
Details
- Info: project has a license file: LICENSE:0
- Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0
Reason
Found 10/25 approved changesets -- score normalized to 4
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
- Warn: no security policy file detected
- Warn: no security file to analyze
- Warn: no security file to analyze
- Warn: no security file to analyze
Reason
project is not fuzzed
Details
- Warn: no fuzzer integrations found
Reason
branch protection not enabled on development/release branches
Details
- Warn: branch protection not enabled for branch 'master'
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
- Warn: 0 commits out of 15 are checked with a SAST tool
Reason
39 existing vulnerabilities detected
Details
- Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92
- Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw
- Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg
- Warn: Project is vulnerable to: GHSA-x9w5-v3q2-3rhw
- Warn: Project is vulnerable to: GHSA-w8qv-6jwh-64r5
- Warn: Project is vulnerable to: GHSA-wg6g-ppvx-927h
- Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275
- Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c
- Warn: Project is vulnerable to: GHSA-w573-4hg7-7wgq
- Warn: Project is vulnerable to: GHSA-vh7m-p724-62c2
- Warn: Project is vulnerable to: GHSA-r9p9-mrjm-926w
- Warn: Project is vulnerable to: GHSA-434g-2637-qmqr
- Warn: Project is vulnerable to: GHSA-49q7-c7j4-3p7m
- Warn: Project is vulnerable to: GHSA-977x-g7h5-7qgw
- Warn: Project is vulnerable to: GHSA-f7q4-pwc6-w24p
- Warn: Project is vulnerable to: GHSA-fc9h-whq2-v747
- Warn: Project is vulnerable to: GHSA-vjh7-7g9h-fjfh
- Warn: Project is vulnerable to: GHSA-2j2x-2gpw-g8fm
- Warn: Project is vulnerable to: GHSA-4q6p-r6v2-jvc5
- Warn: Project is vulnerable to: GHSA-ww39-953v-wcq6
- Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h
- Warn: Project is vulnerable to: GHSA-jf85-cpcp-j695
- Warn: Project is vulnerable to: GHSA-fvqr-27wr-82fm
- Warn: Project is vulnerable to: GHSA-4xc9-xhrj-v574
- Warn: Project is vulnerable to: GHSA-x5rq-j2xg-h7qm
- Warn: Project is vulnerable to: GHSA-p6mc-m468-83gw
- Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9
- Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm
- Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv
- Warn: Project is vulnerable to: GHSA-hxm2-r34f-qmc5
- Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3
- Warn: Project is vulnerable to: GHSA-vh95-rmgr-6w4m
- Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h
- Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9
- Warn: Project is vulnerable to: GHSA-g6ww-v8xp-vmwg
- Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw
- Warn: Project is vulnerable to: GHSA-g4rg-993r-mgx7
- Warn: Project is vulnerable to: GHSA-4wf5-vphf-c2xc
- Warn: Project is vulnerable to: GHSA-c4w7-xm78-47vh
Score
2.3
/10
Last Scanned on 2025-03-03
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn MoreOther packages similar to redux-act
redux-act-reducer
A lib to create actions and reducers for Redux
redux-act-async
Reducing the boilerplate of redux asynchronous applications
redux-symbiote
Write your actions and reducers without pain
redux-act-dispatch-free
extend 'redux-act' package for async actions call without dispatch