Skip to content

Latest commit

 

History

History
190 lines (160 loc) · 3.98 KB

UPGRADING.md

File metadata and controls

190 lines (160 loc) · 3.98 KB

3.x to 4.0.0

This release introduces changes to error handling.

Previously, the parameter of the rejected promise callback was both the dispatched action and an error object. The middleware also always constructed a new error object, which caused unexpected mutation and circular references.

Now, the parameter of the rejected promise callback is the value of reject. The middleware does not construct a new error; it is your responsibility to make sure the promise is rejected with an Error object.

// before
const bar = () => ({
  type: 'FOO',
  payload: new Promise(() => {
    reject('foo');
  })
});.then(() => null, ({ reason, action }) => {
  console.log(action.type): // => 'FOO'
  console.log(reason.message); // => 'foo'
});

// after
const bar = () => ({
  type: 'FOO',
  payload: new Promise(() => {

    /**
     * Make sure the promise is rejected with an error. You
     * can also use `reject(new Error('foo'));`. It's a best
     * practice to reject a promise with an Error object.
     */
    throw new Error('foo');
  })
});.then(() => null, error => {
  console.log(error instanceof Error); // => true
  console.log(error.message); // => 'foo'
});

2.x to 3.0.0

This release introduces some major changes to the functionality of the middleware:

First, the middleware returns a promise instead of the action.

// before
const foo = () => ({
  type: 'FOO',
  payload: {
    promise: Promise.resolve('foo')
  }
});

foo().action.promise.then(value => {
  console.log(value); // => 'foo'
});

// after
const bar = () => ({
  type: 'BAR',
  payload: Promise.resolve('bar')
});

bar().then(({ value }) => {
  console.log(value); // => 'bar'
});

Second, a new promise is created so .then() and .catch() work as expected.

// before
const foo = () => ({
  type: 'FOO',
  payload: {
    promise: Promise.reject('foo')
  }
});

foo().action.promise.then(
  value => {
    console.log(value); // => 'foo'
  },
  reason => {
    // nothing happens
  }
);

// after
const bar = () => ({
  type: 'BAR',
  payload: Promise.reject('bar')
});

bar().then(
  ({ value }) => {
    // ...
  },
  ({ reason }) => {
    console.log(reason); // => 'bar'
  }
);

const baz = () => ({
  type: 'BAZ',
  payload: new Promise((resolve, reject) => {
    throw 'baz'
  })
});

bar().catch(({ reason }) => {
  console.log(reason) // => 'baz'
});

Third, promises can be explicitly or implicitly in the action object.

// before
const foo = () => ({
  type: 'FOO',
  payload: {
    promise: Promise.resolve()
  }
});

// after, with implicit promise as the value of the 'payload' property
const bar = () => ({
  type: 'BAR',
  payload: Promise.resolve()
});

Of course, if you prefer the explicit syntax, this still works. This syntax is also required for optimistic updates.

// after, but with explicit 'promise' property and 'data' property
const bar = () => ({
  type: 'BAZ',
  payload: {
    promise: Promise.resolve(),
    data: ...
  }
});

Fourth, thunks are no longer bound to the promise. If you are chaining actions with Redux Thunk, this is critical change.

// before, with Redux Thunk
const foo = () => ({
  type: 'FOO',
  payload: {
    promise: new Promise((resolve, reject) => {
      ...
    }).then(
      value => (action, dispatch) => {
        // handle fulfilled
        dispatch(someSuccessHandlerActionCreator());
      },
      reason => (action, dispatch) => {
        // handle rejected
        dispatch(someErrorHandlerActionCreator());
      }
    )
  }
});

// after, with Redux Thunk
const bar = () => {
  return (dispatch, getState) => {


    return dispatch({
      type: 'FOO',
      payload: Promise.resolve('foo')
    }).then(
      ({ value, action }) => {
        console.log(value); // => 'foo'
        console.log(action.type); // => 'FOO_FULFILLED'
        dispatch(someSuccessHandlerActionCreator());
      },
      ({ reason, action }) => {
        // handle rejected
        dispatch(someErrorHandlerActionCreator());
      }
    );
  };
};