Search code examples
reactjsreduxreact-reduxredux-thunk

Infinite loop when fetch data in middleware


I try to fetch data and display it into a react component, but i have an infinite loop on the fetch call in my middleware and action seems not dispatched. i Receive no result in my post component.

Action.js :

import { DATA_LOADED } from './../constants/action-types';

export function getData() {
    return {type: DATA_LOADED}
}

Middleware :

export function asyncMiddleWare({dispatch}) {
    return function(next) {
        return function (action) {
            if (action.type === DATA_LOADED) {
                return fetch('https://jsonplaceholder.typicode.com/posts')
                .then(response => response.json())
                .then(json => {
                    console.log('---');
                    console.log('infinite calls');
                    console.log('---');
                    dispatch({type:DATA_LOADED, payload: json});
                })
            }
            return next(action);
        }
    }
}

Reducer :

    if (action.type === DATA_LOADED) {
        return Object.assign({}, state, {
            articles: state.remoteArticles.concat(action.payload)
        })
    }

and the store

import {createStore, applyMiddleware, compose} from 'redux';
import rootReducer from '../reducers/index';
import {asyncMiddleWare } from "../middleware";
import thunk from "redux-thunk";

const storeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(rootReducer, storeEnhancers(applyMiddleware(asyncMiddleWare, thunk)));

export default store;

I load data in componentDidMount method in my component :

import React from "react";
import { connect } from "react-redux";
import { getData } from "./js/actions/index";

class Post extends React.Component {

    componentDidMount() {
        this.props.getData();
    }

    render () {
        console.log(this.props.articles);
      return (
      <div className='post'>
          {this.props.articles.map(article => (
              <div className='post'>
                  {article}
              </div>
      ))}
      </div>
      )
    }
  }



  const mapStateToProps = (state) => {
    return {
      articles: state.remoteArticles.slice(0, 10)
    };
  }
export default connect(
    mapStateToProps,
    {getData}
)(Post);

Solution

  • If you look into your middleware resolved promise function you'll notice that you are dispatching action of same type (DATA_LOADED) again which causes middleware to process it again.

    Take a look at this approach

    export function asyncMiddleWare({dispatch}) {
        return function(next) {
            return function (action) {
                if (action.type === DATA_LOAD_REQUEST) {
                    return fetch('https://jsonplaceholder.typicode.com/posts')
                    .then(response => response.json())
                    .then(json => {
                        console.log('---');
                        console.log('infinite calls');
                        console.log('---');
                        dispatch({type:DATA_LOAD_SUCCESS, payload: json});
                    }, (error) => {
                        dispatch({type:DATA_LOAD_ERROR, payload: error});
                    })
                }
                return next(action);
            }
        }
    }
    

    You should separate your REQUEST, SUCCESS and ERROR calls so when you call each of those actions you don't end up in infinite loop.