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

reducify

v1.0.3

Published

Make reducers with less effort.

Downloads

12

Readme

Reducify Build Status

Make Redux reducers with less effort.

NPM

http://reducify.mediadrake.com/

Installation

% npm install reducify

Usage

import { createStore } from 'redux';
import reducify from 'reducify';


const store = createStore(
  reducify({
    "ADD": (state = 0, action) => state + action.data,
    "SUBTRACT": (state = 0, action) => state - action.data
  })
);

store.dispatch({
   type: 'ADD',
   data: 10
});

// State is: 10

store.dispatch({
   type: 'SUBTRACT',
   data: 5
});

// State is: 5

Turn configurations into reducer functions

Tired of massive, unwieldly switch statements? Wish you could break up reducers into re-usable and configurable parts?

Yes, this problem is literally ruining my life.

We thought so. With Reducify you can create reducers with a configurations and sleep a bit easier.

Reducers Made Three Ways

Functions

The vanilla approach. Passing in a function just spits out the same function.

If you've got your reducers all squared away we don't want to rock the boat.

function myReducer(state, action) {
    switch(action.type) {
        // reducer stuff
    }
}

createStore(reducify(myReducer));

Configs

If you pass in a config, we'll turn it into a reducer function.

Check out the config api reference to see what you can add.

const myConfig = {
    defaultsTo: 10
    reducer(state, action) { // state will be 10        
        // reducer stuff
    }
};

createStore(reducify(myConfig));

Arrays

Passing in an array is just a short version of the config above.

const myArrayConfig = [
    10,
    (state, action) => { // state will be 10        
        // reducer stuff
    }
];

createStore(reducify(myArrayConfig));

Arrays are deconstructed with the following signature:

[defaultsTo, [select, merge], reducerAndActions]

Some examples:

// No Select

[10, myReducerFunction] 

// Same as:
{defaultsTo: 10, reducer: myReducerFunction}


// Key Select

[{myCount: 10}, 'myCount', myReducerFunction]  

// Same as:
{defaultsTo: {myCount: 10}, select: 'myCount', reducer: myReducerFunction}


// Long form select

[
    {myCount: 10}, 
    state => state.myCount, 
    (result, state) => {...state, myCount: result}, 
    myReducerFunction
]

// Same as:
{
    defaultsTo: {myCount: 10}, 
    select: state => state.myCount, 
    merge: (result, state) => {...state, myCount: result}, 
    reducer: myReducerFunction
}

Configuration Sugar

Because we're opening the door on configuration, we get the ability to add in some user-directed magic that solves common redux boilerplate.

Action Methods

This might bring out some pitchforks, but you don't need to do a switch statement for everything. If you pass in an action type that is a method, we'll run them before we run any declared reducers.

import { createStore } from 'redux';
import reducify from 'reducify';

const store = createStore(
  reducify({
    "INCREMENT": (state = 0, action) => state + 1,    
    "DECRAMENT": (state = 0, action) => state - 1
  })
);

store.dispatch({
   type: 'INCREMENT'
});

// State is 1

store.dispatch({
   type: 'DECREMENT'
});

// State is 0

The following is the same as above

import { createStore } from 'redux';
import reducify from 'reducify';

const store = createStore(
  function(state = 0, action) {
    switch(action.type) {
        case "INCREMENT":
            return state + 1;
        case "DECREMENT":
            return state - 1;
        default:
            return state;
    }
  }
);

// same as above

Nice! We went from 10 lines to 4. Not bad.

Keep in mind, this is still Redux. So don't take any shortcuts like trying to not make copies of your objects.

// GOOD
const store = createStore(
  reducify({
    "ADD_PITCHFORK": (state = {pitchforks: 0}, action) => ({...state, pitchforks: state.pitchforks + 1}),    
    "USE_PITCHFORK": (state = {pitchforks: 0}, action) => ({...state, pitchforks: state.pitchforks - 1})
  })
);

// BAD
const store = createStore(
  reducify({
    "ADD_PITCHFORK": (state = {pitchforks: 0}, action) => {
        state.pitchforks ++;
        return state;
    },    
    "USE_PITCHFORK": (state = {pitchforks: 0}, action) => {
       state.pitchforks --;
       return state;
   }
  })
);

You can even combine these methods with a reducer function! The actions will always run first.

import { createStore } from 'redux';
import reducify from 'reducify';

const store = createStore(
  reducify({
    "ADD_PITCHFORK": (state = {pitchforks: 0}, action) => ({...state, pitchforks: state.pitchforks + 1}),    
    "USE_PITCHFORK": (state = {pitchforks: 0}, action) => ({...state, pitchforks: state.pitchforks - 1}),
    reducer(state, action) {
        switch(action.type) {
            case "CLEAR_PITCHFORKS":
                return {...state, pitchforks: 0};
            default:
                return state;
        }
    }
  })
);

