Si8
Si8

Reputation: 9225

Infinite Looping Errorr in ReactJS

Layout.js

import React, {Component} from "react"
import Aircraft from "./Aircraft"

class Layout extends Component {
    constructor(props) {
        super(props)
        this.state = {
            test: true,
            acn: ""
        }
    }

    WhatIsAircraftName = (acn) => {
        this.setState({
            acn: acn
        })
    }

    render() {
        return (
            <div>
                <div className="mainD posRel hidO">
                    <div className="posRel hidO topD">

                    </div>
                    <div className="posRel hidO bottomD container">
                        <Aircraft clk={this.WhatIsAircraftName} />
                    </div>
                </div>
            </div>
        )
    }
}

export default Layout

Aircraft.js

import React, {Component} from "react"

import Loading from "./Loading"

class Aircraft extends Component {
    constructor(props) {
        super(props)
        this.state = {
            aircraft: [],
            loading: false,
            utilized: 0
        }
    }

    componentDidMount() {
        let mod_this = this
        this.setState({
            loading: true
        })
        fetch("https://some-endpoint")
        .then(function(response) {
            if (response.ok) {
                return response.json()
            }
          })
          .then(function(myJson) {
              mod_this.setState({
                  aircraft: myJson,
                  loading: false
              })
          })
    }

    DisplayAircraft() {
        let ac = this.state.aircraft
        this.props.clk(ac.data[0].ident)
        return (
            <div className="acD posRel hidO selected">
                {
                    <h2>{ac.data[0].ident}</h2>
                }
            </div>
        )
    }

    render() {
        const {aircraft} = this.state

        return (
            <div className="posRel hidO leftD">
                <h1>Aircrafts</h1>
                {
                    !aircraft || aircraft.length <= 0 || this.state.loading ?
                    <Loading /> :
                    this.DisplayAircraft()
                }
            </div>
        )
    }
}

export default Aircraft

When I run my app, I get setState loop error:

Unhandled Rejection (Invariant Violation): Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.

The reason I am doing it like this is the Aircraft component will get the Aircraft ID which I want to send to another child component, hence I am sending it to the parent to use it as props for another component.

Section is:

Layout.WhatIsAircraftName [as clk]

Upvotes: 1

Views: 75

Answers (2)

Justin Savala
Justin Savala

Reputation: 111

Try something like this, I've moved the logic around that you are no longer updating the state from the render function.

import React, {Component} from "react"

import Loading from "./Loading"

class Aircraft extends Component {
    constructor(props) {
        super(props)
        this.state = {
            aircraft: [],
            loading: false,
            utilized: 0
        }
    }

    componentDidMount() {
        let mod_this = this
        this.setState({
            loading: true
        })
        fetch("https://some-endpoint")
        .then(function(response) {
            if (response.ok) {
                return response.json()
            }
          })
          .then(function(myJson) {
              this.props.clk(myJson.data[0].ident)
              mod_this.setState({
                  aircraft: myJson,
                  loading: false
              })
          })
    }

    render() {
        const {aircraft} = this.state

        return (
            <div className="posRel hidO leftD">
                <h1>Aircrafts</h1>
                {
                    !aircraft || aircraft.length <= 0 || this.state.loading ?
                    <Loading /> :
                    (<div className="acD posRel hidO selected">
                       <h2>{aircraft.data[0].ident}</h2>
                     </div>)
                }
            </div>
        )
    }
}

export default Aircraft

This should at least get it working for you but based on what I can see in your code I do have another suggestion. It would be much simpler to do the API call from the Layout component and then pass the aircraft information down to the Aircraft component via Props. In your current code you are having to pass the data back up via a function you passed via a prop which makes it all a little more complicated. React tends to be much easier to work with when you are passing data down the chain.

Hope that helps!

Upvotes: 1

skovy
skovy

Reputation: 5650

The problem is that your render method is not pure and is updating state.

The render() function should be pure, meaning that it does not modify component state, it returns the same result each time it’s invoked, and it does not directly interact with the browser. - React Docs

In your parent component, you have WhatIsAircraftName which is calling setState. Whenever this method is invoked, it will trigger a state update which will trigger a render. In the parent's render you are passing the Aircraft component the prop clk which is assigned to WhatIsAircraftName. Then, in Aircraft's render, it's calling DisplayAircraft, which is invoking the prop clk which starts us back at the top.

Layout#render -> Aircraft#render -> DisplayAircraft -> this.props.clk -> WhatIsAircraftName -> this.setState -> Layout#render -> etc. (infinite loop).

This loop needs to be broken, setState should never be invoked within render.

Upvotes: 3

Related Questions