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

duckfactory

v1.3.5

Published

Factory for creating redux ducks

Downloads

4

Readme

Duckfactory

Redux ducks, even simpler

Redux / react redux are pretty great, but as the code grows, things easily get cumbersome, complex and fragmented. Redux ducks is an interesting proposal for handling this: organizing the code in bundles of action creators, action types and reducers that belong together, in the very common cases where there's a one-to-one (-to-one) relationship between them.

Duckfactory is a library for wrapping and simplifying this even more. It auto-generates action creators and action types and encapsulates them, only exposing them when needed for side purposes like unit testing. A lot of the boilerplate is abstracted away, the project code gets more minimal, clean and readable, and you, the brilliant developer, gets happier.

How does it work?

In short, give it a prefix string to group the actions, an initial state, an object with the names of action creators and the reducers the actions should trigger. It will then create an object that exposes ordinary redux action creators, reducers and action types.

Example

Let's define a reducer in an example file users.js:

import DuckFactory from 'duckfactory';

// Just an example initial redux state (userId → userObject):
const initialState = {
    42: {
        name: "Arthurarthurdent",
        age: "30-ish",
        occupation: "Sandwich maker"
    },
};

// Defining two ducks in the collection 'myapp/userducks':
const userDucks = new DuckFactory("myapp/userducks", initialState, {
    setName: (state, {userId, newName}) => {
        const newUser = {...state[userId]};
        if (!newUser) {
            return state;
        }
    	return {
    	    ...state,
    	 	[userId]: {
    	    	...newUser,
    	    	name: newName
    	 	}
    	}
    },
    
    addUser: (state, {userId, name, age, occupation}) => {
    	return {
    	    ...state,
    	    [userId]: { name, age, occupation }
    	}
    }
});

About the syntax here: I prefer the ES6 arrow notation and destructuring the action argument. But the more classic syntax like this should also work fine, so you can define it like this instead:

	///...
	
    setName: function (state, action) {
        const newUser = {...state[action.userId]};
        if (!newUser) {
            return state;
        }
    	return {
    	    ...state,
    	 	[action.userId]: {
    	    	...newUser,
    	    	name: action.newName
    	 	}
    	}
    },
    
    addUser: (state, action) => {
    	return {
    	    ...state,
    	    [action.userId]: { 
    	        name: action.name, 
    	        age: action.age, 
    	        occupation: action.occupation 
    	    }
    	}
    }
    
    ///...

Both of these syntaxes are fine: the action creators are autogenerated in runtime, and the action creator arguments are deduced from the arguments in the reducer functions in the definition. That way, once you've defined it as above, you can get the action creators and use them directly:

const actionCreators = userDucks.getActionCreators();

const action1 = actionCreators.setName("Arthur Dent");
const action2 = actionCreators.addUser(43, "Marvin", "Will it never end?", "Paranoid android");

In this example, these two actions will look like this, ready to dispatch and will trigger regular reducers:

console.log(action1);
//    {
//      type: "myapp/userducks/setName",
//      newName: "Arthur Dent"
//    }


console.log(action2);
//   {
//     type: "myapp/userducks/addUser",
//     userId: 42,
//     name: "Marvin",
//     age: "Will it never end?",
//     occupation: "Paranoid android",
//  }

Hooking it up: exporting to combine reducers

If you've used redux before, this should be simple. At the bottom of users.js, export the necessary:

export default userGeese.getReducers();
export const actionCreators = userGeese.getActionCreators();

...and then use the default export in combinereducers as usual, with other duckfactories if you like:

import { combineReducers } from 'redux';

import userReducer from './users'; // <-- This is the default export from above
import gameReducer from './game';

export default combineReducers({
    game: gameReducer,
    users: userReducer
});

I haven't tried, but I can't see any reason it shouldn't work to mix reducers from duckfactories with reducers that are created in other ways, e.g. regular redux if you need to. You probably want to make sure the action names are all unique, though.

Constructor:

new DuckFactory(actionTypePrefix, initialState, actionAndReducerMap, checkAndWarn, logBuilt)
  • actionTypePrefix: a prefix string added before each action creator name, to produce the action type strings. I've used the redux-ducks suggestion - app/reducer - in the examples, but it's up to you. It should be globally unique if you use more than one duckfactory (and/or goosefactory if you use them together). If empty or missing, no prefix will be added and the actioncreator names will be used as action types - then it's up to you to ensure your action types become unique. A slash is added at the end, in line with the redux-ducks suggestion.

  • initialState: the reducer's initial state (for the state subtree that the duck should cover),

  • actionAndReducerMap: an object where the keys become the names of action creators, and the values are anonymous functions that become the corresponding reducer to the action creator. The reducer's arguments are: state as the first argument, and the second argument should be either missing (for reducers that don't require data from the action), or be an action object - that may be destructured into its keys.

  • checkAndWarn: an optional boolean (default: true) that sets whether to check the duck for consistency: are the arguments correct? Does it produce actionTypes that are globally unique? Serious errors throw Errors, less serious ones only log a warning to the console.

  • logBuilt: a last optional boolean (default: false) that sets whether to log some details about the produced action creators and actions. Handy for development, no need for it in prod.

Exposed after creation:

After creation, the resulting duck exports as JS objects:

  • .getActionCreators(): actionCreator-name → actionCreator-function, as described above.
  • .getReducer(): actionType → reducer-function
  • .getTypes(): actionCreator name → actionType. (This differs from the original redux-ducks suggestion, in that the types that are exported here don't guarantee the duck convention: app/reducer/ACTION_TYPE, since the content here depends on your choice of prefix and the keys in actionAndReducerMap.

Installation

npm install duckfactory --save

or

yarn add duckfactory

NOTE: if your app uses minification/uglification, version 1.3.0 should be okay, but don't use the versions below that. My own testing has been done with webpack 2 and yarn. If your mileage varies, please let me know.

Contributions

Suggestions, improvements, corrections, bug notifications, etc... all is welcome on github or [email protected]. Special thanks to NorwegianKiwi for awesome help!

Using it with redux-sagas

The produced actions can of course be used to trigger redux-sagas too. But duckfactory can only use reducer functions, not saga generators. Luckily, here's a sibling library that does the same thing for sagas: Goosefactory. Duckfactories and Goosefactories play nicely with each other.