get it, you won't chase us down with pitchforks because we're letting you use switch statements too? Nevermind, sigh - moving on

Selectors

Ever dealt with mutating a large redux object? It's not a lot of fun to try and peak at your model over a massive switch statement and just hope you're getting it right.

String selectors

It's even less fun to have to deal with updating your model because Brad in product design thinks that we should go from having 1 user profile picture to 10.

You're a jerk Brad. I ate all of the spaghetti you brought in for lunch - it was only ok.

import { createStore } from 'redux';
import reducify from 'reducify';

const store = createStore(
  reducify({
    defaultsTo: {username: 'Brad', hasSpaghetti: true},
    select: 'hasSpaghetti',
    "EAT_SPAGHETTI": (state, action) => false
  })
);

store.dispatch({
   type: 'EAT_SPAGHETTI'
});

// State is: {username: 'Brad', hasSpaghetti: false}

In the example above, we passed a string to the select method. The string is mapped to an object key that we automatically merge and select from.

Selector Methods

You can pass in select and merge methods. This would be identical to the reducer above:

import { createStore } from 'redux';
import reducify from 'reducify';

const store = createStore(
  reducify({
    defaultsTo: {username: 'Brad', hasSpaghetti: true},
    select: (state) => state.hasSpaghetti,
    merge: (result, state) => ({...state, hasSpaghetti: result})
    "EAT_SPAGHETTI": (state, action) => false
  })
);

store.dispatch({
   type: 'EAT_SPAGHETTI'
});

// State is: {username: 'Brad', hasSpaghetti: false}

And you can use some aliases - $ for select and _ for merge.

const store = createStore(
  reducify({
    defaultsTo: {username: 'Brad', hasSpaghetti: true},
    $: 'hasSpaghetti'
    "EAT_SPAGHETTI": (state, action) => false
  })
);

// eating Brad's spaghetti

const store = createStore(
  reducify({
    defaultsTo: {username: 'Brad', hasSpaghetti: true},
    $: (state) => state.hasSpaghetti,
    _: (result, state) => ({...state, hasSpaghetti: result})
    "EAT_SPAGHETTI": (state, action) => false
  })
);

// still eating Brad's spaghetti

Deep selectors

If you're trying to access an object that's nested into your state, you can pass in an array and we'll traverse that path for you

import { createStore } from 'redux';
import reducify from 'reducify';

const store = createStore(
  reducify({
    defaultsTo: {username: 'Brad', lunch: {hasSpaghetti: true}},
    select: ['lunch', 'hasSpaghetti'],
    "EAT_SPAGHETTI": (state, action) => false
  })
);

store.dispatch({
   type: 'EAT_SPAGHETTI'
});

// State is: {username: 'Brad', lunch: {hasSpaghetti: false}}

Action Partials

When you're declaring your reducer, you've got a chance to set some default values for all actions that go through it.

import { createStore } from 'redux';
import reducify from 'reducify';

function incrementReducer(state = 0, {data = 1, ...action}) {
    switch (action.type) {
        case 'INCREMENT':
            return state + data;
        case 'DECREMENT':
            return state - data;
        default:
            return state;
    }
}

const store = createStore(
  reducify({    
    reducer: incrementReducer,
    actionPart: {data: 2}
  })
);

store.dispatch({
   type: 'INCREMENT'
});

// State is: 2

store.dispatch({
   type: 'DECREMENT'
});

// State is: 0

Defaults

Just use the config option defaultsTo.

import { createStore } from 'redux';
import reducify from 'reducify';

const store = createStore(
  reducify({    
    defaultsTo: {myNumber: 10}, 
    select: 'myNumber', 
    "ADD": (state = 0, action) => state + action.data,
    "SUBTRACT": (state = 0, action) => state - action.data    
  })
);

store.dispatch({
   type: 'ADD',
   data: 20
});

// State is: {myNumber: 30}

store.dispatch({
   type: 'SUBTRACT',
   data: 5
});

// State is: {myNumber: 25}

You will get a state with all of your reducers, so if you're relying on method signature defaults, that will get overridden.

const store = createStore(
  reducify({    
    defaultsTo: {myNumber: 10}, 
    select: 'myNumber', 
    "ADD": (state = 0, action) => state + action.data,
    "SUBTRACT": (state = 0, action) => state - action.data    
  })
);

Statics

A cousin of defaultsTo. Static reducers just return the state or default value regardless of action type.

import { createStore } from 'redux';
import reducify from 'reducify';

const store = createStore(
  reducify({    
    foo: 'bar'    
  })
);

store.dispatch({
   type: 'ADD',
   data: 20
});

// State is: {foo: 'bar'}

store.dispatch({
   type: 'SUBTRACT',
   data: 5
});

// State is: {foo: 'bar'}

Pass in a plain object or value and that's what you'll get back every time. Good for mocking and some plugins.

Credits

Reducify is free software under the MIT license. It was created in sunny Santa Monica by Matthew Drake.