【问题标题】:React Function Component counter with hooks带有钩子的 React 函数组件计数器
【发布时间】:2019-12-13 04:13:03
【问题描述】:

我正在尝试了解新的 React 钩子及其用例。

我的目标是增加一个计数的单个组件,并且每个 x 滴答计数另一个计数器。

我使用 useEffect 和 useState 实现了它,主要有两个问题:
1. 在调用超时之前卸载组件时的内存泄漏(使用 react-router 导航时)
2. 由于useEffect 和useState 都触发了渲染,因此组件在每个tick 上渲染两次。

我认为解决方案将是 useRef 或 useMemo 但我还没有弄清楚。

我当前的组件(带有打字稿):

import React from "react";

const Component: React.FC = () => {
  const [trigger, setTrigger] = React.useState(0);
  const [timer, setTimer] = React.useState({ cycle: 0, count: 0 });

  let refTimer = React.useRef({ cycle: 0, count: 0 });

  // useRef
  // React.useEffect(() => {
  //   setInterval(() => {
  //     console.log("tick");
  //     if (refTimer.current.count % 2 === 0) {
  //       refTimer.current.cycle++;
  //       setTimer(refTimer.current);
  //     }
  //     refTimer.current.count++;
  //     setTimer(refTimer.current);
  //     // console.log(timer);
  //   }, 1000);
  // }, []);

  // useState
  React.useEffect(() => {
    console.log("effect tick");
    setTimeout(() => {
      console.log("tick");
      const count = timer.count + 1;
      if (count % 2 === 0) {
        const cycle = timer.cycle + 1;
        setTimer({ ...timer, count, cycle });
        return;
      }
      setTimer({ ...timer, count });
    }, 1000);
  }, [timer]);
  return (
    <div>
      <br />
      <br />
      <br />
      <br /> Playground:
      <div>Count: {timer.count}</div>
      <div>Cycle: {timer.cycle}</div>
      <button type="button" onClick={(): void => setTrigger(trigger + 1)}>
        Trigger Count: {trigger}
      </button>
    </div>
  );
};

export default Component;

正如我所说,像这样我有提到的两个问题。我可以完全移除修复双重渲染的 useEffect,但是当我单击触发按钮时,刻度会堆积起来,这比双重渲染更糟糕。

注释的 useRef 部分是我尝试过的,但不知何故不起作用。

感谢所有帮助!

编辑: 第三个小问题是,像这样,计数器只在 setTimeout 下运行,这将触发另一个 setTimeout,因此如果该过程需要一些时间,它实际上并不是一个精确的时间间隔。

所以我的目标是一个在单独进程中运行的间隔(我会说在 useEffect 中),这将导致每次滴答时重新渲染,并且不会在每次调用或其他触发重新渲染时叠加。

【问题讨论】:

  • 对于清理,useEffect 应该返回一个删除计时器的函数(将在 unMount 上调用)。为此,您应该使用 useInterval 并将其保存在本地,这会得到清理:return () => clearTimeout(t),其中 t 是您的间隔/超时

标签: reactjs memory-leaks settimeout setinterval react-hooks


【解决方案1】:

您可以修复 #1 中提到的内存泄漏。

React.useEffect(() => {
  console.log("effect tick", timer);

  // .. ? get the timeout ID to clear on unmount
  const id = setTimeout(() => {
    console.log(`tick id=${id}`, timer);
    const count = timer.count + 1;
    if (count % 2 === 0) {
      const cycle = timer.cycle + 1;
      setTimer({ ...timer, count, cycle });
      return;
    }
    setTimer({ ...timer, count });
  }, 1000);

  // ... ? Clean up here with the ID on unmount
  return () => clearTimeout(id);
}, [timer]);

关于#2 双重渲染,您能说得更具体些吗? 在上面的useEffect 进行清理之前/之后,我无法理解您的意思,因为当前的控制台日志似乎按预期工作。

【讨论】:

  • 谢谢你的回答,我不太明白return是用useEffect做什么的。你能传递一个在卸载时运行的函数吗?有这方面的文件吗?关于#2,我认为我错了,我认为 useEffect 和 setState 都会触发渲染,但我认为只会发生一次,因为 useEffect 在每次渲染后运行,对吗?
猜你喜欢
  • 2022-10-12
  • 1970-01-01
  • 2019-08-22
  • 2021-02-01
  • 2020-01-27
  • 2020-04-22
  • 1970-01-01
  • 2023-03-19
  • 1970-01-01
相关资源
最近更新 更多