Skip to content

Instantly share code, notes, and snippets.

@burdiuz

burdiuz/.npmignore

Last active Dec 15, 2019
Embed
What would you like to do?
Redux Side Effects

@actualwave/redux-side-effect

Very simple to use middleware for redux to make side effects for actions. Inspired by redux-saga but not uses generators, instead relies on async functions and can be used with redux-thunk.

How to use

  1. Register middleware
import sideEffectMiddleware from '@actualwave/redux-side-effect';

const store = createStore(
 myReducers,
 applyMiddleware(sideEffectMiddleware),
);
  1. Add side effects
import { take, put, select, call } from '@actualwave/redux-side-effect';  

take('FETCH_DATA', async (action) => {
  const { data } = await axios.get('/something');
  
  put({ type: 'FETCH_DATA_SUCCESS', payload: data });
});

take('POST_DATA', async (action) => {
  // ....
});

take('PATCH_DATA', async (action) => {
  // ....
});

That's it.

Big gotcha that may save you from a headache or sleepless night

This middleware will work with single redux store, it's not a problem for most of the projects because developers usually create one redux store per application. But for projects that may be using multiple stores and this middleware, they have to generate middlewares for each store, like this:
main-redux-store.js

import { createNamespace } from '@actualwave/redux-side-effect';

const { put, call, select, take, middleware } = createNamespace();

const store = createStore(
  mainReducers,
  applyMiddleware(middleware),
);

export { put, call, select, take };

sub-redux-store.js

import { createNamespace } from '@actualwave/redux-side-effect';

const { put, call, select, take, middleware } = createNamespace();

const store = createStore(
  subReducers,
  applyMiddleware(middleware),
);

export { put, call, select, take };

With such setup you could use separate sets of functions for different stores side-effects.js

import * as main from 'main-redux-store.js';
import * as sub from 'sub-redux-store.js';

main.take('ACTION_TYPE', () => main.put({ type: 'ACTION_SUCCESS' }))

const createSubEffect = ({ take, put }) => {
 take('GENERAL_ACTION_TYPE', () => put({ type: 'GENERAL_ACTION_SUCCESS' }));
};

createSubEffect(main);
createSubEffect(sub);

An example:

import sideEffectMiddleware, { take, put, select, call } from '@actualwave/redux-side-effect';

const store = createStore(
  myReducers,
  compose(
    applyMiddleware(thunkMiddleware, sideEffectMiddleware),
    // Redux Tool Google Chrome Extension setup
    window.__REDUX_DEVTOOLS_EXTENSION__
      ? window.__REDUX_DEVTOOLS_EXTENSION__()
      : compose,
  ),
);

// Side effect function could be a normal function too
take('action-type', (action) => {
  console.log('MY ACTION TYPE INTERCEPTED:', action);

  put({ type: 'side-effect-success' });
  
  select((store) => console.log(store));
});

take('async-action-type', async (action) => {
  console.log('MY ACTION TYPE INTERCEPTED:', action);

  const value = await call(myFunction, 'arg1', 'arg2', 'arg3');
  
  await put({ type: 'side-effect-success', payload: { value } });
  
  select((store) => console.log(store));
});
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const { createMapOfSets } = require('@actualwave/map-of-sets');
const createNamespace = () => {
const handlers = createMapOfSets();
let store;
const put = (...args) => store.dispatch(...args);
const select = (selector, ...args) => selector(store.getState(), ...args);
const call = (fn, ...args) => fn(...args);
const take = (actionType, actionHandler) => {
handlers.add(actionType, actionHandler);
};
const middleware = (storeArg) => {
store = storeArg;
return (next) => (action) => {
const { type } = action;
handlers.eachValue(type, (handler) => handler(action));
next(action);
};
};
return {
put,
call,
select,
take,
middleware,
};
};
const { put, call, select, take, middleware } = createNamespace();
exports.put = put;
exports.call = call;
exports.select = select;
exports.take = take;
exports.middleware = middleware;
exports.createNamespace = createNamespace;
exports.default = middleware;
{
"name": "@actualwave/redux-side-effect",
"description": "Simple redux middleware inspired by redux-saga to use with async/await",
"version": "0.0.2",
"main": "index.js",
"keywords": [
"redux",
"middleware",
"async",
"await",
"side effect",
"function",
"promise"
],
"author": {
"name": "Oleg Galaburda",
"email": "burdiuz@gmail.com",
"url": "http://actualwave.com/"
},
"bugs": {
"url": "https://gist.github.com/burdiuz/8769f01ecb45c4fcceb76ef0bc8a1c76",
"email": "burdiuz@gmail.com"
},
"homepage": "https://gist.github.com/burdiuz/8769f01ecb45c4fcceb76ef0bc8a1c76",
"dependencies": {
"@actualwave/map-of-sets": "0.0.2"
},
"license": "MIT"
}
import { createMapOfSets } from '@actualwave/map-of-sets';
export const createNamespace = () => {
const handlers = createMapOfSets();
let store;
const put = (...args) => store.dispatch(...args);
const select = (selector, ...args) => selector(store.getState(), ...args);
const call = (fn, ...args) => fn(...args);
const take = (actionType, actionHandler) => {
handlers.add(actionType, actionHandler);
};
const middleware = (storeArg) => {
store = storeArg;
return (next) => (action) => {
const { type } = action;
handlers.eachValue(type, (handler) => handler(action));
next(action);
};
};
return {
put,
call,
select,
take,
middleware,
};
};
const { put, call, select, take, middleware } = createNamespace();
export { put, call, select, take, middleware };
export default middleware;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment