UseEffect 中的 setInterval 获得调用 2/4 次

setInterval in UseEffect getting call 2/4 times

嗨,我只想点击一次 useEffect,但它被调用了 2-3 次,我尝试将函数包装在另一个函数中,但代码开始中断,你们能帮忙吗

需要调用 successHandler,但是它被调用了 n 次

  const { successHandler } = props; 
  const [completed, setCompleted] = React.useState(10); 
  const fileType = getFileType(props.fileName); 

  React.useEffect(() => { 
         function progress() { 
            setCompleted(prevCompleted => { 
               if (prevCompleted >= 100) { 
                   successHandler(); 
                   return 100; 
               } 
              return prevCompleted + 10; }); 
         } 
         const timer = setInterval(progress, 500); 
 
         return () => { clearInterval(timer); }; 
      }, [successHandler]); 

如果useEffect被多次调用,则意味着其依赖项中的某些内容发生了变化。在你的情况下 successHandler 发生了变化。查看它的来源,也许您需要使用 useCallback 来确保它不会在 re-render.

上发生变化

例如,这将在每个 re-render 上 re-run useEffect,因为 f 每次都是一个新函数:

const f = () => {};
useEffect(() => { f(); }, [f]);

虽然这只会 运行 useEffect 一次,因为 useCallback 记忆函数并且 f 不会在每个 re-render 上改变:

const f = useCallback(() => {}, []);
useEffect(() => { f(); }, [f]);

稍后编辑 - 我误解了这个问题。尝试像这样重写它:

const { successHandler } = props;
const [completed, setCompleted] = React.useState(10);
const fileType = getFileType(props.fileName);

React.useEffect(() => {
  function progress() {
    setCompleted(prevCompleted => Math.max(100, prevCompleted + 10));
  }
  const timer = setInterval(progress, 500);
  return () => {
    clearInterval(timer);
  };
}, []);

React.useEffect(() => {
  if (completed === 100) {
    successHandler();
  }
}, [completed, successHandler]);

这种方式 successHandler 应该只在 completed 变成 100 时调用一次(假设 successHandler 没有改变)。

你可能也想在进度为 100 时停止计时,那么你可以这样做:

const { successHandler } = props;
const [completed, setCompleted] = React.useState(10);
const fileType = getFileType(props.fileName);
const timer = React.useRef(null);

const stopTimer = React.useCallback(() => {
  if (timer.current) {
    clearInterval(timer.current);
    timer.current = null;
  }
}, []);

React.useEffect(() => {
  function progress() {
    setCompleted(prevCompleted => Math.max(100, prevCompleted + 10));
  }
  timer.current = setInterval(progress, 500);
  return stopTimer;
}, [stopTimer]);

React.useEffect(() => {
  if (completed === 100) {
    stopTimer();
    successHandler();
  }
}, [completed, stopTimer, successHandler]);