reactjs 从一个组件路由到另一个组件时,计时器的呈现停止

ergxz8rk  于 2023-01-30  发布在  React
关注(0)|答案(2)|浏览(158)

所以我做了一个简单的倒计时器,如果我路由到另一个组件,它会停止渲染更新的时间。我使用了一个每秒更新一次倒计时时间的setInerval(),每当我路由到另一个组件时,setInterval()会继续运行,但没有渲染发生,倒计时器会回到它的开始时间。
应用程序

import { Route, Routes } from "react-router-dom";
import Countdown from "./components/Countdown";
import Timer from "./components/Timer";

const App = () => {
  return (
    <>
      <Routes>
        <Route path="/" element={<Countdown />} />
        <Route path="timer" element={<Timer />} />
      </Routes>
    </>
  );
};

export default App;

倒计时

import { useState } from "react";
import { Link } from "react-router-dom";

const Countdown = () => {
  const [time, setTime] = useState(600);

  const startTimer = () => {
    const start = Date.now();
    const updateTime = () => {
      const updatedTime = time - (((Date.now() - start) / 1000) | 0);
      setTime(updatedTime);
      console.log("Set Interval running");
    };
    setInterval(updateTime, 1000);
  };

  const minutes = (time / 60) | 0;
  const seconds = time % 60 | 0;
  return (
    <>
      <h3>
        {minutes}:{seconds}
      </h3>
      <p>Start the timer first 👇</p>
      <button onClick={startTimer}>START</button>
      <p>After starting the timer go this component 👇</p>
      <Link to="timer">
        <button>Go to Timer</button>
      </Link>
    </>
  );
};

export default Countdown;

计时器

import { Link } from "react-router-dom";

const Timer = () => {
  return (
    <>
      <p>
        Now go back to the Countdown component, the timer is reset and the
        setInterval() is still running. How can I avoid this issue ?
      </p>
      <Link to="/">
        <button>Go back to Countdown</button>
      </Link>
    </>
  );
};

export default Timer;

下面是应用程序的链接-codesandbox.io
用我那点初学者知识,我尝试每秒在localStorage中存储更新的时间,然后每当组件重新呈现时,我尝试从localStorage中获取它并启动计时器,但它不工作,因为setInterval()中的任何内容在路由到另一个组件时都不工作。

5jdjgkvh

5jdjgkvh1#

时间的状态值只与活动组件有关。如果您跳到一个不同的组件上然后返回,则状态值将重置。
为了避免这种情况,您可以设置上下文API,以帮助组件之间的数据共享。
查看this article了解更多信息。

6jjcrrmo

6jjcrrmo2#

问题

您在此处看到的问题是由Countdown组件中的状态引起的。当您从"/"路由导航到"/timer"路由时,Countdown已卸载。间隔仍在运行的原因是,在Countdown卸载时,没有清除功能来清除间隔。

溶液

解决方案是将Lift State Up传递给公共祖先,并将time状态和startTimer回调作为 prop 向下传递给负责它们的子组件/后代组件。
由于您使用的是react-router-dom,因此一个简单的解决方案是呈现一个布局路由组件,该组件保存状态和设置器,并通过ReactContext提供它们。
示例:
定时器布局

import { useEffect, useRef, useState } from "react";
import { Outlet } from "react-router-dom";

const TimerLayout = () => {
  const [time, setTime] = useState(600);
  const timerRef = useRef();

  useEffect(() => {
    return () => {
      clearInterval(timerRef.current);
    };
  }, []);

  const startTimer = () => {
    console.log("Set Interval running");
    setTime(600);

    const tick = () => {
      setTime((time) => time - 1);
    };

    timerRef.current = setInterval(tick, 1000);
  };

  return <Outlet context={{ startTimer, time }} />;
};

export default TimerLayout;

应用程序

import { Route, Routes } from "react-router-dom";
import TimerLayout from "./components/TimerLayout";
import Countdown from "./components/Countdown";
import Timer from "./components/Timer";

const App = () => {
  return (
    <Routes>
      <Route element={<TimerLayout />}>
        <Route path="/" element={<Countdown />} />
        <Route path="timer" element={<Timer />} />
      </Route>
    </Routes>
  );
};

export default App;

使用者将使用useOutletContext挂钩访问所提供的上下文值。这里,剩余的minutesseconds可以从传递的time状态派生。
倒计时

import { Link, useOutletContext } from "react-router-dom";

const Countdown = () => {
  const { time, startTimer } = useOutletContext();

  const minutes = String(Math.floor(time / 60)).padStart(2, "0");
  const seconds = String(time % 60).padStart(2, "0");

  return (
    <>
      <h3>
        {minutes}:{seconds}
      </h3>
      <p>Start the timer first 👇</p>
      <button onClick={startTimer}>START</button>
      <p>After starting the timer go this component 👇</p>
      <Link to="timer">
        <button type="button">Go to Timer</button>
      </Link>
    </>
  );
};

export default Countdown;

相关问题