Ninja Power Manifesto

    redux-rsync

    0.1.1 • Public • Published

    RSync: Redux async middleware

    RSync

    Code Climate License

    RSync is an alternative to redux-saga to handle async actions without generator. It makes handling complicated async flow easier and offers a great readability.

    Table of Contents

    Demo

    For a demo, click here.

    Installation

    yarn add redux-rsync

    Getting Started

    1. Apply rsync middlware redux/index.js

    import rootReducer from './reducer'
    import rsync from 'redux-rsync'
    import { createStore, applyMiddleware } from 'redux'
     
    export const store = createStore(
      rootReducer,
      applyMiddleware(rsync)
    )

    2. Decorate actions with async or flow metadata

    redux/action.js

    import api from '../api'
     
    ...
    export function requestGetUser (payload) {
      return {
        type: 'REQUEST_GET_USER',
        payload,
        meta: {
          async: {
            effect: () => api.user.show(),
            resolve: { type: 'RESOLVE_REQUEST_GET_USER' },
            reject: { type: 'REJECT_REQUEST_GET_USER' },
            take: 'latest'
          }
        }
      }
    }
    ...

    redux/flow.js

    import { requestGetUser, requestGetPosts } from './action'
    import { loadInitialDataParams } from './prepare'
     
    ...
    export function loadInitialData (payload) {
      return {
        type: 'LOAD_INITIAL_DATA',
        payload,
        meta: {
          flow: {
            actions: [
              [
                {
                  effect: requestGetUser,
                  break: ({ response }) => !response.data.args.user
                },
                {
                  prepare: loadInitialDataParams.requestGetPosts,
                  effect: requestGetPosts
                }
              ]
            ],
            resolve: { type: 'RESOLVE_LOAD_INITIAL_DATA' },
            reject: { type: 'REJECT_LOAD_INITIAL_DATA' },
            take: 'every:serial'
          }
        }
      }
    }
    ...

    Documentation

    RSync works by decorating actions with async and/or flow metadata

    Async

    Example

    import api from '../api'
     
    ...
    export function requestGetUser (payload) {
      return {
        type: 'REQUEST_GET_USER',
        payload,
        meta: {
          async: {
            effect: () => api.user.show(),
            resolve: { type: 'RESOLVE_REQUEST_GET_USER' },
            reject: { type: 'REJECT_REQUEST_GET_USER' },
            take: 'latest'
          }
        }
      }
    }
    ...

    Properties

    Property Type Value Description
    effect function payload => {} Async side effect to run
    resolve object { type: '<ACTION_NAME>' } Will be dispatched if the effect execution is successful. Payload and effect result/response will be passed to the reducer automatically
    reject object { type: '<ACTION_NAME>' } Will be dispatched if the effect execution is failed. Payload and error will be passed to the reducer automatically
    take string every:parallel(default), latest latest: if an action effect still running when another action with the same type is dispatched, then the previous action will be cancelled

    every:parallel: take all dispatched actions

    Flow

    Example

    // async.js
    import api from '../api'
     
    ...
    export function requestGetUser (payload) {
      return {
        type: 'REQUEST_GET_USER',
        payload,
        meta: {
          async: {
            effect: () => api.user.show(),
            resolve: { type: 'RESOLVE_REQUEST_GET_USER' },
            reject: { type: 'REJECT_REQUEST_GET_USER' },
            take: 'latest'
          }
        }
      }
    }
     
    export function requestGetPosts (payload) {
      return {
        type: 'REQUEST_GET_POSTS',
        payload,
        meta: {
          async: {
            effect: () => api.post.index(),
            resolve: { type: 'RESOLVE_REQUEST_GET_POSTS' },
            reject: { type: 'REJECT_REQUEST_GET_POSTS' },
            take: 'latest'
          }
        }
      }
    }
    ...
     
     
    // flow.js
    import { requestGetUser, requestGetPosts } from './action'
    import { loadInitialDataParams } from './prepare'
     
    ...
    export function loadInitialData (payload) {
      return {
        type: 'LOAD_INITIAL_DATA',
        payload,
        meta: {
          flow: {
            actions: [ // will be executed in order
              {
                effect: requestGetUser,
                break: ({ response }) => !response.data.args.user
              },
              {
                prepare: loadInitialDataParams.requestGetPosts,
                effect: requestGetPosts
              },
              [ // to execute multiple async actions in parallel, wrap them inside another array 
                {
                  prepare: loadInitialDataParams.doFoo,
                  effect: doFoo
                },
                {
                  prepare: loadInitialDataParams.doBar,
                  effect: doBar
                },
              ]       
            ],
            resolve: { type: 'RESOLVE_LOAD_INITIAL_DATA' },
            reject: { type: 'REJECT_LOAD_INITIAL_DATA' },
            take: 'every:serial'
          }
        }
      }
    }
    ...

    Properties

    Property Type Value Description
    actions array[object/array] or [{ effect: () => {}, ... }, ...}] Array of actions to run. The action will support these following properties: effect, prepare, break

    The actions will be executed in order. To run the actions in parallel, Wrapping the actions inside another another array will do it. (see example above)

    effect: function that will return redux action with meta:async property. (see example above)

    prepare: function to prepare result/response from previous async action into params for the current action

    break: function to evaluate the result/response from the action. return true to break the flow or return false to continue
    resolve object { type: '<ACTION_NAME>' } Will be dispatched if the effect execution is successful. Effect result/response will be passed to the reducer automatically.
    reject object { type: '<ACTION_NAME>' } Will be dispatched if the effect execution is failed. Error will be passed to the reducer automatically.
    take string first(default), every:serial, every:parallel first: will not accept any flow actions with the same type with the one that currently running unti it's done

    every:serial: take all dispatched flow actions with the same type, put them in a queue and execute them in serial

    Contributing

    We appreciate feedback and contribution to this repo! Before you get started, please see the following:

    Support + Feedback

    • Use Issues for code-level support
    • Use Mail for usage, questions, specific cases

    License

    MIT

    Install

    npm i redux-rsync

    DownloadsWeekly Downloads

    3

    Version

    0.1.1

    License

    MIT

    Unpacked Size

    17.7 kB

    Total Files

    6

    Last publish

    Collaborators

    • adhyapranata