BFilip
BFilip

Reputation: 71

componentDidUpdate causing an infinite loop

I have this parent component called ListGyms. This is a component that renders a table. In the each row of the table you can see a gym record fetched from the API and two buttons. The buttons render 2 different child components (ViewMembers or EditGym). Each of these child components are rendered one at a time conditionally (depending on which button in the row of the table is being clicked).

My trouble is in my EditGym component. Everything works correctly when sending a PUT request to the API, however, the ListGyms component does not update its state once this PUT request from EditGym has been called.

I have tried to make it work using componentDidUpdate() method in my ListGyms component, but unfortunatelly me website is lagging and constantly sending a network requests. The component is stuck in an infinity loop.

I have tried to solve my problem in several ways, but I failed. I have tried passing the method fetchingGyms as a prop to my child component EditGym, and later calling this prop function onSubmit, but I got the error this.setState is undefined. I am out of ideas really.

Here is my code:

ListGyms.js

import 'bootstrap/dist/css/bootstrap.min.css';
import { Spinner } from 'react-bootstrap';
import '../App.css';
import axios from 'axios';
import ViewMembers from './ViewMembers';
import EditGym from './EditGym';
import { unmountComponentAtNode } from 'react-dom';

class ListOfGyms extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            gyms: [],
            isItClicked: false,
            isItClickedMember: false,
            isFetching: true,
            checkForUpdate: true,
           
        }
        this.handleChange = this.handleChange.bind(this);
        this.handleChangeForMember = this.handleChangeForMember.bind(this);

    }
    
    componentDidMount() {
        this.fetchingGyms();
    }
   
    componentDidUpdate(prevProps, prevState) {
       if(prevState.gyms !== this.state.gyms){
           this.fetchingGyms();
       }
   }
    
   async fetchingGyms(){
       axios.get('https://localhost:44345/api/gyms')
           .then(res => {
               const gyms = res.data;
               this.setState({ gyms, isFetching: false });
               console.log("GYMS", gyms);
           })
           .catch(err => {
               console.log(err);
           })
   }
 

    handleChange = (e) => {
        e.preventDefault();
        this.setState({
            isItClicked: true,
            isItClickedMember: false,
            id: e.target.value

        }, () => {
            console.log("e", e.target.value);
            console.log("id", this.state.id);
        });
    }

    handleChangeForMember = (e) =>{
        this.setState({
            isItClickedMember: true,
            isItClicked: false,
            id: e.target.value
        }, () => {
            console.log("e", e.target.value);
            console.log("id", this.state.id);
        });
    }

    renderTableData() {

        return this.state.gyms.map((gym) => {
            return (
                <tr key={gym.gymID}>
                    <td>
                        <label for="gymName">Gym Name: {gym.gymName}</label>
                    </td>
                    <td>
                        <button value={gym.gymID} onClick={this.handleChange}>Edit</button>
                    </td>
                    <td>
                        <button value={gym.gymID} onClick={this.handleChangeForMember}>View Members</button>
                    </td>
                </tr>
            )
        })
    }
    render() {
        const isFetching  = this.state.isFetching;

        if (isFetching){
            return (
                <Spinner animation="border" variant="primary" />
            )
        }

        if (this.state.isItClicked) {
            return (
                <>
                    <div class="container" className="ListOfGyms">
                        <div class="row">
                            <div class="col-sm-6">
                                <h2>List of Gyms</h2>
                            </div>
                        </div>
                        <table id='gyms' class="table table-dark">
                            <tbody>
                                {this.renderTableData()}
                            </tbody>
                        </table>
                    </div>
                    <EditGym id={this.state.id}/>


                </>
            )
        }
        if (this.state.isItClickedMember) {
            return (
                <>
            <div class="container" className="ListOfGyms">
                <div class="row">
                    <div class="col-sm-6">
                        <h2>List of Gyms</h2>
                    </div>
                </div>
            <table id='gyms' class="table table-dark">
                <tbody>
                    {this.renderTableData()}
                </tbody>
            </table>
            </div>
            <ViewMembers id={this.state.id}/>
          </>
        )}

        
      
        return (
            <div class="container" className="ListOfGyms">
                <div class="row">
                    <div class="col-sm-6">
                        <h2>List of Gyms</h2>
                    </div>
                </div>
                <table id='gyms' class="table table-dark">
                    <tbody>
                        {this.renderTableData()}
                    </tbody>
                </table>
            </div>
        )
    }
}
export default ListOfGyms;
EditGym.js
import React from 'react';
import axios from 'axios';
import { Spinner } from 'react-bootstrap';

