Redux-Thunk: A better way to fetch data

Fetching data is Redux apps can be streamlined by using redux-thunk. We are going to use redux-thunk to fetch weather data

The idea behind redux-thunk is that we return a function from an action that gets passed dispatch. This allows us to do asynchronous things (like data fetching) in our actions:

function someAction()
  // Notice how we return a function – this is what's called a "thunk"!
  return function thisIsAThunk(dispatch) {
    // Do something asynchronous in here
  }
}

First implementation

Let’s try to write an action called fetchData that fetches our data! Start with the basic structure:

// actions.js

/* …more actions here… */

export function fetchData() {
  return function thunk(dispatch) {
    // LET'S FETCH OUR DATA HERE
  }
}

Now let’s copy and paste the xhr call from the App component and put it in there:

// actions.js

/* …more actions here… */

export function fetchData() {
  return function thunk(dispatch) {
    xhr({
      url: url
    }, function (err, data) {

      var body = JSON.parse(data.body);
      var list = body.list;
      var dates = [];
      var temps = [];
      for (var i = 0; i < list.length; i++) {
        dates.push(list[i].dt_txt);
        temps.push(list[i].main.temp);
      }

      self.props.dispatch(setData(body));
      self.props.dispatch(setDates(dates));
      self.props.dispatch(setTemps(temps));
      self.props.dispatch(setSelectedDate(''));
      self.props.dispatch(setSelectedTemp(null));
    });
  }
}

Now we need to fix three things: 1) We need to import xhr, 2) we need to get the URL from the action and 3) we need to rename all self.props.dispatch calls to dispatch:

// actions.js

// REQUIRE xhr
import xhr from 'xhr';

/* …more actions here… */

// PASS URL IN HERE
export function fetchData(url) {
  return function thunk(dispatch) {
    xhr({
      url: url
    }, function (err, data) {

      var data = JSON.parse(data.body);
      var list = data.list;
      var dates = [];
      var temps = [];
      for (var i = 0; i < list.length; i++) {
        dates.push(list[i].dt_txt);
        temps.push(list[i].main.temp);
      }
      // RENAME self.props.dispatch TO dispatch
      dispatch(setData(data));
      dispatch(setDates(dates));
      dispatch(setTemps(temps));
      dispatch(setSelectedDate(''));
      dispatch(setSelectedTemp(null));
    });
  }
}

Well, that was easy! That’s our thunked action done – let’s call it from our App component:

/* … */

class App extends React.Component {
  fetchData = (evt) => {
    evt.preventDefault();

    var location = encodeURIComponent(this.props.location);

    var urlPrefix = 'http://api.openweathermap.org/data/2.5/forecast?q=';
    var urlSuffix = '&APPID=dbe69e56e7ee5f981d76c3e77bbb45c0&units=metric';
    var url = urlPrefix + location + urlSuffix;

    this.props.dispatch(fetchData(url));
  },
  onPlotClick = (data) => { /* … */ },
  changeLocation = (evt) => { /* … */ },
  render() { /* … */ }
});

/* … */

That makes our App so much nicer to work with already!

Wiring it up

The last step is wiring up redux-thunk. redux-thunk is a so-called “middleware”. Middlewares sit in between the action and the reducers, every action you dispatch gets passed to all middlewares you add. (that’s why they’re called middle ware)!

Now let’s apply the thunk middleware in our createStore call in index.js:

// index.js

/* … */
import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';

/* … */

var store = createStore(
  mainReducer,
  applyMiddleware(thunkMiddleware)
);
/* … */

And that’s it, everything should be working again now. Look how easy it is to handle our components, how nicely everything is separeted by concern and how easy it would be to add a new feature to our app! That’s the power of redux, our application is easier to reason about and to handle, instead of having one massive top-level App component we separate the concerns properly.

Here’s our Redux app in action

Get hands-on with 1200+ tech skills courses.