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

goosefactory

v1.3.3

Published

Factory for creating the redux-saga analogy to a redux duck: a goose

Downloads

7

Readme

Goosefactory

Simple creation and use of 'geese' - a redux-saga analogy to redux ducks.

Background

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.

I made a library for wrapping and simplifying this even more: Duckfactory. It auto-generates action creators and action types and encapsulates them, only exposing them when needed for side purposes like unit testing. This way, you, the brilliant developer, can focus on what's going on in the reducers instead of on the boilerplate wiring everything together.

What's a goose?

redux-sagas is a really handy library for handling asynchronous segments or just bundling chains of redux actions. I've used it for a while, but I have the same impression here as with general redux: most of the time actions, action creators, sagas and the take-effects that control them (takeEvery, takeLatest etc) belong together as a unit.

A goose is that tuple: {action, action creator, saga, take-effect}.

Goosefactory aims to make life as easy as possible when working with these units.

Why "goose'? Well, ducks are named after the last syllable of redux. And while goose isn't quite as similar to the last syllable in sagas, it is a bit similar to a duck.

How does it work?

Short version: give it a prefix string to group the actions, an object with the names of action creators, and the sagas they trigger, where the argument names in the saga mirror the names of the fields in the action. In return, it will create an object that exposes ordinary redux action creators, saga generators and action types:

Example

import { put, call } from 'redux-sagas';
import GooseFactory from 'goosefactory';

// Let's assume we have some resources, for example:
import { searchNamesAPI, getUserAPI, getAdminAPI } from './myAPIs'; 
import { displayNames, setCurrentUser } from './someReduxActions';

// Defining two geese in one collection called 'users':
const userGeese = new GooseFactory("myapp/usergeese", {
    listUsers: function* ({nameSearchString}) {
        const nameList = yield call(searchNamesAPI, nameSearchString);
        yield put(displayNames(nameList));
    },
    
    fetchUser: function* ({userId, isAdmin}) {
        const api = isAdmin ? getAdminAPI : getUserAPI;
        const user = yield call(api, userId);
        yield put(setCurrentUser(user));
    }
});

Note the argument syntax in the saga generator: destructuring an action argument. It's also possible to just use a single action argument instead:

	///...
	
	listUsers: function* (action) {
        const nameList = yield call(searchNamesAPI, action.nameSearchString);
        yield put(displayNames(nameList));
     },
        
    fetchUser: function* (action) {
        const api = action.isAdmin ? getAdminAPI : getUserAPI;
        const user = yield call(api, action.userId);
        yield put(setCurrentUser(user));
    }
    
    ///...

Both these syntaxes are fine (I prefer the first one).

This constructs a goose bundle with the two sagas listUsers and fetchUser. You can now directly create regular redux actions like this:


const actionCreators = userGeese.getActionCreators();

const action1 = actionCreators.listUsers("Arthur");
const action2 = actionCreators.fetchUser(42, true);

In this example, these two actions will look like this:

console.log(action1);
//    {
//      type: "myapp/usergeese/listUsers",
//      nameSearchString: "Arthur"
//    }


console.log(action2);
//   {
//     type: "myapp/usergeese/fetchUser",
//     userId: 42,
//     isAdmin: true,
//  }

Note that the fields nameSearchString, userId and isAdmin are the names defined in the constructor above (derived from the destructured arguments, or in the 'action' syntax: derived from the way subfields of action are used in the generator). For this reason, it won't allow the sagas to have action arguments named type: that name's reserved for the action.

These actions are ready to dispatch, and will trigger the sagas as in regular redux-sagas.

Hooking it up: creating a root saga

In order to use the actions like this, the sagas must be hooked up to the redux-sagas middleware in the ordinary way. To create a root saga for that, use the named export createRootSaga: it takes as argument an array of created goose collections and returns one rootSaga covering all of them.

import { createRootSaga } from 'gooseFactory';

const rootSaga = createRootSaga([userGoose, anotherGoose]);

Constructor:

new GooseFactory(actionTypePrefix, actionAndSagaMap, defaultTakeEffect, checkAndWarn, logBuilt)
  • actionTypePrefix: prefix string that is prepended before the action types. Must be globally unique, inside the same global namespace as all other goosefactories (and/or duckfactories if you use them together). This way, they can share a redux dispatcher. A slash is added at the end, in line with the redux-ducks suggestion.

  • actionAndSagaMap: an object where the keys become the names of action creators, and the values are EITHER:

    • anonymous generators that become the corresponding saga to the action creator, OR

    • an array where the first element is a redux-sagas takeEffect, specific for that saga, and the second element is the saga generator function as described above, OR

    • a JS object where the takeEffect and the saga are the values under those keys: takeEffect and saga.

  • defaultTakeEffect: An optional takeEffect function that will replace the default redux-sagas takeEffect for the sagas in this particular goosefactory (the default is: takeEvery). Any saga-specific takeEffects in the previous argument will override this default (for that saga only, of course).

  • checkAndWarn: An optional boolean (default: true) that sets whether to check the created goose 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 option boolean (default: false) set sets whether to log some details when an action creator is produced, and when it creates actions. Handy for development, no need for it in prod.

After creation:

Although the goose encapsulates some moving parts such as action types, they are available as exported maps (regular JS objects):

  • .getActionCreators(): actionCreator-name → actionCreator-function, as described above
  • .getSagas(): actionType → saga-generator
  • .getTypes(): actionCreator name → actionType
  • .getTakeEffects(): actionType → takeEffect

Installation

npm install --save goosefactory

...or

yarn add goosefactory

NOTE: if your app uses minification/uglification, version 1.3.0 should be okay, but don't use the versions below. 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!