簡體   English   中英

setState 即使在渲染函數之外也會導致無限循環

[英]setState causes infinite loop even though it is outside render function

我目前正在使用 openweatherapp API 開發一個簡單的天氣應用程序。 該應用程序旨在從兩個端點獲取數據:一個返回您所在城市的當前天氣,另一個返回未來 5 天的天氣預報。 該應用程序還應在 60 秒后觸發重新獲取數據的事件。 這就是我嘗試構建我的解決方案的方式:

在 App.js 中,我正在獲取數據,然后將其作為道具傳遞給其他兩個組件,一個處理當前天氣,另一個處理天氣預報。 在 CurrentWeatherForecast 組件中,我還啟動了使用鈎子每秒更新狀態的函數。 當計時器達到 60 秒時,我正在調用我作為 App.js 中的道具傳遞的“handleRefresh”函數。 (在 App.js 中是實際更新發生的地方)。 “handleRefresh”函數在 App.js 的 render 方法之外,它更新一個“step”變量,然后應該導致組件重新渲染並重新獲取數據。 問題是,在調用 setState 時,該函數會導致無限循環,我不明白為什么,因為該函數在 render 方法之外。 我將在下面發布我的代碼。

import React, { Component } from "react";
import { CurrentWeatherForecast } from "./components/CurrentWeatherForecast";
import { NextDaysWeatherForecast } from "./components/NextDaysWeatherForecast";

export class App extends Component {
constructor(props) {
    super(props);
    this.state = {
        currentWeather: [],
        nextDaysWeather: [],
        step: 0,
    };
}
componentDidMount() {
    const { step } = this.state;
    var currentWeather;
    var nextDaysWeather; // step is used to indicate wether I want to fetch data or not
    if (step === 0) {
        fetch(
            "https://api.openweathermap.org/data/2.5/weather?q=London&appid=1fc71092a81b329e8ce0e1ae88ef0fb7"
        )
            .then((response) => {
                const contentType = response.headers.get("content-type");
                if (
                    !contentType ||
                    !contentType.includes("application/json")
                ) {
                    throw new TypeError("No JSON data!");
                }
                return response.json();
            })
            .then((data) => {
                currentWeather = data;
            })
            .catch((error) => console.error(error));
        fetch(
            "https://api.openweathermap.org/data/2.5/forecast?q=London&appid=1fc71092a81b329e8ce0e1ae88ef0fb7"
        )
            .then((response) => {
                const contentType = response.headers.get("content-type");
                if (
                    !contentType ||
                    !contentType.includes("application/json")
                ) {
                    throw new TypeError("No JSON data!");
                }
                return response.json();
            })
            .then((data) => {
                let requiredData = data.list.slice(0, 5);
                nextDaysWeather = requiredData;
            })
            .catch((error) => console.error(error));
        let f = setTimeout(() => {
            this.setState({
                currentWeather: currentWeather,
                nextDaysWeather: nextDaysWeather,
                step: 1, // updating step to 1 after fetching the data
            });
        }, 1000);
    }
}

handleRefresh = () => {
    const { step } = this.state;
    console.log(step);
    this.setState({ step: 0 }); // updating the step to 0 this causes the infinite loop
};

render() {
    const { currentWeather, nextDaysWeather } = this.state;
    return (
        <div>
            <CurrentWeatherForecast
                currentWeather={currentWeather}
                handleRefresh={this.handleRefresh}
            />
            <NextDaysWeatherForecast nextDaysWeather={nextDaysWeather} />
        </div>
    );
}
}

export default App;

這是在 App.js 中忽略 NextDaysWeatherForecast 組件,因為它現在是空的

import React, { useEffect, useState } from "react";

export const CurrentWeatherForecast = (props) => {
const { currentWeather } = props;
const [progressValue, setValue] = useState(0);

useEffect(() => {
    const interval = setInterval(() => {
        setValue((progressValue) =>
            progressValue < 61 ? progressValue + 1 : (progressValue = 0)
        );
    }, 1000);
    return () => clearInterval(interval);
}, []);
if (progressValue === 60) {
    props.handleRefresh(); // calling the handleRefresh function passed from App.js
}

return (
    <div>
        <label htmlFor="file">Downloading progress:</label>
        <progress id="file" value={progressValue} max="60">
            {progressValue}%
        </progress>
    </div>
);
};

這是 NextWeatherForecast 組件,我在其中啟動計時器,然后調用我作為道具傳遞的“handleRefresh”函數。

提前謝謝你們!

看看這個 effect-phase 和 render-phase 代碼,並嘗試猜測哪里出了問題。

useEffect(() => {
    const interval = setInterval(() => {
        setValue((progressValue) =>
            progressValue < 61 ? progressValue + 1 : (progressValue = 0)
        );
    }, 1000);
    return () => clearInterval(interval);
}, []);
if (progressValue === 60) {
    props.handleRefresh(); // calling the handleRefresh function passed from App.js
}

特別是這個聞起來像溢出:在渲染階段調用的導致重新渲染的函數(我們知道handleRefresh會導致重新渲染。

if (progressValue === 60) {
    props.handleRefresh(); // calling the handleRefresh function passed from App.js
}

現在,讓我們尋找一些應該停止溢出的東西(也就是說,它嘗試將progressValue設置為 60 以外的其他值,一旦它是 60)。

這里是:

progressValue < 61 ? progressValue + 1 : (progressValue = 0)

除了,這僅每 1000 毫秒觸發一次。 這意味着您的組件在一整秒內陷入了重新渲染循環。 一旦設置為60 ,React 開始瘋狂渲染,並在很短的時間內超過渲染限制,而progressValue距離設置為 0 仍有很多很多毫秒。


一個示例解決方案是在另一個效果中檢查progressValue === 60

export const CurrentWeatherForecast = (props) => {
    const { currentWeather } = props;
    const [progressValue, setValue] = useState(0);

    useEffect(() => {
        const interval = setInterval(() => {
            setValue(prevProgressValue => prevProgressValue === 60 ? 0 : prevProgressValue + 1);
        }, 1000);
        return () => clearInterval(interval);
    }, []);

    useEffect(() => progressValue === 60 && props.handleRefresh(), [progressValue]);

    return (
        <div>
            <label htmlFor="file">Downloading progress:</label>
            <progress id="file" value={progressValue} max="60">
                {progressValue}%
        </progress>
        </div>
    );
};

嘗試這個:

import React, { useEffect, useState } from "react";

export const CurrentWeatherForecast = ({ currentWeather }) => {

useEffect(() => {
    const interval = setInterval(() => {
         props.handleRefresh();
    }, 60000);
    return () => clearInterval(interval);
}, []);
   
return (
    <div>
        your codes goes here...
    </div>
);
};

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM