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-effects

v0.4.3

Published

You write pure functions, redux-effects handles the rest.

Downloads

1,499

Readme

redux-effects

Virtual DOM for effects and impurities. You write pure functions, redux-effects handles the rest.

Installation

npm install redux-effects

Benefits

  • Trivial universality. If your effect middleware is universal, your app is universal.
  • Powerful meta-programming facilities (e.g. request caching)
  • More testable code
  • Better insights into what is happening in your application (e.g. logging effects)
  • Better ability to serialize state

Usage

Add redux-effects anywhere in your redux middleware stack*:

import effects from 'redux-effects'
import fetch from 'redux-effects-fetch'
import cookie from 'redux-effects-cookie'
import location from 'redux-effects-location'

applyMiddleware(effects, fetch, cookie, location)

* It used to need to come first, but that is no longer the case

In action creators

All effectful action creators should return a declarative object describing the effect to be done, even if their operation is normally synchronous*. In order to operate on the values returned by these actions, you need to specify your handlers in .meta.steps in the action object. This library comes with a composition utility bind, but you are free to write your own such as declarative-promise.

*E.g. Math.random or accessing cookies. These values are also impure, even though they are synchronous, because they are non-deterministic with respect to your function's parameters.

import {bind} from 'redux-effects'
import {cookie} from 'redux-effects-cookie'
import {createAction} from 'redux-actions'

function checkAuth () {
  return bind(cookie('authToken'), setAuthToken)
}

const setAuthToken = createAction('SET_AUTH_TOKEN')

The values returned by your actions will be passed to the handlers specified on your action object. Then, what your handlers return will be dispatched back into redux to either trigger other effects or be preserved in state. If your functions do not return a value, they do not do anything. Their only ability to effect the world is by ultimately dispatching actions that trigger state reducers, or by triggering effects that cause state to be preserved elsewhere (e.g. a POST request to an API server).

Effect composition

Effects compose by placing steps in .meta.steps on the action object. E.g.

{
  type: 'EFFECT_COMPOSE',
  payload: {
    type: 'FETCH'
    payload: {url: '/some/thing', method: 'GET'}
  },
  meta: {
    steps: [
      [success, failure]
    ]
  }
}

Since this is cumbersome to write out, there are libraries / action creators to help with it:

But it is important to understand that ultimately these libraries just produce plain JS objects, and you are totally free to create your own composition interfaces that behave exactly the way you want if you don't like these. There is nothing magical going on.

Writing effectful middleware

Effects middleware look essentially just like regular redux middleware, except that it must return a promise. If it does not return a promise, it won't compose with other effects, and so won't be very useful to anyone.

As a general rule, your effect middleware should be as dumb as possible. There are two reasons for this:

  • Effect middleware is inherently impure, and therefore difficult to test and reason about
  • Keeping complexity out of the effect middleware ensures it is as generally useful as possible, and as open as possible to abstraction and metaprogramming.

Example - Simple cookie middleware

import cookie from 'component-cookie'

export default function ({dispatch, getState}) {
  return next => action => {
    if (action.type !== 'COOKIE') {
      return next(action)
    }

    switch (action.verb) {
      case 'set':
        return Promise.resolve(cookie(action.name, action.value))
      case 'get':
        return Promise.resolve(cookie(action.name))
    }
  }
}

Example - Universal cookie middleware

import _cookie from 'component-cookie'

export default function (cookieMap) {
  return ({dispatch, getState}) => next => action => {
    if (action.type !== 'COOKIE') {
      return next(action)
    }

    switch (action.verb) {
      case 'set':
        return Promise.resolve(cookie(action.name, action.value))
      case 'get':
        return Promise.resolve(cookie(action.name))
    }
  }

  function cookie (name, value) {
    if (arguments.length === 2) {
      if (cookieMap) cookieMap[name] = value
      else _cookie(name, value)
    }

    return cookieMap ? cookieMap[name] : _cookie(name)
  }
}

With this form, you can simply initialize your cookie middleware with a map of cookies. E.g.

function (req, res, next) {
  req.store = applyMiddleware(effects(cookie(req.cookies)))(createStore)
}

Metaprogramming

Where this approach gets really interesting is when you start applying transformations to your effects. Normally these things are implemented in disparate and often hacky ways. But when you have declarative descriptions of all of your effects, you can unify your transformations into your redux middleware stack, and they can be completely orthogonal to the actual implementations of the effects themselves. Here are some examples:

Request caching

function httpCache () {
  const {get, set, check} = cache()

  return next => action =>
   !isGetRequest(action)
      ? next(action)
      : check(action.payload.url)
        ? Promise.resolve(get(action.payload.url))
        : next(action).then(set(action.payload.url))
}

Response normalization

function normalize () {
  return next => action =>
    isGetRequest(action)
      ? next(action).then(normalizr)
      : next(action)
}

Note that while these examples both transform http requests, they are completely orthogonal to the actual implementation of those requests, and completely orthogonal to the action creator interface you choose to use to generate your descriptors. That means you can:

  • Swap out your http request implementation
  • Change your action creator interface
  • Use a different effect composition strategy
  • ...And most importantly, compose other transformations

And not have to change your transform middleware at all.

Ecosystem

Effect drivers

Plugins that enable various effects:

Alternate composition middleware

Composition helpers