Giter VIP home page Giter VIP logo

redux-ship's Introduction

Logo Redux Ship

Side effects with snapshots for Redux.

travis status appveyor status npm version npm downloads

Redux Ship is a side effects handler for Redux with a built-in system of snapshots for:

  • live debugging thanks to a graphical visualization of the side effects;
  • simpler unit tests with snapshot testing.

The key insight is to use generators to serialize all the side effects, including the control flow of the code, using the approach of free monads. Additionally, we provide a composition mechanism of sub-stores with effects and full typing (for now only in Flow).

Install

npm install --save redux-ship

Documentation

Example

Get the movies of R2D2 using the StarWars API:

// Check if we already have the list of movies in the Redux store.
const currentMovies = yield* Ship.getState(state => state.movies);
if (!currentMovies) {
  // If not, notify Redux that we start loading the movies.
  yield* Ship.commit({type: 'LoadStart'});
  // Get the description of R2D2 with an API call.
  const r2d2 = yield* Effect.httpRequest('https://swapi.co/api/people/3/');
  // For each movie of R2D2:
  const movies = yield* Ship.all(JSON.parse(r2d2).films.map(function* (movieUrl) {
    // Get the movie title with an API call.
    const movie = yield* Effect.httpRequest(movieUrl);
    return JSON.parse(movie).title;
  }));
  // Write the list of movies in the Redux store.
  yield* Ship.commit({type: 'LoadSuccess', movies});
}

With the Redux Ship devtools we see the following snapshot of the execution: Snapshot

This shows us that we:

  • access the Redux state;
  • dispatch the action LoadStart;
  • make an HTTP call;
  • make seven HTTP calls in parallel (to get the movie titles);
  • dispatch the action LoadSuccess.

Even without reading the code we get a sense of what the application does. By clicking on each pill we see the details of each element, like:

  • an access to a value in the store:

snapshot-get-state

  • a dispatch of an action to the store:

snapshot-load-success

  • an other side effect (API call, cookies update, ...) with the effect and its result:

snapshot-http-request

We also get a serialized snapshot, directly usable to make a unit test:

