redux-ready-wrapper

1.1.15 • Public • Published

NOTE: This repo is deprecated and archived. Please use redux-thunk-init instead.

What is redux-ready-wrapper?

npm version build status npm downloads

  • A middleware of Redux library that handles asynchronous action flow.
  • If you are familiar with redux-thunk, you probably already know how to use redux-ready-wrapper, as alternative.
  • This middleware allows us to return a higher-order function (ready / wrap) of "thunk" in action creator instead of an action, by accepting a callback function as argument.

API

a) ready - function which accepts two arguments and returns "thunk" function that eventually returns a Promise:

  • callback (mandatory) - A callback function that will receive dispatch and getState methods from redux's store object.

  • options (optional, default: {}) - A user defined options to be passed as second argument and assigned to ready action (the object), in this form:

{
  type: 'READY_ACTION',
  options: /* your options values */
}

Once ready is invoked, it will dispatch additional ready action BEFORE dispatching the next targeted action in callback. It could be useful if you plan to have a generic reducer for some controls with provided options during the "ready" phase.

b) wrap - function which is similar to ready, except it only accepts one callback argument without dispatching ready action:

  • callback (mandatory) - A callback function that will receive dispatch and getState methods from redux's store object.

Using wrap instead of ready if you just need asynchronous handling without having ready action to be dispatched.

Installation

  • To install package:
npm install redux-ready-wrapper --save
  • import and apply middleware:
import { createStore, applyMiddleware } from "redux";
import { createLogger } from "redux-logger";
import readyWrapper from "redux-ready-wrapper"; // <--- import this
import rootReducer from "./reducer";

// add middleware by calling it!
const middlewares = [readyWrapper()];

if (process.env.NODE_ENV !== "production") {
  middlewares.push(createLogger());
}

export default createStore(rootReducer, applyMiddleware(...middlewares));

Usage

  • Example 1:
import { wrap } from "redux-ready-wrapper"; // <--- import `wrap` and/or `ready`
import { SOMETHING, SOMETHING_NEW } from "./constants";

// return `wrap` function instead
// so that we can implement promise chaining
export function doSomething() {
  return wrap(dispatch =>
    dispatch({
      type: SOMETHING,
      payload: {
        key1: "value1",
        key2: "value2"
      }
    })
  );
}

// extend received action (the source) and return it as new action
export function extendSomething(action = {}) {
  const payload = { ...action.payload, key2: "new value 2", key3: "value 3" };
  const newAction = { ...action, type: SOMETHING_NEW, payload };

  return newAction;
}

// do something else with newly extended action or,
// throw error if it is invalid.
export function doSomethingElse(action = {}) {
  if (action.type !== SOMETHING_NEW) {
    throw new Error("Invalid new action received!");
  }

  console.log(`Yay! new action received:  ${JSON.stringify(action)}`);

  return action;
}

// assumed `store` object is available:
const { dispatch } = store;

dispatch(doSomething())
  .then(dispatched => extendSomething(dispatched)) // extend dispatched action from `doSomething`
  .then(extended => dispatch(doSomethingElse(extended))) // passing extended action to `doSomethingElse` and dispatch
  .catch(error => alert(`Oops! ${error}`)); // alert thrown error message if invalid action
  • Provide options as second argument to ready function and deal with reducers, eg:
// dispatch action from action creator with options provided
export function doSomething() {
  const options = {
    key1: "value1",
    key2: "value2"
  };

  return ready(dispatch => dispatch(actionCreator()), options);
}

// add a reducer say, `something` pure function
export function something(state = {}, action) {
  if (action.type === "READY_ACTION") {
    // manage state change and `action.options`
  } else if (action.type === "SOMETHING") {
    // manage state change for `SOMETHING`
  }

  return state;
}
  • Example 2 (React-Redux):
// filename: userActions.js

import { ready } from 'redux-ready-wrapper';
import { LOGIN_NOTIFICATION } from './constants';

// dispatch user login notification
// message could be handled in your middleware
export function showLoginNotification(message) {
  return {
    type: LOGIN_NOTIFICATION,
    message
  };
}

// user login action
export function userLogin(formData) {
  return ready(dispatch => (
    fetch('/login', { method: 'POST', body: formData })
    .then(response => response.json())
    .then(user => {
      if (!user.id) throw new Error('Login failure. Please try again!');
      return dispatch(showLoginNotification('You have logged in!'));
    })
    .catch(error => dispatch(showLoginNotification(error)))
  ));
}

// proceed to the next action after user logged in
export function doNextAfterLoggedIn() {
  return ready((dispatch, getState) => {
    const current = getState();
    ...
  });
}
// filename: User.js
// User react component that has `handleSubmit` method
// and with bound user actions in `mapDispatchToProps`

import React, { Component } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as userActions from './userActions';

class User extends Component {
  ...

  // say, invoked through `onSubmit` via user login form
  // and proceed to next once logged in successfully
  handleSubmit(evt) {
    evt.preventDefault();
    const { userLogin, doNextAfterLoggedIn } = this.props.actions;
    const formData = ...;

    // note that promise here is from `fetch`
    userLogin(formData)
    .then(() => doNextAfterLoggedIn());
  }

  ...
}

const mapStateToProps = ...;
const mapDispatchToProps = dispatch => ({
  actions: bindActionCreators(userActions, dispatch)
});

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(User);

Note: You may not need to return the wrapper in all action creators. It should only be used based on the context.

  • Returning function from action creator without using ready / wrap:
export function actionCreator() {
  // return a function in action creator with received `store` object
  return store => {
    // do something with `store.dispatch()`
    // or with 'store.getState()'
  };

  // or, return function by using object destructuring approach
  return ({ dispatch, getState }) => {
    // do something with `dispatch()`
    // or with 'getState()'
  };
}

License

MIT

Package Sidebar

Install

npm i redux-ready-wrapper

Weekly Downloads

0

Version

1.1.15

License

MIT

Unpacked Size

62.5 kB

Total Files

15

Last publish

Collaborators

  • boris.ding82