我目前正在使用openweatherapp API开发一个简单的气象应用程序。该应用程序旨在从两个端点获取数据:一个端点返回您所在城市的当前天气,另一个端点返回未来5天的天气预报。该应用程序还应该在60秒后触发一个事件,以重新获取数据。这就是我尝试构建解决方案的方式:
在App.js中,我正在获取数据,然后将其作为道具传递给另外两个组件,一个组件处理当前天气,另一个组件用于天气预报。在CurrentWeatherForecast组件中,我还启动了使用钩子每秒更新状态的函数。当计时器达到60秒时,我正在调用“ handleRefresh”函数,该函数已作为App.js的支持传递给了我。 (在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”函数。
先谢谢大家!
答案 0 :(得分:2)
看看这个效果阶段和渲染阶段代码,并尝试猜测出什么问题。
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)
除了,这仅每1000ms触发一次。这意味着一秒钟您的组件将陷入重新渲染循环中。一旦将其设置为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>
);
};
答案 1 :(得分:2)
尝试一下:
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>
);
};