react 如何更好地处理下面的代码模式?

async function like(cleanups: (() => void)[] ) {
  const a = await delay(2000) // 等待一些异步操作
  if(a) {
    const clearId = setInterval(play, 2000);
    cleanups.push(() => {
      clearInterval(clearId);
    });
  }
}
useEffect(() => {
      const cleanups: (() => void)[] = [];
      like(cleanups);
      function clean() {
        cleanups.forEach((cleanup) => {
          cleanup();
        });
      }
      return clean
  }, [status, interval]); // 删除了部分 status, interval 的依赖逻辑

以上的代码如果在 StrictMode 模式下运行就会发现有一个定时器没有被移除,这也表明其实以上的代码是有问题的。如果依赖项快速变化,那么会有越来越多的定时器没有被移除,原因是 cleanups 的收集是异步的,在没有收集到定时器前,可能 clean 就执行了,导致了之后收集的定时器并没有被移除。如何在保持一样的功能下修复上面的问题?

阅读 760
2 个回答
✓ 已被采纳

可以使用一个 useRef 来存储当前的定时器 ID,并在 useEffect 清理函数中立即清理它。
以下是修复后的代码:

import { useEffect, useRef } from 'react';

async function like(setupCleanup: (cleanup: () => void) => void) {
  const a = await delay(2000); // 等待一些异步操作
  if (a) {
    const clearId = setInterval(play, 2000);
    setupCleanup(() => {
      clearInterval(clearId);
    });
  }
}

useEffect(() => {
  const cleanupRef = useRef<(() => void) | null>(null);

  const setupCleanup = (cleanup: () => void) => {
    if (cleanupRef.current) {
      cleanupRef.current(); // 清理之前的定时器
    }
    cleanupRef.current = cleanup;
  };

  like(setupCleanup);

  return () => {
    if (cleanupRef.current) {
      cleanupRef.current(); // 组件卸载时清理定时器
    }
  };
}, [status, interval]); // 确保 status 和 interval 是定义好的依赖

使用 useRef 来存储当前的清理函数,并在每次 useEffect 执行时立即清理之前的定时器。这确保了即使在依赖项快速变化的情况下,定时器也能被正确清理。这样可以避免定时器泄漏的问题。

这样可以吗:

async function like(cleanups: (() => void)[]) {
  let a = false;
  const clearId = setInterval(() => a && play(), 2000);
  cleanups.push(() => clearInterval(clearId));

  a = await delay(2000);
}
推荐问题
宣传栏