API Reference

Ship<Effect, Commit, State, A>

The type of a ship. A ship is a generator and can be defined using the function* syntax.

  • Effect the type of the side effects the ship can call. We often use a single Effect type for a whole program
  • Commit the type of the commits the ship can commit
  • State the type of the state as visible from the ship
  • A the type of the value returned by the ship (often void)

Example

A controller to load a random gif: export function* control(action: Action): Ship.Ship {

  switch (action.type) {
  case 'Load': {
    yield* Ship.commit({
      type: 'LoadStart',
    });
    const result = yield* Effect.httpRequest(
      `http://api.giphy.com/v1/gifs/random?api_key=dc6zaTOxFJmzC&tag=${action.tag}`
    );
    const gifUrl: string = JSON.parse(result).data.image_url;
    yield* Ship.commit({
      type: 'LoadSuccess',
      gifUrl,
    });
    return;
  }
  default:
    return;
  }
}

Snapshot<Effect, Commit>

The type of the snapshot of an execution of a ship. A snapshot includes the side effects ran by a ship, as well as their execution order (sequential or concurrent). You can take a snapshot with the redux-ship-logger dev tools or with the snap function.

  • Effect the type of effects in the snapshot
  • Commit the type of commits in the snapshot

Example

The snapshot of a controller loading a random gif:

[
  {
    "type": "Commit",
    "commit": {
      "type": "LoadStart"
    }
  },
  {
    "type": "Effect",
    "effect": {
      "type": "HttpRequest",
      "url": "http://api.giphy.com/v1/gifs/random?api_key=dc6zaTOxFJmzC&tag=minion"
    },
    "result": [...]
  },
  {
    "type": "Commit",
    "commit": {
      "type": "LoadSuccess",
      "gifUrl": "http://media3.giphy.com/media/HyanD1KpfzPiw/giphy.gif"
    }
  }
]

all

all<Effect, Commit, State, A>(
  ships: Ship<Effect, Commit, State, A>[]
): Ship<Effect, Commit, State, A[]>

Returns the array of results of the ships by running them in parallel. This is the equivalent of Promise.all in Ship.

  • ships an array of ships to execute concurrently

If you have a fixed number of tasks with different types of result to run in parallel, use:

all2(ship1, ship2)
all3(ship1, ship2, ship3)
...
all7(ship1, ship2, ship3, ship4, ship5, ship6, ship7)

The result type is a tuple with the same types as the arguments:

all3<Effect, Commit, State, A1, A2, A3>(
  ship1: Ship<Effect, Commit, State, A1>,
  ship2: Ship<Effect, Commit, State, A2>,
  ship3: Ship<Effect, Commit, State, A3>
): Ship<Effect, Commit, State, [A1, A2, A3]>

Example

To concurrently get three random gifs:

const gifUrls = yield* Ship.all(['cat', 'minion', 'dog'].map(function* (tag) {
  const result = yield* Effect.httpRequest(
    `http://api.giphy.com/v1/gifs/random?api_key=dc6zaTOxFJmzC&tag=${tag}`
  );
  return JSON.parse(result).data.image_url;
}));

call

call<Effect, Commit, State>(
  effect: Effect
): Ship<Effect, Commit, State, any>

Calls the serialized effect effect. The type of the result is any because it depends on the value of the effect, which is only known at runtime.

  • effect the effect to call

Example

To prevent type errors, we recommend to wrap your calls to call with one wrapper per kind of effect. For example, if you have an effect HttpRequest which always returns a string:

export function httpRequest<Commit, State>(url: string): Ship<Effect, Commit, State, string> {
  return Ship.call({
    type: 'HttpRequest',
    url,
  });
}

commit

commit<Effect, Commit, State>(
  commit: Commit
): Ship<Effect, Commit, State, void>

Commits a commit of type Commit and waits for its termination.

  • commit the commit to apply

Example

To commit the result of a successful HTTP request:

yield* Ship.commit({
  type: 'LoadSuccess',
  gifUrl,
});

getState

getState<Effect, Commit, State, A>(
  selector: (state: State) => A
): Ship<Effect, Commit, State, A>

Returns a part of the current state by applying a selector.

  • selector a selector to extract the useful part of the current state

Example

To get the current gif in the store:

const currentGif = yield* Ship.getState(state => state.randomGif.gifUrl);