[
  {
    "type": "GetState",
    "state": null
  },
  {
    "type": "Commit",
    "commit": {
      "type": "LoadStart"
    }
  },
  {
    "type": "Effect",
    "effect": {
      "type": "HttpRequest",
      "url": "https://swapi.co/api/people/3/"
    },
    "result": "{\"name\":\"R2-D2\",\"height\":\"96\",\"mass\":\"32\",\"hair_color\":\"n/a\",\"skin_color\":\"white, blue\",\"eye_color\":\"red\",\"birth_year\":\"33BBY\",\"gender\":\"n/a\",\"homeworld\":\"https://swapi.co/api/planets/8/\",\"films\":[\"https://swapi.co/api/films/2/\",\"https://swapi.co/api/films/5/\",\"https://swapi.co/api/films/4/\",\"https://swapi.co/api/films/6/\",\"https://swapi.co/api/films/3/\",\"https://swapi.co/api/films/1/\",\"https://swapi.co/api/films/7/\"],\"species\":[\"https://swapi.co/api/species/2/\"],\"vehicles\":[],\"starships\":[],\"created\":\"2014-12-10T15:11:50.376000Z\",\"edited\":\"2014-12-20T21:17:50.311000Z\",\"url\":\"https://swapi.co/api/people/3/\"}"
  },
  {
    "type": "All",
    "snapshots": [
      [
        {
          "type": "Effect",
          "effect": {
            "type": "HttpRequest",
            "url": "https://swapi.co/api/films/2/"
          },
          "result": "{\"title\":\"The Empire Strikes Back\",\"episode_id\":5,\"opening_crawl\":\"It is a dark time for the\\r\\nRebellion. Although the Death\\r\\nStar has been destroyed,\\r\\nImperial troops have driven the\\r\\nRebel forces from their hidden\\r\\nbase and pursued them across\\r\\nthe galaxy.\\r\\n\\r\\nEvading the dreaded Imperial\\r\\nStarfleet, a group of freedom\\r\\nfighters led by Luke Skywalker\\r\\nhas established a new secret\\r\\nbase on the remote ice world\\r\\nof Hoth.\\r\\n\\r\\nThe evil lord Darth Vader,\\r\\nobsessed with finding young\\r\\nSkywalker, has dispatched\\r\\nthousands of remote probes into\\r\\nthe far reaches of space....\",\"director\":\"Irvin Kershner\",\"producer\":\"Gary Kurtz, Rick McCallum\",\"release_date\":\"1980-05-17\",\"characters\":[\"https://swapi.co/api/people/1/\",\"https://swapi.co/api/people/2/\",\"https://swapi.co/api/people/3/\",\"https://swapi.co/api/people/4/\",\"https://swapi.co/api/people/5/\",\"https://swapi.co/api/people/10/\",\"https://swapi.co/api/people/13/\",\"https://swapi.co/api/people/14/\",\"https://swapi.co/api/people/18/\",\"https://swapi.co/api/people/20/\",\"https://swapi.co/api/people/21/\",\"https://swapi.co/api/people/22/\",\"https://swapi.co/api/people/23/\",\"https://swapi.co/api/people/24/\",\"https://swapi.co/api/people/25/\",\"https://swapi.co/api/people/26/\"],\"planets\":[\"https://swapi.co/api/planets/4/\",\"https://swapi.co/api/planets/5/\",\"https://swapi.co/api/planets/6/\",\"https://swapi.co/api/planets/27/\"],\"starships\":[\"https://swapi.co/api/starships/15/\",\"https://swapi.co/api/starships/10/\",\"https://swapi.co/api/starships/11/\",\"https://swapi.co/api/starships/12/\",\"https://swapi.co/api/starships/21/\",\"https://swapi.co/api/starships/22/\",\"https://swapi.co/api/starships/23/\",\"https://swapi.co/api/starships/3/\",\"https://swapi.co/api/starships/17/\"],\"vehicles\":[\"https://swapi.co/api/vehicles/8/\",\"https://swapi.co/api/vehicles/14/\",\"https://swapi.co/api/vehicles/16/\",\"https://swapi.co/api/vehicles/18/\",\"https://swapi.co/api/vehicles/19/\",\"https://swapi.co/api/vehicles/20/\"],\"species\":[\"https://swapi.co/api/species/6/\",\"https://swapi.co/api/species/7/\",\"https://swapi.co/api/species/3/\",\"https://swapi.co/api/species/2/\",\"https://swapi.co/api/species/1/\"],\"created\":\"2014-12-12T11:26:24.656000Z\",\"edited\":\"2017-04-19T10:57:29.544256Z\",\"url\":\"https://swapi.co/api/films/2/\"}"
        }
      ],
      [
        {
          "type": "Effect",
          "effect": {
            "type": "HttpRequest",
            "url": "https://swapi.co/api/films/7/"
          },
          "result": "{\"title\":\"The Force Awakens\",\"episode_id\":7,\"opening_crawl\":\"Luke Skywalker has vanished.\\r\\nIn his absence, the sinister\\r\\nFIRST ORDER has risen from\\r\\nthe ashes of the Empire\\r\\nand will not rest until\\r\\nSkywalker, the last Jedi,\\r\\nhas been destroyed.\\r\\n \\r\\nWith the support of the\\r\\nREPUBLIC, General Leia Organa\\r\\nleads a brave RESISTANCE.\\r\\nShe is desperate to find her\\r\\nbrother Luke and gain his\\r\\nhelp in restoring peace and\\r\\njustice to the galaxy.\\r\\n \\r\\nLeia has sent her most daring\\r\\npilot on a secret mission\\r\\nto Jakku, where an old ally\\r\\nhas discovered a clue to\\r\\nLuke's whereabouts....\",\"director\":\"J. J. Abrams\",\"producer\":\"Kathleen Kennedy, J. J. Abrams, Bryan Burk\",\"release_date\":\"2015-12-11\",\"characters\":[\"https://swapi.co/api/people/1/\",\"https://swapi.co/api/people/3/\",\"https://swapi.co/api/people/5/\",\"https://swapi.co/api/people/13/\",\"https://swapi.co/api/people/14/\",\"https://swapi.co/api/people/27/\",\"https://swapi.co/api/people/84/\",\"https://swapi.co/api/people/85/\",\"https://swapi.co/api/people/86/\",\"https://swapi.co/api/people/87/\",\"https://swapi.co/api/people/88/\"],\"planets\":[\"https://swapi.co/api/planets/61/\"],\"starships\":[\"https://swapi.co/api/starships/77/\",\"https://swapi.co/api/starships/10/\"],\"vehicles\":[],\"species\":[\"https://swapi.co/api/species/3/\",\"https://swapi.co/api/species/2/\",\"https://swapi.co/api/species/1/\"],\"created\":\"2015-04-17T06:51:30.504780Z\",\"edited\":\"2015-12-17T14:31:47.617768Z\",\"url\":\"https://swapi.co/api/films/7/\"}"
        }
      ],
      [...] // other API calls
    ]
  },
  {
    "type": "Commit",
    "commit": {
      "type": "LoadSuccess",
      "movies": [
        "The Empire Strikes Back",
        "Attack of the Clones",
        "The Phantom Menace",
        "Revenge of the Sith",
        "Return of the Jedi",
        "A New Hope",
        "The Force Awakens"
      ]
    }
  }
]