class EditGym extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            GymName: '',
            gym_description: '',
            address_line1: '',
            address_line2: '',
            state: '',
            postcode: '',
            Country: '',
            isFetching: true,
            
        }
        this.onChangeGymsName = this.onChangeGymsName.bind(this);
        this.onChangeGymsDescription = this.onChangeGymsDescription.bind(this);
        this.onChangeAddressLine1 = this.onChangeAddressLine1.bind(this);
        this.onChangeAddressLine2 = this.onChangeAddressLine2.bind(this);
        this.onChangeState = this.onChangeState.bind(this);
        this.onChangePostCode = this.onChangePostCode.bind(this);
        this.onChangeCountry = this.onChangeCountry.bind(this);
        this.onSubmit = this.onSubmit.bind(this);
    }



    onChangeGymsName(e) {
        this.setState({
            GymName: e.target.value
        });
    }
    onChangeGymsDescription(e) {
        this.setState({
            gym_description: e.target.value
        });
    }
    onChangeAddressLine1(e) {
        this.setState({
            address_line1: e.target.value
        });
    }
    onChangeAddressLine2(e) {
        this.setState({
            address_line2: e.target.value
        });
    }
    onChangeState(e) {
        this.setState({
            state: e.target.value
        });
    }
    onChangePostCode(e) {
        this.setState({
            postcode: e.target.value
        });
    }
    onChangeCountry(e) {
        this.setState({
            Country: e.target.value
        });
    }
    componentDidMount(){
        this.setState({ isFetching: false})
    }
    
    onSubmit(e) {
        var id = this.props.id;
        
        e.preventDefault();
        
        const obj = {
            GymName: this.state.GymName,
            gym_description: this.state.gym_description,
            address_line1: this.state.address_line1,
            address_line2: this.state.address_line2,
            State: this.state.state,
            postcode: this.state.postcode,
            Country: this.state.Country
        }
        
        axios.put(`https://localhost:44345/api/gyms/${id}/`, obj)
            .then(res => console.log(res.data));
        
    
    }

    render() {
        const isFetching = this.state.isFetching;
        if (isFetching){
            return ( 
                <Spinner animation="border" variant="primary" />
            )
        }
        return (
            <div class="form-group">
                <h2>Edit the Gym Infomation</h2>
                <form onSubmit={this.onSubmit}>
                    <div>
                        <label htmlFor="gymName" class="form-check-label">Gym Name</label>
                        <input name="gymName" value={this.state.GymName} onChange={this.onChangeGymsName} class="form-control" placeholder="Gym Name"/>
                    </div>
                    <div>
                        <label htmlFor="gymDescription" class="form-check-label">Description</label>
                        <input name="gymDescription" value={this.state.gym_description} onChange={this.onChangeGymsDescription} class="form-control" placeholder="Description"/>
                    </div>
                    <div>
                        <label htmlFor="addressLine1">Address Line 1</label>
                        <input name="addressLine1" value={this.state.address_line1} onChange={this.onChangeAddressLine1} class="form-control" placeholder="Address Line 1"/>
                    </div>
                    <div>
                        <label htmlFor="addressLine2">Address Line 2</label>
                        <input name="addressLine2" value={this.state.address_line2} onChange={this.onChangeAddressLine2} class="form-control" placeholder="Address Line 2"/>
                    </div>
                    <div>
                        <label htmlFor="state">State</label>
                        <input name="state" value={this.state.state} onChange={this.onChangeState} class="form-control" placeholder="State"/>
                    </div>
                    <div>
                        <label htmlFor="postCode">Postcode</label>
                        <input name="postCode" value={this.state.postcode} onChange={this.onChangePostCode} class="form-control" placeholder="Postcode"/>
                    </div>
                    <div>
                        <label htmlFor="country">Country</label>
                        <input name="country" value={this.state.country} onChange={this.onChangeCountry} class="form-control" placeholder="Country"/>
                    </div>
                    <button class="form-control" type="submit">Update a Gym Values</button>
                </form>
            </div>
        )
    }
}
export default EditGym;

Upvotes: 1

Views: 204

Answers (2)

Davin Tryon
Davin Tryon

Reputation: 67336

To me, it looks like gyms is a JavaScript object. And therefore, prevState.gyms will never equal this.state.gyms:

    componentDidUpdate(prevProps, prevState) {
       if(prevState.gyms !== this.state.gyms){
           this.fetchingGyms();
       }
   }

Do you need a deep equal?

Upvotes: 1

Wiktoor
Wiktoor

Reputation: 176

In every onChange (handle) method of Yours, You should add event.preventDefault(). What this will do, it will prevent and infinite rerender of components when they are not updated. It should improve Your optimization.

Upvotes: 0

Related Questions