redux-rsync
v0.1.1
Published
Redux saga alternative to handle async actions
Downloads
4
Maintainers
Readme
RSync: Redux async middleware
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 cancelledevery: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 actionbreak
: 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 doneevery: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: