1.0.1 • Published 6 years ago

redux-berry v1.0.1

Weekly downloads
-
License
MIT
Repository
github
Last release
6 years ago

Installation

yarn add redux-berry

Usage

In store configuration file

import { createStore, applyMiddleware } from 'redux';
import reducer from './reducer';
import berry from 'redux-berry';

export default createStore(reducer, applyMiddleware(berry));

Apply redux-berry as the first middleware, if you are going to use other middlewares together. Otherwise, it may not work as intended.

In your actions.js files

import axios from 'axios';

export const GET_USER = 'GET_USER';
export const getUser = ({ id }) => ({
  type: GET_USER,
  payload: axios.get(`/user/${id}`)
});

In your reducer.js files

import { GET_USER } from './actions';

const initialState = {
  user: {}
};

export default (state = initialState, action) => {
  switch (action.type) {
    case GET_USER:
      return Object.assign({}, state, {
        user: action.payload.data.user
      });
    default:
      return state;
  }
};

Features

You can organize your actions in a consistent way.

If an action should be dispatched as a result of an asynchronous task, simply declare the task in the action's payload property as a Promise.

The action will be passed to reducers after the Promise is resolved, and the payload of action will be replaced with the resolved value of the promise so you can handle it in your reducers.

You can use some helpful functions for asyncronous task

By defining the meta property of actions, you can apply some features in your asyncronous tasks. Please go see BerryMeta.

throttle

typedefault
number0

When throttle property is set, the action will be ignored if there are same type actions dispatched in the last throttlems.

It will be ignored, if the action is dispatched in the before, after, error array.

debounce

typedefault
number0

When debounce property is set, the action will be handled only if there are no same type actions dispatched for the next debouncems.

It will be ignored, if the action is dispatched in the before, after, error array.

lock

typedefault
booleanfalse

When lock property is set to true, all dispatch of same type action is blocked until the action is resolved.

before

typedefault
Array<AnyAction | Function>[] (empty array)

All actions and callbacks will be handled serially according to their type before execute Promise.

  • Action - They are simply dispatched
  • Function - They are executed, and if there is a return, it will be disaptched, so they should not return other than action. It is useful when it is needed to determine whether to dispatch sub actions or not according to arguments of the action creator.

Dispatch of asyncronous actions in before, after, error is not parallel. This means that a dispatch of an action in these array put off handling next item (no matter it is action or function) until the action is settled.

const getUserInfoAsync = ({ id }) => {
  type: ASYNC_ACTION,
  payload: axios.get(`/user/${ id }`).then(res => res.data),
  meta: {
    before: [
      { type: 'setLoadingSpinner', payload: true }
    ]
  }
};

after

typedefault
Array<AnyAction | Function>[] (empty array)

All actions and callbacks will be handled serially according to their type after Promise is settled. Once the Promise get settled, They are handled regardless of whether the Promise has been resolved or rejected.

  • Action - same with before
  • Function - same with before except it receives resolved value as a first argument and rejected value as a second argument.
const getUserInfo = ({ id }) => {
  type: GET_USER_INFO,
  payload: axios.get(`/user/${ id }`).then(res => res.data),
  meta: {
    before: [setLoadingSpinner(true)],
    after: [
      data => getTrendyArticles(data.favoriteCategory),
      setLoadingSpinner(false) // It will be dispatched after the above promise(getTrendyArticles) is settled
    ]
  }
};

const getTrendyArticles = payload => {
  type: GET_TRENDY_ARTICLES,
  payload: axios.get(`/articles/${payload}/trendy`).then(res => res.data)
};

const setLoadingSpinner = payload => ({
  type: SET_LOADING_SPINNER,
  payload
});

error

typedefault
Array<AnyAction | Function>[] (empty array)

All actions and callbacks will be handled serially according to their type only when Promise is rejected.

  • Action - same with before
  • Function - same with before except it receives rejected value as first argument.
import { commonErrorHandler, userErrorHandler } from './errHandler';

const getUserInfo = ({ id }) => {
  type: GET_USER_INFO,
  payload: axios.get(`/user/${ id }`).then(res => res.data),
  meta: {
    error: [commonErrorHandler, userErrorHandler]
  }
};

with Typescript

Redux-berry supports typescript. If you want to use redux-berry and typescript together, just import BerryActionCreator and specify it as the type of your asyncronous action creator.

import { BerryActionCreator } from 'redux-berry';

const GET_USER_INFO = 'GET_USER_INFO';
const getUserInfo: BerryActionCreator<
  { id: string; }, // payload type
  { data: { user: any; }; }, // resolved data type
  { error: any; } // rejected data type
> = ({ id }) => { // type inference for "id" works
  type: GET_USER_INFO,
  payload: axios.get(`/user/${ id }`),
  meta: {
    after: [
      (res, err) => { ... } // type inference for "res" and "err" works
    ],
    error: [
      (err) => { ... } // type inference for "err" works
    ]
  }
};

Types exported from redux-berry

type BerryActionCreator

/**
 *  An action creator type which returns BerryAction
 *
 *  @template TPayload the type of argument of action creator
 *  @template TResolve the type of resolved value from the promise of the action
 *  @template TReject the type of rejected value from the promise of the action
 *  @template TType the type of promise of action returned from the action creator
 */
export type BerryActionCreator<TPayload = any, TResolve = any, TReject = any, TType = string> = {
  (payload?: TPayload): BerryAction<TResolve, TReject, TType>;
};

type BerryAction

/**
 *  An action type which is handled by redux-berry
 *
 *  @template TResolve the type of resolved value from the promise
 *  @template TReject the type of rejected value from the promise
 *  @template TType the type of action's "type" property
 */
type BerryAction<TResolve = any, TReject = any, TType = string> = {
  type: TType;
  payload: Promise<TResolve>;
  meta?: BerryMeta<TResolve, TReject>;
};

type BerryMeta

/**
 * A meta type of BerryAction
 *
 *  @template TResolve the type of resolved value from the promise of the action
 *  @template TReject the type of rejected value from the promise of the action
 */
type BerryMeta<TResolve = any, TReject = any> = {
  throttle?: number;
  debounce?: number;
  lock?: boolean;
  before?: (AnyAction | ((...args: any[]) => void | AnyAction))[];
  after?: (AnyAction | ((res?: TResolve, err?: TReject, ...args: any[]) => void | AnyAction))[];
  error?: (AnyAction | ((err?: TReject, ...args: any[]) => void | AnyAction))[];
};
1.0.1

6 years ago

1.0.0

6 years ago

0.1.2

6 years ago

0.1.1

6 years ago

0.1.0

6 years ago

0.0.3

6 years ago

0.0.2

6 years ago

0.0.1

6 years ago