简体   繁体   中英

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:

在此输入图像描述

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)

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(ie 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)) }
    }
}

You should never fetch data in render function. You should do it in componentDidMount function.

render is called after each state or props change, and if you execute an api call in render function, it will trigger setState and by doing so - render again and again and again...

See link

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM