UNPKG

@actualwave/redux-create-reducer

Version:

Function that combines reducers and calls them when action of same name received

313 lines (247 loc) 7.19 kB
# @actualwave/redux-create-reducer This package contains two functions made to create a redux reducer function from smaller per-action functions. So instead of ```javascript import { FETCH_DATA_SUCCESS, FETCH_DATA_FAILURE, UPDATE_DATA_SUCCESS, UPDATE_DATA_FAILURE, DELETE_DATA_SUCCESS, DELETE_DATA_FAILURE, } from './actions'; const initialState = {}; const reducer = (state = initialState, action) => { switch(action.type) { case FETCH_DATA_SUCCESS: ... break; case FETCH_DATA_FAILURE: ... break; case UPDATE_DATA_SUCCESS: ... break; case UPDATE_DATA_FAILURE: ... break; case DELETE_DATA_SUCCESS: ... break; case DELETE_DATA_FAILURE: ... break; } } export default reducer; ``` You may have this structure ```javascript const getInitialState = () => ({}); export const fetchDataSuccess = (state, action) => { ... }; export const fetchDataFailure = (state, action) => { ... }; export const updateDataSuccess = (state, action) => { ... }; export const updateDataFailure = (state, action) => { ... }; export const deleteDataSuccess = (state, action) => { ... }; export const deleteDataFailure = (state, action) => { ... }; export default getInitialState; ``` ## Install Use NPM ``` npm install @actualwave/redux-create-reducer ``` Yarn ``` yarn add @actualwave/redux-create-reducer ``` Or download source from [original gist](https://gist.github.com/burdiuz/b9701e29ac8ed96a85f773c1b663d61d). ## How to use For example, we have three files for actions, reducer and redux configuration in setup file. 1. Define action types in actions file ```javascript export const FETCH_SETTINGS_SUCCESS = 'fetchSettingsSuccess'; ``` The value of action type will be used to determine which function should be called. 2. Use action type to create a reducer function for it in reducer file ```javascript export const fetchSettingsSuccess = (state, { payload }) => ({ ...state, ...payload, initialized: true, }); ``` 3. If default export from reducer file exists, it must be a factory function for default state. If not defined, function that always returns empty object will be used. So, let's add default state factory to reducer file ```javascript export default () => { return { firstSetting: true, secondSetting: false, initialized: false, }; }; ``` 4. Add reducer to redux storage in setup file ```javascript import { createStore, combineReducers } from 'redux'; import { createReducer } from '@actualwave/redux-create-reducer'; import * as settings from './reducer'; const store = createStore( combineReducers({ settings: createReducer(settings), }), ); ``` If you have multiple reducers ```javascript import { createStore, combineReducers } from 'redux'; import { createReducers } from '@actualwave/redux-create-reducer'; import * as items from './items/reducer'; import * as settings from './settings/reducer'; import * as users from './users/reducer'; const store = createStore( combineReducers( createReducers({ items, settings, users, }), ), ); ``` ### API This module exposes two functions - `createReducer()` -- create reducer function from an object with set of action handlers. Each action handler is assigned to action type by property key and will be called only when action of this type passed. ```javascript /* Will create a reducer function which accept "fetchDataSuccess" and "fetchDataFailure" actions. */ const reducer = createReducer({ /* this function will be called only when action with type "fetchDataSuccess" is dispatched: { type: "fetchDataSuccess" } */ fetchDataSuccess: (state, action) => ..., /* this function will be called only when action with type "fetchDataFailure" is dispatched: { type: "fetchDataFailure" } */ fetchDataFailure: (state, action) => ..., }); ``` ```javascript /* Will create a reducer function which accept "fetchDataSuccess" and "fetchDataFailure" actions and have empty `items` array in default state object. */ const reducer = createReducer({ // handles "fetchDataSuccess" actions fetchDataSuccess: (state, action) => ..., // handles "fetchDataFailure" actions fetchDataFailure: (state, action) => ..., // default state factory default: () => ({items: []}), }); ``` Normally its used against exports from one file that describes single reducer ```javascript /* contents of ./data/reducer file Every exported entity must be an action handling function and `export default` must be a default state factory. */ // handles "fetchDataSuccess" actions export const fetchDataSuccess: (state, action) => ...; // handles "fetchDataFailure" actions export const fetchDataFailure: (state, action) => ...; // default state factory export default () => ({items: []}); ``` Import everything from a reducer file and create a reducer function from it. ```javascript import * data from './data/reducer'; const reducer = createReducer(data); ``` - `createReducers()` -- applies `createReducer()` to object properties. ```javascript // create reducers for "data", "settings" and "items" properties const reducers = createReducers({ data, settings, items, }); ``` Is equivalent to ```javascript const reducers = { data: createReducer(data), settings: createReducer(settings), items: createReducer(items), }); ``` Accepts object of objects and returns object with same keys but reducer functions as values. ```javascript const reducers = createReducers({ data: { fetchDataSuccess: (state, action) => ..., fetchDataFailure: (state, action) => ..., default: () => ({ items: [] }), }, }); ``` This code will result in creating a reducer for "fetchDataSuccess" and "fetchDataFailure" actions which will be stored in "data" property: ```javascript { data: (state, action) => { /* "fetchDataSuccess" and "fetchDataFailure" handled here */ ... }, }; ``` Then this object can be passed directly to redux's `combineReducers()`. ```javascript const reducers = combineReducers( createReducers({ data: { fetchDataSuccess: (state, action) => ({ ...state, loaded: true }), fetchDataFailure: (state, action) => ({ ...state, loaded: false }), default: () => ({ items: [], loaded: false }), }, }) ); ``` ### Notes From my experience, `createReducer()` best works with camel-cased action types ```javascript // good const MY_FIRST_ACTION = 'myFirstAction'; // bad const MY_SECOND_ACTION = 'MY_SECOND_ACTION'; ``` In this case you will get these function names for action handlers in reducer file: ```javascript // for "myFirstAction" action type export const myFirstAction = (state, action) => { ... }; // for "MY_SECOND_ACTION" action type export const MY_SECOND_ACTION = (state, action) => { ... }; ``` But its all up to developer how to generate action types, they just need to be valid JS identifiers. > If you like such way of structuring reducers, check [redux-actions](https://redux-actions.js.org/) library, it gives tools for complex and standartized approach for managing actions and reducers.