简体   繁体   中英

Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate

There's already some people asking this question but they are almost caused by the same reason (fire the callback in situ eg <div onClick={this.handleClick()}></div> ). However this doesn't happen to me but I got the same error.

PS Some util functions are defined behind the scene.

// Parent
export default class SearchArea extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            data: {
                update: '',
                confirmed: '',
                recovered: '',
                deaths: ''
            },
            showDashboard: false
        }
        this.setCountryData = this.setCountryData.bind(this);
        this.showDashboard = this.showDashboard.bind(this);
    }

    setCountryData(data) {
        console.log(data);
        this.setState({
            data: {
                update: data.lastUpdate,
                confirmed: data.confirmed,
                recovered: data.recovered,
                deaths: data.deaths
            }
        });
    }

    showDashboard(toggle) {
        this.setState({ showDashboard: toggle })
    }

    render() {
        return (
            <div>
                <Dropdown onSetCountryData={this.setCountryData} onShowDashboard={this.showDashboard} />
                <Dashboard data={this.state.data} visible={this.state.showDashboard} />
            </div>
        )
    }
}
// Sibling1
class Dropdown extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            countries: [],
            currentCountry: ''
        };
        this.handleClick = this.handleClick.bind(this);
        this.handleChange = this.handleChange.bind(this);
    }

    handleClick() {
        const showCountryData = Fetch(newFetchCountry).showJSONData;
        showCountryData().then(res => {
            const data = res[0];
            this.passCountryData(data);
        })

        this.passToggleDashboard(true);
    }

    handleChange(e) {
        this.setState({ currentCountry: e.target.value, });
        this.passToggleDashboard(false);
    }

    passCountryData(data) {
        this.props.onSetCountryData(data);
    }

    passToggleDashboard(toggle) {
        this.props.onShowDashboard(toggle);
    }

    componentDidMount() {
        let timer = setTimeout(() => {
            const showCountryList = Fetch(fetchCountryList).showJSONData;
            showCountryList().then(res => {
                const data = res.map(country => country);
                this.setState({ countries: data })
            });
            clearTimeout(timer);
        }, 2000)
    }

    render() {
        return (
            <section className="dropdown-area">
                <div className="dropdown">
                    <label htmlFor="country">Select country:</label>
                    <input list="countries" name="country" id="country" onChange={this.handleChange} />
                    <datalist id="countries" required>
                        {this.state.countries.length ?
                            this.state.countries.map(country => <option key={country.name}>{country.name}</option>) :
                            <option disabled>Loading</option>}
                    </datalist>
                </div>
                <button className="comp" onClick={this.handleClick}>Search</button>

            </section>
        )
    }
}
// Sibling2
class Dashboard extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            data: {
                update: '',
                confirmed: '',
                recovered: '',
                deaths: ''
            }
        };
    }

    componentDidMount() {
        if (!('data' in this.props)) {
            const showWorldData = Fetch(fetchWorld).showJSONData;
            showWorldData().then(res => {
                const data = res[0];
                this.setState({
                    data: {
                        update: data.lastUpdate,
                        confirmed: data.confirmed,
                        recovered: data.recovered,
                        deaths: data.deaths
                    }
                });
            });
         }
    }

    componentDidUpdate() { // Error here!!!
        if ('data' in this.props) {
            const data = this.props.data;
            this.setState({
                data: {
                    update: data.lastUpdate,
                    confirmed: data.confirmed,
                    recovered: data.recovered,
                    deaths: data.deaths
                }
            });
        }
    }

    render() {
        const visibility = {
            visibility: 'visible' in this.props && !this.props.visible ? 'hidden' : 'visible'
        };
        return (
            <section className="dashboard-area" style={visibility}>
                <span>Last Update: {this.state.data.update || 'Loading...'}</span>
                <div className="dashboards">
                    <DashboardItem num={this.state.data.confirmed} type="Confirmed" />
                    <DashboardItem num={this.state.data.recovered} type="Recovered" />
                    <DashboardItem num={this.state.data.deaths} type="Deaths" />
                </div>
            </section>
        )
    }
}

class DashboardItem extends React.Component {
    render() {
        return (
            <div className="dashboard">{this.props.type}: <br /><span>{this.props.num || 'Loading...'}</span></div>
        )
    }
}

Error is in the componentDidMount() in the Dashboard component. I can't find where I fired the re-render infinitely.

The setState method is repeatedly updating the component because every time the 'data' in this.props equals to true you're calling setState and calling setState will by default update the component and componentDidUpdate will check again if 'data' in this.props equals to true and so

You should make strict conditions for if statement

try this

componentDidUpdate(prevProps, prevState) { 
    if ('data' in this.props && this.props.data !== prevProps.data) {
        const data = this.props.data;
        this.setState({
            data: {
                update: data.lastUpdate,
                confirmed: data.confirmed,
                recovered: data.recovered,
                deaths: data.deaths
            }
        });
    }
}

Your issue stems from derived state: state which is made dependent on props and is an anti-pattern in react.

This will tell you more about it:

https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html

There are some work arounds, but its recommended you instead restructure your data flow.

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