欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

React中几种定时器实现方案的梳理与归纳

最编程 2024-07-25 11:05:57
...

方法1:将定时器回调用到的状态写到useEffect的依赖项中,这样才能获取最新的值

问题:但是会重复执行useEffect,导致定时器被频繁重置,此外,useEffect的其他逻辑也会被同时执行

useEffect(() => {
  const timer = window.setInterval(() => {
    setCount((prevCount) => {
      return prevCount + 1;
    });
  }, 2000);
  return () => {
    clearInterval(timer);
  };
}, [count]);

方法2:setState时接受一个函数返回最新的state,函数的参数就是最新的状态值(?)

问题:只是在setState回调函数内部可以获取最新的state,但是函数外依然是onmount时的值。 例如if (count === 0) { clearInterval(timer) return; }是无法实现的。

useEffect(() => {
  const timer = window.setInterval(() => {
    setCount((prevCount) => {
      return prevCount + 1;
    });
  }, 2000);
  return () => {
    clearInterval(timer);
  };
}, []);

方法3:使用useReducer,把 state 更新逻辑移到 effect 之外,对于我们这种简单的逻辑,书写大量的模板代码有点杀鸡用牛刀了

const initState: TimerState = { count: 0 };
const [state, dispatch] = useReducer(reducer, initState);

function reducer(prevState: TimerState, action: TimerAction): TimerState {
  const { count } = prevState;
  console.log('prevState', prevState);
  console.log('action', action);
  const nextState: TimerState = { count: 0 };
  switch (action.type) {
    case 'add':
      nextState.count = count + 1;
      break;
    default:
      nextState.count = 66;
  }
  console.log('nextState', nextState);
  return nextState;
}

useEffect(() => {
  const timer = setInterval(() => {
    dispatch({ type: 'add' });
  }, 2000);
  
  return () => {
    clearInterval(timer);
  };
}, []);

方法4:使用useRef来存count的值,在useEffect中使用ref.current,不用state的count

const [count, setCount] = useState(10);
const latestCount = useRef(count); // 定义一个ref,初始值是10

useEffect(() => {
  latestCount.current = count;
}, [count]);

useEffect(() => {
  const timer = setInterval(() => {
    console.log(`定时器count=${latestCount.current}`);
    if (latestCount.current === 0) {
      clearInterval(timer);
      return;
    }
    setCount(c => c - 1);
  }, 1000);
}, []);

方法5:自定义hook,比之前的实现更加优雅(TODO:清除定时器的实现)

// 定义
export function useInterval(callback: any, timeout = 1000) {
  const latestCallback = useRef(() => {
  });

  useEffect(() => {
    latestCallback.current = callback;
  });

  useEffect(() => {
    const timer = setInterval(() => latestCallback.current(), timeout);
    return () => clearInterval(timer);
  }, []);
}

// 使用
useInterval(() => {
  setCount(count + 1);
  console.log(`count=${count}`);
  console.log('定时器返回timer', timer);
  if (count > 5) clearInterval(timer);
}, 1500);

推荐阅读