Thunk

One of the most common uses of middleware is to solve asynchronicity.

We can install the thunk middleware using:

However, we will write a thunk action creator to make an asynchronous request to an API and dispatch an action when the response is received.

Ex:


  // ./src/index.js

  import React from 'react';
  import ReactDOM from 'react-dom';
  import { Provider } from 'react-redux';

  import './index.css';
  import App from './App';
  import configureStore from './store';
  import { fetchFruits } from './actions/fruitActions';

  const store = configureStore();
  store.dispatch(fetchFruits());

  ReactDOM.render(
    <React.StrictMode>
      <Provider store={store}>
        <App />
      </Provider>
    </React.StrictMode>,
    document.getElementById('root')
  );



-------------------------------------------------------------------



  // ./store/configureStore.js

  import { createStore, applyMiddleware } from 'redux';
  import logger from 'redux-logger';

  import rootReducer from './reducers/rootReducer';
  import thunk from './middleware/thunkMiddleware';

  const configureStore = (preloadedState = {}) => {
    return createStore(
      rootReducer,
      preloadedState,
      applyMiddleware(thunk, logger),
    );
  };

  export default configureStore;




  ---------------------------------------




  // ./store/middleware/thunkMiddleware.js

  const thunk = ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState);
    }
    return next(action);
  };

  export default thunk;

  ----------------------------------------

  // ./store/actions/fruitActions.js

  export const RECEIVE_TWEETS = 'RECEIVE_TWEETS';

  // the below is a thunk action creator
  export const fetchTweets = () => {
    return async dispatch => {
      const res = await fetch(`/tweets`); // Using proxy in our package.json, 
               // we're able to set the path to simply '/tweets'
      const data = await res.json();
      dispatch(receiveTweets(data.tweets));
    }
  };

  // the below is an action creator
  const receiveTweets = (tweets) => {
    return {
      type: RECEIVE_TWEETS,
      tweets,
    };
  };





  -------------------------------------------



  // ./store/reducers/fruitReducer.js

  import { RECEIVE_FRUITS } from '../actions/fruitActions';

  const fruitReducer = (state = [], action) => {
    Object.freeze(state);
    switch (action.type) {
      case RECEIVE_FRUITS:
        return action.fruits;
      default:
        return state;
    }
  };

  export default fruitReducer;