React之setInterval使用

前端 0 703 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);

使用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>();
  const [count, setCount] = useState(1);

  const handleTimer = () => {
    setCount(count + 1);
  };

  useEffect(() => {
    countRef.current = handleTimer;
  });

  useEffect(() => {
    if (!started) {
      clearInterval(timer);
      return;
    }
    const newTimer = setInterval(() => {
      countRef.current();
    }, 500);
    setTimer(newTimer);
  }, [started]);

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

  const add100 = () => {
    setCount(count + 100);
  };

  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);

把逻辑封装成hooks

useInterval

因为是hooks,需要遵循hooks规则:https://zh-hans.reactjs.org/docs/hooks-rules.html

import { useEffect, useRef } from 'react';

export const useInterval = (
  callback: any,
  delay: number | undefined,
  options?: { immediate: boolean }
) => {
  const savedCallback = useRef<any>();
  savedCallback.current = callback;

  useEffect(() => {
    if (typeof delay !== 'number' || delay < 0) return;
    if (options?.immediate) {
      callback();
    }
    const timer = setInterval(() => {
      savedCallback.current();
    }, delay);
    return () => clearInterval(timer);
  }, [delay]);
};

使用

// import { useInterval } from 'ahooks';
import { memo, useState } from 'react';

import { useInterval } from '@/hooks/useInterval';
const Counter = () => {
  console.log('Counter组件渲染了');
  const [count, setCount] = useState(1);
  const [delay, setDelay] = useState<number | undefined>();
  function callback() {
    setCount(count + 1);
  }

  useInterval(() => {
    callback();
  }, delay);

  return (
    <div>
      <div>count:{count}</div>
      <button onClick={() => setCount(count + 100)}>加100</button>
      <button onClick={() => setDelay(undefined)}>清除定时器</button>
      <button onClick={() => setDelay(delay ? undefined : 500)}>
        {!delay ? '开始' : '停止'}定时器
      </button>
    </div>
  );
};

export default memo(Counter);

使用useReducer

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

const Counter = () => {
  console.log('Counter组件渲染了');

  const reducer = (state, action) => {
    switch (action.type) {
      case 'addNum':
        return state + action.num;
      case 'addOne':
        return state + 1;
      default:
        throw new Error();
    }
  };
  const [count, dispatch] = useReducer(reducer, 1);
  const [timer, setTimer] = useState<any>(null);

  const add100 = () => {
    dispatch({ type: 'addNum', num: 100 });
  };

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

  const switchTimer = (flag) => {
    if (!flag) {
      clearInterval(timer);
      setTimer(null);
      return;
    }
    const newTimer = setInterval(() => {
      dispatch({ type: 'addOne' });
    }, 500);
    setTimer(newTimer);
  };

  return (
    <div>
      <div>count:{count}</div>
      <button onClick={() => add100()}>加100</button>
      <button onClick={() => clearTimer()}>清除定时器</button>
      <button onClick={() => switchTimer(!timer ? true : false)}>
        {!timer ? '开始' : '停止'}定时器
      </button>
    </div>
  );
};

export default memo(Counter);

React hooks 重学系列

最后更新于:2022-04-28 20:12:45

欢迎评论留言~
0/200
支持markdown
Comments | 0 条留言
按时间
按热度
目前还没有人留言~