带钩子的React Function组件计数器

时间:2019-08-05 15:44:32

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

我试图了解新的React钩子及其用例。

我的目标是单个组件向上计数,每个x滴答声也计数另一个计数器。

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

我认为解决方案将是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中运行),这将导致在每个刻度上重新渲染,并且不会在每次调用时或在其他情况触发重新渲染时叠加。 / p>

1 个答案:

答案 0 :(得分:0)

您可以修复#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中进行清理之前/之后,由于当前的控制台日志似乎按预期工作,我无法弄清您的意思。

Edit so.answer.57361956