0

I have a problem with infinite loop componentDidMount calling itself infinitely when I do something like this:

this.props.setRootState({industries: ['hello','world']});

Whole code looks like this:

import React from 'react';
import {Route, Link} from 'react-router-dom';
import FourthView from '../fourthview/fourthview.component';
import {Bootstrap, Grid, Row, Col, Button, Image, Modal, Popover} from 'react-bootstrap';
import Header from '../header/header.component';
import style from './information.style.scss';
import industryApi from '../industries/industry.api';

class InformationFilter extends React.Component {

    constructor(props) {
        super(props);
        this.state = {};
    }

    componentDidMount() {
        industryApi.getAll().then(response => {
            if (response.data) {
                this.props.setRootState({industries: response.data._embedded.industries});
            } else {
                console.log(response);
            }
        });
    }


    onIndustryChangeOption(event) {
        this.props.setRootState({selectedIndustry: event.target.value});
    }

    onJobChangeOption(event) {
        this.props.setRootState({selectedJob: event.target.value});
    }

    render() {
        return (
            <div className={"wrapperDiv"}>
                <div className={"flexDivCol"}>
                    <div id="header">
                        <Header size="small"/>
                    </div>
                    <div id="industryFilter">
                        <h2 className={"center"} style={{marginBottom: '25px'}}>Tietoa Aloista</h2>
                        <p className={"primaryColor center"}>Valitse opintoala</p>
                        <div className={"industries dropdownListHolder center"}>
                            <select id={"dropdownList"} onChange={(e) => this.onIndustryChangeOption(e)}
                                    value={this.props.rootState.selectedIndustry}>
                                {this.props.rootState.industries.map((industry, i) => <option key={i}
                                                                                              value={industry.id}>{industry.title}</option>)}
                            </select>
                        </div>
                        <p className={"primaryColor center"}>Valitse työtehtävä</p>
                        <div className={"jobs dropdownListHolder center"}>
                            <select id={"dropdownList"} onChange={(e) => this.onJobChangeOption(e)}
                                    value={this.props.rootState.selectedJob}>
                                {this.props.rootState.industries.map((job, i) => <option key={i}
                                                                                         value={job.id}>{job.text}</option>)}
                            </select>
                        </div>
                    </div>

                    <div id="industryDescription">
                        <h4>Ravintola- ja cateringala</h4>
                        <p className={"secondaryColor"}>
                            Donec facilisis tortor ut augue lacinia, at viverra est semper.
                            Sed sapien metus, scelerisque nec pharetra id, tempor a tortor. Pellentesque non dignissim
                            neque. Ut porta viverra est, ut dignissim elit elementum ut. Nunc vel rhoncus nibh, ut
                            tincidunt turpis. Integer ac enim pellentesque, adipiscing metus id, pharetra odio. 
                        </p>
                        <p className={"secondaryColor"}>
                            Donec facilisis tortor ut augue lacinia, at viverra est semper.
                        Sed sapien metus, scelerisque nec pharetra id, tempor a tortor. Pellentesque non dignissim
                        neque. Ut porta viverra est, ut dignissim elit elementum ut. Nunc vel rhoncus nibh, ut
                        tincidunt turpis. Integer ac enim pellentesque, adipiscing metus id, pharetra odio. 
                        </p>
                    </div>

                    <div id={"btnFilter"}>
                        <Link to='/information-job'>
                            <div onClick={() => this.props.setRootState({globalVariable: 'bar'})}>
                                <Button className={"primaryBtn"}>Seuraava</Button>
                            </div>
                        </Link>
                    </div>
                </div>
            </div>
        );
    }
}
export default InformationFilter;

Routes.js

import React from "react";
import {HashRouter, Route, Switch, Redirect, Link} from 'react-router-dom';
import Main from './components/main/main.component';
import SecondView from './components/secondview/secondview.component';
import ThirdView from './components/thirdview/thirdview.component';
import Traineeship from './components/traineeship/traineeships.component';
import InformationFilter from "./components/information/information-filter.component";
import InformationJob from "./components/information/information-job.component";

