Skip to content

Latest commit

 

History

History
114 lines (77 loc) · 5 KB

16-Wrapping_dispatch_to_Recognize_Promises.md

File metadata and controls

114 lines (77 loc) · 5 KB

16. Wrapping dispatch() to Recognize Promises

Video Link

The receiveTodos action creator is not very useful by itself because anytime we call it, we want to fetch the todos first. Since fetchTodos and receiveTodos accept the same arguments, it would be great if we could group this code into a single action creator.

Existing fetchData() inside VisibleTodoList

fetchData() {
  const { filter, receiveTodos } = this.props;
  fetchTodos(filter).then(todos =>
    receiveTodos(filter, todos)
  );
}

Refactoring our Action creators

We'll get started by importing our fake API into our action creators file (src/actions/index.js).

import * as api from '../api'

Now we'll add an asynchronous action creator called fetchTodos. It takes filter as an argument, and then it calls the API's fetchTodos method with it.

I'm using the Promise then method to transform the result of the Promise from the response to the action object generated by receiveTodos given the filter and the response.

Inside src/actions/index.js

export const fetchTodos = (filter) =>
  api.fetchTodos(filter).then(response =>
    receiveTodos(filter, response)
  );

receiveTodos returns an action object synchronously, but fetchTodos returns a Promise that resolves through the action object.

Now can stop exporting receiveTodos from our action creators because we can change the components to use fetchTodos directly.

Updating VisibleTodoList

Back in our component file, we can use the fetchTodos prop injected by connect. This corresponds to the new asynchronous fetchTodos action creator we just wrote.

We can remove import { fetchTodos } from '../api' because from now on we will be using the fetchTodos action creator, which is injected into the props by connect.

fetchData() {
  const { filter, fetchTodos } = this.props;
  fetchTodos(filter);
}

Recapping what we just did...

The fetchTodos action creator calls the fetchTodos function from the API, but then it transforms its result into a Redux action generated by receiveTodos.

However, by default, Redux only allows dispatching plain objects rather than Promises. We can teach it to recognize Promises by using the same trick that we used in addLoggingToDispatch() inside of configureStore.js (recall that the addLoggingToDispatch function takes the dispatch from the store and returns a new version of dispatch that logs every action and the state).

Adding Promise Support

Inside of configureStore.js, we will create a function addPromiseSupport() that takes the store and returns a version of dispatch that supports promises.

First, we will grab the rawDispatch function at it is defined on the store so that we can call it later. We return a function that has the same API as a dispatch function-- that is, it takes an action.

const addPromiseSupportToDispatch = (store) => {
  const rawDispatch = store.dispatch;
  return (action) => {
    if (typeof action.then === 'function') {
      return action.then(rawDispatch);
    }
    return rawDispatch(action);
  };
};

Since we don't know if the action is a real action or a promise of an action, we check if it has a then method that is a function. If it does, then we know it is a promise. If the action is a promise, we wait for it to resolve to an action object that we will pass through rawDispatch.

Otherwise, we'll just call rawDispatch right away with the action object we received.

Our new addPromiseSupportToDispatch function allows us to dispatch both actions and promises that resolve to actions.

To finish up, we need to call our new function one more time before returning the store to the app.

// At the bottom of `configureStore.js`
const configureStore = () => {
  const store = createStore(todoApp);

  if (process.env.NODE_ENV !== 'production') {
    store.dispatch = addLoggingToDispatch(store);
  }

  store.dispatch = addPromiseSupportToDispatch(store);

  return store;
};

If we run the app now, we will still see the 'RECEIVE_TODOS' action being dispatched when the response is ready. However, the component uses a more convenient API that encapsulates the asynchronous logic in the asynchronous action creator.

Order Matters

Remember that the order in which we override the dispatch function inside of configureStore is important.

If we change it so that we call addPromiseSupportToDispatch before addLoggingToDispatch, the action will first be printed and then the promise will be processed

This would give us an action type of undefined and we see the Promise instead of the action, which is not very useful.

Recap at 4:15 in video

<- Prev Next ->