React之setInterval使用

前端 0 827 0
发表于: 2022-04-28 20:12:45

简介: 学了,但完全没有学

前言

学习以及使用一门技术这么长时间,却连它的官方文档都没看过几次,是可笑还是可悲。

取巧的实现

取巧的实现,而且有局限性。

import { memo, useEffect, useState } from 'react';

const Counter = () => {
  console.log('Counter组件渲染了');
  const [count, setCount] = useState(1);
  const [started, setStarted] = useState(false);
  const [timer, setTimer] = useState<any>();

  // useEffect没有传入依赖数组,说明每次Counter组件渲染都会执行
  // 利用这一点,就可以每次渲染时开始定时器,并且通过返回函数清楚定时器副作用实现
  // 缺点:因为是依赖于每次渲染,才开始定时器的,如果我一直手动点击加100的按钮,Counter组件就会不断渲染,
  // 期间里面的定时器就会不断的开始定时器,还没等到定时器执行完,就因为渲染问题立即清除了定时器(因为我是一直点击加100的按钮,一直渲染的)
  // 因此,只有等我不点击加100的按钮了,才会继续执行定时器。即这个定时器的逻辑是和我点击加100的行为冲突的,
  // 做不到我一边点击加100的按钮,这个定时器也做自己的逻辑。
  useEffect(() => {
    if (!started) return;
    const timer = setInterval(() => {
      console.log('此时的count', count); //这个count是最新的
      setCount(count + 1);
      setTimer(timer);
    }, 500);
    // setTimer(timer); //不能在这里设置,会死循环。
    return () => clearInterval(timer);
  });

  const clearTimer = () => {
    setStarted(false);
    clearInterval(timer);
  };

  return (
    <div>
      <div>count:{count}</div>
      <button onClick={() => setStarted(!started)}>
        {!started ? '开始' : '停止'}定时器
      </button>
      <button onClick={() => setCount(count + 100)}>加100</button>
      <button onClick={() => clearTimer()}>清楚定时器</button>
    </div>
  );
};

export default memo(Counter);

使用函数式更新实现

https://zh-hans.reactjs.org/docs/hooks-reference.html#functional-updates

import { memo, useEffect, useState } from 'react';

const Counter = () => {
  console.log('Counter组件渲染了');
  const [count, setCount] = useState(1);
  const [timer, setTimer] = useState<any>(null);
  const [started, setStarted] = useState(false);

  const handleTimer = () => {
    const newTimer = setInterval(() => {
      console.log('此时的count', count); //这个count引的是外层的count(闭包)他的值往往不是最新的。
      setCount((preVal) => {
        console.log('preVal', preVal); //这个preVal是上一次更新的值
        return preVal + 1; //返回值是更新后的值
      });
    }, 500);
    setTimer(newTimer);
  };

  useEffect(() => {
    if (!started) {
      clearInterval(timer);
      return;
    }
    handleTimer();
  }, [started]);

  const clearTimer = () => {
    clearInterval(timer);
    setStarted(false);
    setTimer(null);
  };

  return (
    <div>
      <div>count:{count}</div>
      <button onClick={() => setCount(count + 100)}>加100</button>
      <button onClick={() => clearTimer()}>清楚定时器</button>
      <button onClick={() => setStarted(!started)}>
        {!started ? '开始' : '停止'}定时器
      </button>
    </div>
  );
};

export default memo(Counter);

使用useRef实现一

import { memo, useEffect, useRef, useState } from 'react';

const Counter = () => {
  console.log('Counter组件渲染了');
  const [timer, setTimer] = useState<any>(null);
  const [started, setStarted] = useState(false);
  const countRef = useRef<any>(1);
  const [count, setCount] = useState(countRef.current);

  const handleTimer = () => {
    const newTimer = setInterval(() => {
      console.log('此时的count', count); //这个count引的是外层的count(闭包)他的值往往不是最新的。
      countRef.current = countRef.current + 1; //这个countRef.current会更新,但是不会响应到hooks,我们可以setCount(countRef.current)响应到count
      console.log(countRef.current);
      setCount(countRef.current);
    }, 500);
    setTimer(newTimer);
  };

  useEffect(() => {
    if (!started) {
      clearInterval(timer);
      return;
    }
    handleTimer();
  }, [started]);

  const clearTimer = () => {
    clearInterval(timer);
    setStarted(false);
    setTimer(null);
  };

  const add100 = () => {
    countRef.current = countRef.current + 100;
    setCount(countRef.current);
  };

  return (
    <div>
      <div>count:{count}</div>
      <button onClick={() => add100()}>加100</button>
      <button onClick={() => clearTimer()}>清楚定时器</button>
      <button onClick={() => setStarted(!started)}>
        {!started ? '开始' : '停止'}定时器
      </button>
    </div>
  );
};

export default memo(Counter);