npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

redux-conditional

v0.7.5

Published

Make sharing reducers easy by conditionally applying actions to shared Redux reducers.

Downloads

4

Readme

Redux Conditional

Conditionally apply actions to Redux reducers to make sharing reducers easy.

npm install --save redux-conditional

Motivation & Usage

Redux would apply one action to all reducers under one store. It means different parts of one store using same reducers will always be kept same by default. It makes sharing reducers difficult (One solution is to use high order function to generate reducers. It will get some repeat code and become awkard when you want to share more reducers. And it won't support hierarchal data well).

Sometimes, we want different parts of one store to share same reducers and to be able to maintain their own data. redux-conditional is created to help conditionally apply same reducers to different parts of one store. It can even support to combine multiple conditions in hierarchal store.

Get started

We want a list of counters and each counter can increment and decrement independently.

Let's define normal reducer and action creators.

// just one normal reducer
function counter(state = 0, action) {
  switch (action.type) {
  case 'INCREMENT':
    return state + 1;
  case 'DECREMENT':
    return state - 1;
  default:
    return state;
  }
}

// just normal action creators
function increment() {
  return { type: 'INCREMENT' };
}

function decrement() {
  return { type: 'DECREMENT' };
}

Now we can make use of redux-conditional to create a list of counters sharing the same reducer. Check counter.js at examples

import { conditionalReducerByKey, simpleConditional } from 'redux-conditional';

// we can use other keys than 0, 1, 2
const targets = [0, 1, 2];
const targetReducer = conditionalReducerByKey(targets)(counter);
const actionCreator = simpleConditional().actionCreator;

// new action creators to take "key" into account.
const conditionalIncrement = actionCreator(increment);
const conditionalDecrement = actionCreator(decrement);

// let's operate on different counters
let state = {};

// let increment the counter with key 1
state = targetReducer(state, conditionalIncrement(1)());
// state will become { '0': 0, '1': 1, '2': 0 }

// let decrement the counter with key 2
state = targetReducer(state, conditionalDecrement(2)());
// state will become { '0': 0, '1': 1, '2': -1 }

How it works

  1. One property conditionalKey will be added to each action; The value will be data specific to one counter.
  2. A new reducer will be created based on the original reducer. It will check conditionalKey in one action to decide deliver/discard it.

Combine different conditions in store hierarchy

Just like combineReducers in Redux, redux-conditional supports combination of different conditions in store hierarchy. Check hierarchy.j at examples

const level1 = 'myFirst';
const level2 = 'mySecond';
const conditional = simpleConditional({ switches: [level1, level2] });

const conditionalIncrement = conditional.actionCreator(increment);
const conditionalDecrement = conditional.actionCreator(decrement);

const BTargetReducer = conditionalReducerByKey(['B1', 'B2'], conditional.mySecond)(counter);
const ATargetReducer = conditionalReducerByKey(['A1', 'A2'], conditional.myFirst)(BTargetReducer);

const store = createStore(ATargetReducer);
store.dispatch({ type: 'UNKNOWN' });

store.dispatch((conditionalIncrement({ myFirst: 'A1', mySecond: 'B2' })()));
// store.getState() will be { A1: { B1: 0, B2: 1 }, A2: { B1: 0, B2: 0 } }

store.dispatch((conditionalDecrement({ myFirst: 'A2', mySecond: 'B1' })()));
// store.getState() will be { A1: { B1: 0, B2: 1 }, A2: { B1: -1, B2: 0 } }

Customization

There are several ways you can customize it.

Attach conditionalKey to one path of one action instead of to it directly.

Check simpleConditionalKeyWithPaths.js at examples

// we would like the conditional key to be at action.path1.path2
const paths = ['path1', 'path2'];
const conditional = simpleConditional({ paths });

const targetReducer = conditionalReducerByKey(targets, conditional.default)(counter);
Define different condition to decide deliver/discard one action.

Check customizedConditionMaker.js at examples

import { conditionalReducerByKey, simpleConditional } from 'redux-conditional';

const targets = [0, 1, 2];
const conditional = simpleConditional();

// new action creators to take "key" into account.
const conditionalIncrements = conditional.actionCreator(increment);

// need to return true or false to decide delivering/discarding actions.
// data will be 0, 1 or 2 in this case. it can be any data if we make targets one plain object.
// For example, we don't want counter 1 to be greater than 2.
const conditionMaker = data => (state, action) => {

  // if you use conditional.actionCreator (a witer), you need this reader.
  const key = conditional.default.conditionalKeyReader(action);

  return key === data && (key !== 1 || state === undefined || state < 2);
};

const targetReducer = conditionalReducerByKey(targets, { conditionMaker })(counter);

// let's operate on different counters
let state = {};

// let increment the counter with key 1
state = targetReducer(state, conditionalIncrements(1)());
// state will become { '0': 0, '1': 1, '2': 0 }
state = targetReducer(state, conditionalIncrements(1)());
// state will become { '0': 0, '1': 2, '2': 0 }
state = targetReducer(state, conditionalIncrements(1)());
// state will become { '0': 0, '1': 2, '2': 0 }

// let increment the counter with key 2
state = targetReducer(state, conditionalIncrements(2)());
// state will become { '0': 0, '1': 2, '2': 1 }
state = targetReducer(state, conditionalIncrements(2)());
// state will become { '0': 0, '1': 2, '2': 2 }
state = targetReducer(state, conditionalIncrements(2)());
// state will become { '0': 0, '1': 2, '2': 3 }

Put counters in different levels of our store.

Check differentLevel.js at examples

import { conditionalReducer, simpleConditional } from 'redux-conditional';
import { combineReducers, createStore } from 'redux';

const conditional = simpleConditional();
// combine reducers as we do in redux
const counterBReducer = combineReducers({
  counterB: conditionalReducer(conditional.default.conditionMaker('B'), counter),
});

const rootReducers = combineReducers({
  counterA: conditionalReducer(conditional.default.conditionMaker('A'), counter),
  another: counterBReducer,
});

// new action creators to take "key" into account.
const incrementByKey = conditional.actionCreator(increment);
const decrementByKey = conditional.actionCreator(decrement);

const store = createStore(rootReducers);
// init our store
store.dispatch({type: 'UNKNOWN'});

store.dispatch(incrementByKey('A')());
store.dispatch(incrementByKey('A')());
store.dispatch(decrementByKey('B')());
// state will become { counterA: 2, another: { counterB: -1 } }
Use your own "conditions" and "key"

Check notSimpleCondition.js at examples

const targets = { A: 'anythingForA', B: 'anythingForB' };

// data would be anythingForA or anythingForB
const conditionMaker = (data) => (state, action) => action.myAnything === data;

const targetReducer = conditionalReducerByKey(targets, { conditionMaker })(counter);

// new action creators to take "key" into account.
const incrementByKey = (key) => {
  const currentAction = increment();
  currentAction.myAnything = targets[key];

  return currentAction;
};

const decrementByKey = (key) => {
  const currentAction = decrement();
  currentAction.myAnything = targets[key];

  return currentAction;
};

// let's operate on different counters
let state = {};

// let increment the counter with key 1
state = targetReducer(state, incrementByKey('A'));
// state will become { A: 1, B: 0 }

// let decrement the counter with key 2
state = targetReducer(state, decrementByKey('B'));
// state will become { A: 1, B: -1 }

Please note that simpleConditional would apply its writer to target action and reader to read data back. If any redux middleware you use discards it, it's not going to work. In this case, you have to use writer to action manually before delivering it to reducer.

License

MIT