实时足球板与时间

问题描述 投票:0回答:2

我正在尝试调用示例 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;
javascript html css reactjs timer
2个回答
0
投票

只要去掉 if 语句来检查间隔是否已初始化并存储在 ref 中(

timeInterval.current
),那么它就可以工作了。

原因是在 React 的严格模式下,组件会被挂载 -> 卸载 -> 重新挂载,以识别潜在的不安全生命周期。这只会在开发模式下发生。

在本例中,

timeInterval.current
在使用
setInterval
初始化后被清理,因此间隔在初始渲染后停止工作。

您仍然应该保留清理逻辑作为一般使用

useEffect
的最佳实践,并且还要摆脱 --

if (!timeInterval.current) {

为了让间隔正常工作,特别是

useEffect
的依赖是一个空块,确保它不会以某种方式意外被覆盖。


0
投票

问题

这里的问题是你直接改变了 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
    一次来“播种”时间。
  • 删除计时器为空的条件检查,它就是这样开始的。
  • 更新渲染的 UI 以渲染
    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;

Edit react-live-soccer-board-with-time

© www.soinside.com 2019 - 2024. All rights reserved.