License

MIT

redux-ship's People

Contributors

clarus avatar dustinspecker avatar kerumen avatar marciopuga avatar radarhere avatar sleexyz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

redux-ship's Issues

How to use it without babel-polyfill?

As the title stand. Any how I can use it without babel-polyfil? Because the dist / compiled file need globally available regeneratorRuntime.

Maybe something like un-compiled version? So, the dev can compile it by him self by utilizing babel-runtime & babel-transform-runtime?

Thanks, it was a good libs by the way :)

Is there best practice to use setInterval() in redux-ship?

Hi, I'm new to redux-ship and have an experience of redux-saga.
I want to create a periodic timer which dispatches TIME_TICK action every second with redux-ship.
In redux-ship, however, a controller and an effect handler are event-driven.
Is there a good solution to handle setInterval() in redux-ship way?

Here is my example using setTimeout() instead of setInterval().
Project: https://github.com/kuy/lifegame-redux
Source Code: https://github.com/kuy/lifegame-redux/tree/master/src/ship

// From controllers.js

export default function* control(action) {
  switch (action.type) {
  case TIME_PLAY: {
    while (yield* Ship.getState(selectors.time)) {
      yield* Ship.call({ type: Effect.DELAY });
      yield* Ship.commit(timeTick());
    }
    return;
  }
  default:
    return;
  }
}
// From effects.js

function delay(msec) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(msec);
    }, msec);
  });
}

export async function run(effect) {
  switch (effect.type) {
  case DELAY:
    return await delay(PERIOD);
  default:
    return;
  }
}

question: composition and Ship.getState

first of all, i love this lib.

this could be a sign that i need to improve the design of my software, however i've encountered a use case where it would be useful for Ship.getState to have access to parent state ( i have many layers of composition ).

i wonder if would make sense for the api to be something like Ship.getState((modelState, globalState) => ...

though perhaps this represents some sort of anti-pattern.

nevertheless, sometimes escape hatches are useful.

maybe the problem could be solved by treating calls to the redux store as effects:

export async function run(effect: Effect): Promise<any> {
  switch (effect.type) {
    case "HttpRequest":
      const response = await fetch(effect.url, effect.options);
      return await response.json();
    case "GetGlobalState":
      /**
       * presuming store is in scope
       */
       return effect.selector(store.getState())
    default:
      return;
  }
}

export function getGlobalState <Commit, State>(
  selector: func,
): Ship.Ship<Effect, Commit, State, any> {
  return Ship.call({ type: "GetGlobalState", selector  });
}

i'd appreciate any feedback you might have.

thanks!

Update to flow 0.39

I'm running flow 0.39, and I get the following error just with yarn add redux-ship

node_modules/redux-ship/src/ship.js:62
 62: ): Ship<Effect, Commit, State, any[]> {
                                    ^^^^^ array type. Only tuples and array literals with known elements can flow to
 79: ) => Ship<Effect, Commit, State, [A1, A2]> =
                                      ^^^^^^^^ tuple type

node_modules/redux-ship/src/ship.js:62
 62: ): Ship<Effect, Commit, State, any[]> {
                                    ^^^^^ array type. Only tuples and array literals with known elements can flow to
 86: ) => Ship<Effect, Commit, State, [A1, A2, A3]> =
                                      ^^^^^^^^^^^^ tuple type

node_modules/redux-ship/src/ship.js:62
 62: ): Ship<Effect, Commit, State, any[]> {
                                    ^^^^^ array type. Only tuples and array literals with known elements can flow to
 94: ) => Ship<Effect, Commit, State, [A1, A2, A3, A4]> =
                                      ^^^^^^^^^^^^^^^^ tuple type

node_modules/redux-ship/src/ship.js:62
 62: ): Ship<Effect, Commit, State, any[]> {
                                    ^^^^^ array type. Only tuples and array literals with known elements can flow to
103: ) => Ship<Effect, Commit, State, [A1, A2, A3, A4, A5]> =
                                      ^^^^^^^^^^^^^^^^^^^^ tuple type

node_modules/redux-ship/src/ship.js:62
 62: ): Ship<Effect, Commit, State, any[]> {
                                    ^^^^^ array type. Only tuples and array literals with known elements can flow to
113: ) => Ship<Effect, Commit, State, [A1, A2, A3, A4, A5, A6]> =
                                      ^^^^^^^^^^^^^^^^^^^^^^^^ tuple type

node_modules/redux-ship/src/ship.js:62
 62: ): Ship<Effect, Commit, State, any[]> {
                                    ^^^^^ array type. Only tuples and array literals with known elements can flow to
124: ) => Ship<Effect, Commit, State, [A1, A2, A3, A4, A5, A6, A7]> =
                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ tuple type

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.