Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React setState in promise causing infinite loop

Expected

When fetchServices() is called, api.getServices is called and in the promise this.setState is called to change fetchingServices to false. Which then hides the loading spinner animation.

Results

For some reason the App is stuck in an infinite loop:

enter image description here

In my ServicesContainer

constructor(props) {
  super(props);
  this.state = {
    services: props.state.servicesReducer.services,
    fetchingServices: true,
    addingService: false
  }
  this.fetchServices = this.fetchServices.bind(this);
}

The return()

return (
  <div className='services-container'>
    <ul className='services-list'>
      <li>
        <AddServiceContainer />
      </li>
      { this.state.fetchingServices
          ? <div className="icon-spin5 animate-spin"></div>
          : null }

      { this.fetchServices() }
    </ul>
  </div>
)

Finally fetchServices()

fetchServices() {
  console.log('fetchServices')
  api.getServices(12345).then(res => {
    console.log(' api.getServices res:', res)

    this.setState({
      fetchingServices: false
    });
  });
}

Full code

import React, { Component } from 'react'
import { connect } from 'react-redux'

import { AddServiceContainer } from './AddServiceContainer'
import { ServiceCard } from '../../components'
import { getServices } from '../../actions'
import * as api from '../../services/api'

export class ServicesContainer extends Component {
    constructor(props) {
        super(props);
        this.state = {
            services: props.state.servicesReducer.services,
            fetchingServices: true,
            addingService: false
        }
        this.fetchServices = this.fetchServices.bind(this);
    }

    onFormSubmit(e, user) {
        e.preventDefault();
        this.props.searchUser(user)
    }

    fetchServices() {
        console.log('fetchServices')
        api.getServices(12345).then(res => {
            console.log(' api.getServices res:', res)

            this.setState({
              fetchingServices: false
            });
        });
    }

    render() {
        return (
            <div className='services-container'>
                <ul className='services-list'>
                    <li>
                        <AddServiceContainer />
                    </li>
                    { this.state.fetchingServices
                            ? <div className="icon-spin5 animate-spin"></div>
                            : null }

                    { this.fetchServices() }
                </ul>
            </div>
        )
    }
}

const mapStateToProps = (state) => {
    return {
        state
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        getServices: (services) => { dispatch(getServices(services)) }
    }
}

const ServicesListContainer = ServicesContainer;
export default connect(mapStateToProps, mapDispatchToProps)(ServicesListContainer)
like image 732
Leon Gaban Avatar asked Dec 24 '22 15:12

Leon Gaban


1 Answers

Whenever you do setState, the render method is called again. Now problem here is that you are calling fetchServices() method inside the render method. Now whenever fetchServices() is called it calls an api. When the result of the api come, you are setting the state using setState, which causes rerender(i.e. your render method is called again), which calls the fetchServices() again. This is why it is going in infinite loop.

The solution: You should call your fetchServices() in componentWillMount/componentDidMount method like this:

import React, { Component } from 'react'
import { connect } from 'react-redux'

import { AddServiceContainer } from './AddServiceContainer'
import { ServiceCard } from '../../components'
import { getServices } from '../../actions'
import * as api from '../../services/api'

export class ServicesContainer extends Component {
    constructor(props) {
        super(props);
        this.state = {
            services: props.state.servicesReducer.services,
            fetchingServices: true,
            addingService: false
        }
        this.fetchServices = this.fetchServices.bind(this);
    }

    componentWillMount(){
       this.fetchServices();
    }
    onFormSubmit(e, user) {
        e.preventDefault();
        this.props.searchUser(user)
    }

    fetchServices() {
        console.log('fetchServices')
        api.getServices(12345).then(res => {
            console.log(' api.getServices res:', res)

            this.setState({
              fetchingServices: false
            });
        });
    }

    render() {
        return (
            <div className='services-container'>
                <ul className='services-list'>
                    <li>
                        <AddServiceContainer />
                    </li>
                    { this.state.fetchingServices
                            ? <div className="icon-spin5 animate-spin"></div>
                            : null }

                </ul>
            </div>
        )
    }
}

const mapStateToProps = (state) => {
    return {
        state
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        getServices: (services) => { dispatch(getServices(services)) }
    }
}
like image 66
Prakash Sharma Avatar answered Dec 28 '22 11:12

Prakash Sharma