map

map<Effect, SubCommit, SubState, Commit, State, A>(
  liftCommit: (subCommit: SubCommit) => Commit,
  extractState: (state: State) => SubState,
  ship: Ship<Effect, SubCommit, SubState, A>
): Ship<Effect, Commit, State, A>

A function useful to compose nested components. Lifts a ship with access to "small set" of commits SubCommit and a "small set" of states SubState to a ship with access to the "larger sets" Commit and State.

  • liftCommit lifts a sub-commit to a commit
  • extractState extract a sub-state from a state
  • ship the ship to map

Example

To embed a controller retrieving one random gif into a controller retrieving two random gifs:

return yield* Ship.map(
  commit => ({type: 'First', commit}),
  state => state.first,
  RandomGifController.control(action.action)
);

middleware

middleware<Action, Effect, Commit, State>(
  runEffect: (effect: Effect) => any | Promise<any>,
  control: (action: Action) => Ship<Effect, Commit, State, void>
): ReduxMiddleware

Returns a Redux middleware to connect Redux Ship to Redux.

  • runEffect the function to evaluate a serialized side effect (may return a promise for asynchronous effects)
  • control the function to evaluate an action with a ship

The Ship middleware eats all actions: it does not let actions propagate to the next middleware. If you want an action to propagate use Ship.commit explicitly.

When a Ship.commit occurs, the commit is dispatched to the next middleware rather than to the whole middleware chain. This is so to prevent loops of commits. Thus, this is not possible to call another ship with Ship.commit. Instead, use the yield* keyword to directly call the corresponding ship.

Example

import {applyMiddleware, createStore} from 'redux';

const middlewares = [
  Ship.middleware(runEffect, control),
  otherMiddleware
];

const store = createStore(reduce, initialState, applyMiddleware(...middlewares));

run

run<Effect, Commit, State, A>(
  runEffect: (effect: Effect) => any | Promise<any>,
  store: {
    dispatch: (commit: Commit) => void | Promise<void>,
    getState: () => State
  },
  ship: Ship<Effect, Commit, State, A>
): Promise<A>

Runs a ship and its side effects by evaluating each primitive effect with runEffect and interpreting each call to Redux with the store.

  • runEffect the function to evaluate a serialized side effect (may return a promise for asynchronous effects)
  • store the current Redux store
  • ship the ship to execute

Example

To run Redux Ship with a Redux store store, you can do:

Ship.run(runEffect, store, ship);

where runEffect is your function to evaluate your side effects:

export type Effect = {
  type: 'HttpRequest',
  url: string,
};

export async function runEffect(effect: Effect): Promise<any> {
  switch (effect.type) {
  case 'HttpRequest': {
    const response = await fetch(effect.url);
    return await response.text();
  }
  default:
    return;
  }
}

simulate

simulate<Effect, Commit, State, A>(
  ship: Ship<Effect, Commit, State, A>,
  snapshot: Snapshot<Effect, Commit>
): Snapshot<Effect, Commit>

Simulates a ship in the context of a snapshot and returns the snapshot of the simulation. A simulation is a purely functional (with no side effects) execution of a ship. Useful for regression testing.

  • ship the ship to simulate
  • snapshot a snapshot of a previous execution of the ship

To simulate a ship, we need a snapshot of a previous live execution because there are many ways to execute a ship. For example, if the ship starts an API request, we do not know which API answer to provide. In this case, we use a snapshot which already contains an API answer. Since the simulation always gives the same API answers as in the snapshot, the ship should behave the same as when its snapshot was taken. In particular, the result of simulate should be equal to snapshot, unless the implementation of ship had a regression since its snapshot was taken.

Example

In a unit test of a controller:

expect(Ship.simulate(control(action), snapshot)).toEqual(snapshot);

snap

snap<Effect, Commit, State, A>(
  ship: Ship<Effect, Commit, State, A>
): Ship<Effect, Commit, State, {
  result: A,
  snapshot: Snapshot<Effect, Commit>
}>

Returns a ship behaving as ship but taking the snapshot of its own execution.

  • ship the ship to take in picture

Example

To take the snapshot of a ship:

const {result, snapshot} = yield* Ship.snap(ship);

To take the snapshot of a controller (the result should always be undefined):

const {snapshot} = yield* Ship.snap(control(action));

results matching ""

    No results matching ""