多个 setTimeout 回调在最后一次超时到期之前不会运行 React

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

不确定这是否是 React 问题。我有一个处于状态的对象数组,并将每个对象映射到一个组件。在这个组件内部,我有一个 useEffect 设置超时,当超时完成时,对象将从状态中删除,导致组件被卸载。我遇到的问题是,setTimeout 回调不会运行,直到所有当前的 setTimeouts 都已过期,然后它们同时运行。我通过向 setTimeout 回调添加 console.log 来确认这不是状态批处理,甚至在最后一个 setTimeout 完成之前也不会运行。

这是我映射组件的地方。 removeToast 是调用的函数,用于从状态数组中删除对象。

        <div className="toastsContainer">
          {toasts.map((toast, i) => (
            <Toast key={i} toast={toast} removeToast={removeToast} />
          ))}
        </div>

删除Toast功能:

  const removeToast = (toast: IToast) => {
    setToasts((prev) => prev.filter(({ id }) => id !== toast.id));
  };

这是带有 setTimeout 的 Toast 组件

import { useEffect } from "react";
import IToast from "../interfaces/toast";
import "../styles/toast.css";
import { useSpring, animated } from "react-spring";

const TOAST_DURATION = 3000;
interface ToastProps {
  toast: IToast;
  removeToast: (toast: IToast) => void;
}

export function Toast({ toast, removeToast }: ToastProps) {
  const { width } = useSpring({
    from: { width: "100%" },
    to: { width: "0%" },
    config: { duration: TOAST_DURATION },
  });

  useEffect(() => {
    const timerId = setTimeout(() => {
      removeToast(toast);
    }, TOAST_DURATION);

    return () => clearTimeout(timerId);
  }, [toast, removeToast]);

  return (
    <div className="toast">
      <div className="message"> {toast.message}</div>
      <animated.div
        className="duration"
        style={{
          width,
        }}
      />
    </div>
  );
}

尝试在父组件中使用 useEffect 并映射对象并在那里设置 setTimeout 。这只会导致每次添加对象时都会将多个 setTimeouts 应用于每个对象。

还尝试直接比较对象,而不是使用 ID,认为这可能是我的过滤功能。这并没有改变任何行为。

reactjs settimeout
1个回答
0
投票

“设置状态函数”是异步的。当您调用该函数时,它所做的是将调用放入“异步队列”中,以便“在将来的某个时刻”执行。当该函数运行时,它将对拥有状态变量的组件进行排队以重新渲染。

React 在批处理一堆“设置状态函数”调用并在实际重新渲染拥有它们的组件之前运行大部分/全部调用方面相当不错。

由于看起来所有组件基本上都在同一超时(+/-几毫秒)下批量运行,因此您可能看到的是,直到所有子组件都渲染完毕后,父组件才会呈现。组件对

removeToast()
的调用(每个组件都调用
setToasts()
)完成其运行。

您可以尝试的一个实验:将索引值从

.map()
(即您的变量
i
)发送给每个孩子,并让他们将 Timeout 设置为
TOAST_DURATION * i
。然后看看应用程序的行为如何。

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