
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
Redan is an opinionated set of utility methods for Redux. The library has two aims:
It's based on the work of Redux Act but with less magic and an extra focus on simplifying thunks.
It's called Redan because it favours the way I (Dan) personally like to use Redux.
yarn add redan
Redan exports three utility functions.
You can import them like so:
import { createAction, createErrorAction, createThunk } from 'redan';
The basic idea is that using string constants to identify action types is messy. Here's an example of how action creators are normally used in Redux:
action-types.js
export const ADD_TODO = 'ADD_TODO';
export const COMPLETE_TODO = 'COMPLETE_TODO'
actions.js
import * as types from './action-types';
export const createAction(types.ADD_TODO);
export const createAction(types.COMPLETE_TODO);
reducer.js
import * as types from './action-types';
export default (state, action) => {
switch (action.type) {
case types.ADD_TODO:
...
case types.COMPLETE_TODO:
...
}
};
The problem is that having action type constants that are separate from the actions is causing indirection. If you want to find anywhere that the addTodo action is being used, you have to search for all usages of both addTodo and ADD_TODO in your codebase, because your reducer identifies the action using the string constant and your components will dispatch the action using the action creator.
But what if we make the action type accessible from the action creator?
const addTodo = createAction('ADD_TODO');
addTodo.type; // => 'ADD_TODO'
Now our code looks like this:
actions.js
import * as types from './action-types';
export const createAction('ADD_TODO');
export const createAction('COMPLETE_TODO');
reducer.js
import * as actions from './actions';
export default (state, action) => {
switch (action.type) {
case actions.addTodo.type:
...
case actions.completeTodo.type:
...
}
};
Now, by searching for 'addTodo' you can find all usages of the action. happy days 👍
Creates simple Redux action creators. It accepts a string as an action type and returns an action creator with the action type bound to it.
The action creator accepts a single payload argument which can be anything.
const addTodo = createAction('ADD_TODO');
addTodo('Buy milk'); // => { type: 'ADD_TODO', payload: 'Buy milk' }
/*
* The action type is bound to the action creator
*/
addTodo.type; // => 'ADD_TODO'
Creates error action creators. It accepts a string as an action type and returns an error action creator with the action type bound to it.
The action creator accepts error and payload arguments.
const todoError = createErrorAction('TODO_ERROR');
todoError(new Error('invalid todo'), 'Dont buy milk'); // => { type: 'ADD_TODO', error: Error, payload: 'Dont buy milk' }
/*
* The action type is bound to the action creator
*/
todoError.type; // => 'ADD_TODO'
Creates a thunk with start, end and error actions attached to it.
When creating a thunk, there are often 3 useful related actions to dispatch within the thunk:
The createThunk utility accepts an action type and a callback. It creates these 3 useful actions and dispatches them for you. It also binds the 3 action types to itself so you can reference them in the reducer.
const fetchTodos = createThunk(
'FETCH_TODOS',
user_id => async (dispatch, getState) => {
const response = await fetch('www.todos.com/todos');
return response.json();
}
);
/*
* The start, end and error types are bound to the thunk
*/
fetchTodos.start.type // => 'FETCH_TODOS_START'
fetchTodos.end.type // => 'FETCH_TODOS_END'
fetchTodos.error.type // => 'FETCH_TODOS_ERROR'
The callback is passed dispatch and getState just like a regular thunk in case you want to do anything more fancy and dispatch extra actions within your callback.
When fetchTodos is dispatched with redux-thunk the following happens:
The start action is dispatched. The payload is whatever payload is passed to the thunk.
dispatch(fetchTodos(123));
// dispatches { type: 'FETCH_TODOS_START', payload: 123 }
The end action is dispatched once the callback has completed. The callback can be asynchronous if you like. The payload of the end action is whatever is returned from the callback.
const fetchTodos = createThunk('FETCH_TODOS', user_id => () => {
return 'hello';
});
// dispatches { type: 'FETCH_TODOS_END', payload: 'hello' }
The end action is NOT dispatched if the callback throws an error.
If the callback throws, the error action gets dispatched instead of the end action. The error action is passed the error and whatever payload was passed to the thunk.
const fetchTodos = createThunk('FETCH_TODOS', user_id => () => {
throw new Error('oh no');
});
dispatch(fetchTodos(123));
// dispatches { type: 'FETCH_TODOS_ERROR', error: Error, payload: 123 }
actions.js
import { createAction, createThunk } from 'redan';
export const addTodo = createAction('ADD_TODO');
export const completeTodo = createAction('COMPLETE_TODO');
export const fetchTodos = createThunk('FETCH_TODOS', user_id => () => {
const response = fetch('www.todos.com/todos');
return response.json();
});
reducer.js
import * as actions from './actions';
export default (state, action) => {
const { type, payload, error } = action;
switch (type) {
case actions.addTodo.type:
return {
...state,
todos: [ ...state.todos, payload ]
};
case actions.completeTodo.type:
return {
...state,
completed: [ ...state.completed, payload ]
};
case fetchTodos.start.type:
return {
...state,
isLoadingTodos: true,
error: null
};
case fetchTodos.end.type:
return {
...state,
isLoadingTodos: false,
todos: payload
};
case fetchTodos.error.type:
return {
...state,
isLoadingTodos: false,
error: error.message
};
default:
return state;
}
}
FAQs
A tiny library to make redux more manageable
We found that redan demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.