我正在尝试调用示例 API 并构建实时足球比分,但我的时间不是在没有刷新的情况下更新实时比分。如何在不刷新的情况下更新时间?
提供下面的 stackblitz 和代码。我查看了控制台,没有错误。我可以调用示例 API。
https://stackblitz.com/edit/react-9ypawr?file=src%2FApp.js,src%2Fstyle.css
import React, { useState, useEffect, useRef } from 'react';
const Scoreboard = () => {
const [homeScore, setHomeScore] = useState(0);
const [awayScore, setAwayScore] = useState(0);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(
'https://jsonplaceholder.typicode.com/posts'
);
const data = await response.json();
setHomeScore(data[0].id);
setAwayScore(data[1].id);
} catch (error) {
console.error(error);
}
};
fetchData();
}, []);
const timeInterval = useRef(null);
useEffect(() => {
if (!timeInterval.current) {
timeInterval.current = setInterval(() => {
updateTime(); // Call the updateTime function every second
}, 1000);
}
return () => {
clearInterval(timeInterval.current); // Clear the interval when the component unmounts
};
}, []);
const updateTime = () => {
const currentTime = new Date().toLocaleTimeString();
const hours = new Date().getHours();
const minutes = new Date().getMinutes();
const seconds = new Date().getSeconds();
const formattedTime = `${hours}:${minutes
.toString()
.padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
document.getElementById(
'home-time'
).textContent = `Last updated: ${formattedTime}`;
document.getElementById(
'away-time'
).textContent = `Last updated: ${formattedTime}`;
};
return (
<div className="scoreboard">
<div className="team">
<img
src="https://upload.wikimedia.org/wikipedia/en/thumb/f/f8/Barcelona_logo.svg/220px-Barcelona_logo.svg.png"
alt="Barcelona logo"
/>
<h1>Barcelona</h1>
<h2>{homeScore}</h2>
<p id="home-time">Last updated: 00:00:00</p>
</div>
<div className="team">
<img
src="https://upload.wikimedia.org/wikipedia/en/thumb/a/a2/Real_Madrid_CF.svg/220px-Real_Madrid_CF.svg.png"
alt="Real Madrid logo"
/>
<h1>Real Madrid</h1>
<h2>{awayScore}</h2>
<p id="away-time">Last updated: 00:00:00</p>
</div>
</div>
);
};
export default Scoreboard;
只要去掉 if 语句来检查间隔是否已初始化并存储在 ref 中(
timeInterval.current
),那么它就可以工作了。
原因是在 React 的严格模式下,组件会被挂载 -> 卸载 -> 重新挂载,以识别潜在的不安全生命周期。这只会在开发模式下发生。
在本例中,
timeInterval.current
在使用setInterval
初始化后被清理,因此间隔在初始渲染后停止工作。
您仍然应该保留清理逻辑作为一般使用
useEffect
的最佳实践,并且还要摆脱 --
if (!timeInterval.current) {
为了让间隔正常工作,特别是
useEffect
的依赖是一个空块,确保它不会以某种方式意外被覆盖。
这里的问题是你直接改变了 DOM。这是一个巨大的 React 反模式。
const updateTime = () => {
const currentTime = new Date().toLocaleTimeString();
const hours = new Date().getHours();
const minutes = new Date().getMinutes();
const seconds = new Date().getSeconds();
const formattedTime = `${hours}:${minutes
.toString()
.padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
document.getElementById(
'home-time'
).textContent = `Last updated: ${formattedTime}`; // <-- DOM mutation!
document.getElementById(
'away-time'
).textContent = `Last updated: ${formattedTime}`; // <-- DOM mutation!
};
React 不一定知道重新渲染页面,因此 UI 不同步。
我建议添加一个
updatedTime
状态并重写代码以更新状态并触发组件重新渲染,以便渲染的DOM保持“同步”。请记住,在 React 中,渲染的 UI,例如渲染到 DOM 的内容是 state 和 props 的函数。
示例:
updatedTime
状态updateTime
函数移至 useEffect
以将其作为外部依赖项删除。updateTime
一次来“播种”时间。updatedTime
状态。import React, { useState, useEffect, useRef } from 'react';
const Scoreboard = () => {
const [homeScore, setHomeScore] = useState(0);
const [awayScore, setAwayScore] = useState(0);
const [updatedTime, setUpdatedTime] = useState("00:00:00");
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(
'https://jsonplaceholder.typicode.com/posts'
);
const data = await response.json();
setHomeScore(data[0].id);
setAwayScore(data[1].id);
} catch (error) {
console.error(error);
}
};
fetchData();
}, []);
const timeInterval = useRef(null);
useEffect(() => {
const updateTime = () => {
const hours = new Date().getHours();
const minutes = new Date().getMinutes();
const seconds = new Date().getSeconds();
const formattedTime = `${hours}:${minutes
.toString()
.padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
setUpdatedTime(formattedTime);
};
// Call once initially to "seed" state value
updateTime();
// Call the updateTime function every second
timeInterval.current = setInterval(updateTime , 1000);
return () => {
// Clear the interval when the component unmounts
clearInterval(timeInterval.current);
};
}, []);
return (
<div className="scoreboard">
<div className="team">
<img
src="https://upload.wikimedia.org/wikipedia/en/thumb/f/f8/Barcelona_logo.svg/220px-Barcelona_logo.svg.png"
alt="Barcelona logo"
/>
<h1>Barcelona</h1>
<h2>{homeScore}</h2>
<p id="home-time">Last updated: {updatedTime}</p>
</div>
<div className="team">
<img
src="https://upload.wikimedia.org/wikipedia/en/thumb/a/a2/Real_Madrid_CF.svg/220px-Real_Madrid_CF.svg.png"
alt="Real Madrid logo"
/>
<h1>Real Madrid</h1>
<h2>{awayScore}</h2>
<p id="away-time">Last updated: {updatedTime}</p>
</div>
</div>
);
};
export default Scoreboard;