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

v2.0.2

Published

An async extension for Redux that keeps you functional

Downloads

9

Readme

redux-walk

Travis CI build
status

An async extension for Redux that keeps you functional

Install

npm install --save redux-walk

Example

Take a look at the example/src folder for an example. To try it out you can have it locally, or go to xaviervia.github.io/redux-walk

git clone git://github.com/xaviervia/redux-walk
cd redux-walk
npm link
cd example
npm install
npm link redux-walk
npm start

Don't forget to open the Chrome DevTools to see the Redux actions log

How

The key concepts are:

  • The walkCreator, which is a function that receives the action and returns a Walk.
  • A Walk, which is either a reference to an action creator, or an object with two keys:
    • resolve, a reference to an action creator to be executed whenever the promise in the action is successful.
    • reject, a reference to an action creator to be executed whenever the promise in the action is rejected.

Let's say that you want to perform an asynchronous operation when firing RANDOM_NUMBER_IN_RANDOM_TIME. We need to add a promise to that action when we create it:

export const randomNumberInRandomTime = function () {
  const numberOfMilliseconds = Math.floor(Math.random() * 5000)
  return {
    type: 'RANDOM_NUMBER_IN_RANDOM_TIME',
    payload: numberOfMilliseconds,
    meta: {
      promise: (resolve, reject) => setTimeout(() => resolve(numberOfMilliseconds), numberOfMilliseconds)
    }
  }
}

export const randomNumberInRandomTimeReceived = function (payload) {
  return {
    type: 'RANDOM_NUMBER_IN_RANDOM_TIME_RECEIVED',
    payload
  }
}

Then we need to specify a walk for the RANDOM_NUMBER_IN_RANDOM_TIME action. The walk should provide an action creator to be called when the promise is resolved. This is how the walk creator in the example looks like:

import * as RandomActions from '../actions/random'

export default function walkCreator ({ type, payload }) {
  switch (type) {
    case 'RANDOM_NUMBER_IN_RANDOM_TIME':
      return {
        resolve: RandomActions.randomNumberInRandomTimeReceived,
        reject: () => { /* Would be called if the promise were to be rejected */ }
      }
  }
}

That's it! Simple, huh? As a result, both the action creators and the walk creators are just pure functions, which can be tested with simplicity and consistency, just like regular synchronous functions would.

Don't forget to add the middleware to the store:

import createWalkMiddleware from 'redux-walk'

import { createStore, applyMiddleware } from 'redux'
import rootReducer from '../reducers'
import walkCreator from '../walks'

export default function configureStore () {
  const walkMiddleware = createWalkMiddleware(walkCreator)
  const createStoreWithMiddleware = applyMiddleware(walkMiddleware, ...middlewares)(createStore)

  return createStoreWithMiddleware(rootReducer, {})
}

Chaining actions together

In case that you just want to chain the execution of two actions, you can skip setting a promise in the action. You will have to provide a walk that has only one target action creator instead of a resolve/reject pair. The middleware will then call the target action creator with the original payload and dispatch the resulting action.

Let's say that once the random number callback is received, we want to show a congratulations to the user, but only if the timeout happened in less than two seconds. We know that the payload is the number of milliseconds that took the setTimeout to be executed. We can write it like this:

import * as RandomActions from '../actions/random'
import * as CongratulationsActions from '../actions/congratulations'

export default function walkCreator ({ type, payload }) {
  switch (type) {
    case 'RANDOM_NUMBER_IN_RANDOM_TIME':
      /* ... */
    case 'RANDOM_NUMBER_IN_RANDOM_TIME_RECEIVED':
      if (payload < 2000) {
        return CongratulationsActions.congratulationsShow
      }
  }
}

You might notice that redux-walk introduces two different but related features into the architecture. One is the idea of returning a promise as part of the action, which is also introduced in redux-promise. The real meat though comes with the concept of a walk, which is actually a way of chaining the execution of actions in a declarative, structured manner: if actions are the nodes of the application execution graph, walks describe the vertices between them.

To avoid embedding a lot of logic into them, the walk creators have access to very restricted information, namely just the action being dispatched. They are supposed to be very atomic operations, in a way like a reducer: the bulk of the logic should still reside in the action creator, with the walk creator being kept a simple conditional structure that provides the link to the next action in the chain. If you have anything more than an if structure in the walk creator, you are adding to much complexity in that layer.

A single target action creator as a walk

You can specify a single action creator in the walk even when the action contains a promise. The middleware will then dispatch that action creator both if the promise is resolved or if it is rejected. In the example of the RANDOM_NUMBER_IN_RANDOM_TIME, this walk creator would also work:

import * as RandomActions from '../actions/random'

export default function walkCreator ({ type, payload }) {
  switch (type) {
    case 'RANDOM_NUMBER_IN_RANDOM_TIME':
      return RandomActions.randomNumberInRandomTimeReceived
  }
}

Using actual promise objects

Promise objects are supported by redux-walk. This will work as expected:

export const randomHalfTimesItWorks = () => {
  return {
    type: 'RANDOM_HALF_TIMES_IT_WORKS',
    meta: {
      promise: new Promise((resolve, reject) => {
        setTimeout(() => {
          if (Math.random() > 0.5) {
            resolve()
          } else {
            reject()
          }
        }, 1000)
      })
    }
  }
}

Further justification

Besides the walks themselves and the meta.promise idiom in the action, which do not affect anything outside them in the architecture, the Walk approach introduces no artifacts in your code:

  • No weird status properties like in redux-promise.
  • Actions are still actions, unlike in redux-thunk.
  • It's super flexible. Plug whatever you want in the promise.

And the icing on the cake: you don't need to learn anything to use them! Walk creators are just pure functions, like reducers and action creators before them.

Another consideration when designing redux-walk was to keep the actions as actions, and treat the promises as metadata. In redux-promise, the promise is provided in the action as part of the payload, because the action is not considered done until the promise is completed; but in practice this leads to some weirdness when the action should be dispatched immediately with a payload to be used in a reducer–for example for optimistic update of the state–because then the payload as returned by the action creator will not be the one actually dispatched, since the redux-promise middleware will modify it.

The philosophy behind redux-walk is that the type signature of the action creator should consistent with the actual action being passed around. This is important as a design good practice (we strive to be more functional, don't we?) but also to keep the cognitive load down: the whole point of the action creators being exposed in an ordered manner is to have canonical descriptions of the possible events that the application is capable or reacting to.

Test

git clone git://github.com/xaviervia/redux-walk
cd redux-walk
npm install
npm test

License

Copyright 2016 Fernando Via Canel

ISC license.

See LICENSE attached.