In the first part of converting my Sudoku solver React app from using Flux to Redux I looked at creating the Redux Store, Action Creators, reducers, and connecting Components to the Store. In this part 2 I’m taking a look at using redux-thunk for making my api calls.
Async API calls are integrated into a Redux app by using a ‘thunk’, support is provided by adding react-thunk to your app:
npm install --save redux-thunk
react-thunk is a middleware for your Redux Store. It looks at each Action dispatched to your Store and if it’s a function instead of an Action object it executes it.
Where you create your store with createStore(), add an import for applyMiddleware and thunk:
import { applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';
const middlewares = [thunk];
Pass a call to applyMiddleware as a param to createStore() passing this middlewares array. In my app I’ve already passed the devToolsEnhancer() to createStore():
let store = createStore(puzzleDataReducer, devToolsEnhancer());
Naively, I thought I’d be able to add thunk and devToolsEnhancer() in the array and pass like this:
const middlewares = [thunk, devToolsMiddleware()];
let store = createStore(puzzleDataReducer, applyMiddleware(...middlewares));
… but this leads to some rather cryptic errors and I’ve honestly no idea what this means or how to resolve it:
VM1047:1 Uncaught TypeError: t.apply is not a function at :1:47480 at :1:33759 at :1:26192 at eval (redux.js:602) at eval (redux.js:646) at createStore (redux.js:79)
Luckily a quick search found this article, suggesting the way to combine these enhancers is to use redux’s compose() function. Import compose from redux:
import { createStore, applyMiddleware, compose } from 'redux';
Use compose() to combine thunk and the devToolsEnhancer() call like this:
compose( applyMiddleware(thunk, otherMiddleware()), window.devToolsExtension ? window.devToolsExtension() : f => f )
In my case I don’t have other middleware to include, so my createStore() was originally this:
let store = createStore(puzzleDataReducer, devToolsEnhancer());
and now including thunk and using compose() it looks like this:
let store = createStore(puzzleDataReducer, compose( applyMiddleware(thunk), window.devToolsExtension ? window.devToolsExtension() : f => f ) );
Next I need to move my Flux Action that makes my api call to a function that is returned by an Action Creator. Since react-thunk looks for functions returned by Action Creators instead of Action objects and executes them. this was a relatively minor change moving the function from my original Action to my Action Creator.
At this point I’ve got a working app, migrated from Flux to Redux. You can check the final result on GitHub here. I’ve still got some cleanup to do – in particular I don’t have a clean separation between my reducer code and my Action Creator code. I’ll code back and do some cleanup later, and also deploy a parallel copy of the same app to AWS. Right now the original Flux based app is deployed here. I’ll deploy the final Redux based app shortly.