class AppRoutes extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            selectedIndustry: '',
            selectedJob: '',
            industries: [],
            jobs: [],
        }
    }

    render() {
        const InformationFilterPage = () => <InformationFilter rootState={this.state}
                                                               setRootState={this.setState.bind(this)}/>;

        const InformationJobPage = () => <InformationJob rootState={this.state}
                                                         setRootState={this.setState.bind(this)}/>;

        const TraineeshipPage = () => <Traineeship rootState={this.state}
                                                   setRootState={this.setState.bind(this)}/>;
        return (
            <HashRouter>
                <Switch>
                    <Route exact path='/' component={Main}/>
                    <Route path='/secondview' component={SecondView}/>
                    <Route path='/traineeships' component={TraineeshipPage}/>
                    <Route path='/information-filter' component={InformationFilterPage}/>
                    <Route path='/information-job' component={InformationJobPage}/>
                    <Redirect from='*' to='/'/>
                </Switch>
            </HashRouter>
        );
    }
}

export default AppRoutes;

Anyone knows what's happening here? I am keeping the state into this component due the cross passing values, you may say why dont you use redux but the thing is I dont want to use for this project as it is quite small project!

Ihor Lavs
  • 2,315
  • 2
  • 15
  • 26
Mizlul
  • 1
  • 7
  • 41
  • 100

1 Answers1

4

When you call setState on the Parent component by setRootState it sets the state of the parent which intern causes a rerender of its children which causes the children to re-mount that calls componentDidMount which then again sets the state on the parent and the cycle continues.

To resolve this you chould call your your api and set the state in your parent component itself.

your Parent component should now have

componentDidMount() {
  this.industryApiCall ()
}

industryApiCall = () => {
  industryApi.getAll().then(response => {
    if (response.data) {
      this.props.setState({
        industries: response.data._embedded.industries
      })
    } else {
      console.log(response)
    }
  })
}

and you can pass this function to your children like

const InformationFilterPage = () => <InformationFilter rootState={this.state}
                               callApi={this.industryApiCall.bind(this)} />;

and use it inside your child comnponent by calling

this.props.callApi()

supra28
  • 1,646
  • 10
  • 17
  • i understand this, how do I fix? – Mizlul May 25 '18 at 11:43
  • yes that solves the problem, but is there a way that I can make http request from child component and update parents state? – Mizlul May 25 '18 at 11:50
  • I am asking is there a way that I can update the state of parent from a child component without causing infinite loop? the thing is I dont want to make http calls from parent, I want child components to handle http requests and update the state of parent! the way you described parent is handling http request just you are calling them from child component! – Mizlul May 25 '18 at 12:04
  • That is not what you really asked and no there is no way for it to happen EXACTLY like you ask because it will re render the children. You cant setState of the parent component inside componentDidMount , that will always cause infinite loops. Although you can call the api from the child component when any change occurs and set the state or the parent. Can you explain the problem you are facing if calling the api from the parent and passing the data downwards – supra28 May 25 '18 at 12:15
  • from where do I call this.props.callApi() in child component? since you are making industryApiCall function available to be called from the child, how do u call this from child? – Mizlul May 25 '18 at 12:25
  • you can call it from any function that you call after any interaction that occurs like onChange={this.props.callApi} or you cann call it from another function like onIndustryChangeOption=()=>{ this.props.callApi()} – supra28 May 25 '18 at 12:31
  • but I want it to call when the component loads, so initially there should be industries before you do any onChange or smth like that! – Mizlul May 25 '18 at 12:35
  • What I mean is, I dont want to make any call from parent component within componentDidMount() , otherwise I would end up making all calls on parent component which is not what I want, let say I have three child components that comes into view one by one that each requires a http call, should I make these calls from parent component all at once, otherwise how?? – Mizlul May 25 '18 at 12:37