React Hooks与setInterval

更新日期: 2019-11-08 阅读: 3.5k 标签: Hooks

前言

Hooks出来已经有段时间了,相信大家都用过段时间了,有没有小伙伴们遇到坑呢,我这边就有个 setInterval 的坑,和小伙伴们分享下解决方案。


前言

写个 count 每秒自增的定时器,如下写法结果,界面上 count 为 1 ?

function Counter() {
  let [count, setCount] = useState(0);
  useEffect(() => {
    let id = setInterval(() => {
      setCount(count + 1);
    }, 1000);
    return () => clearInterval(id);
  }, []);
  return <h1>{count}</h1>;
}

https://codesandbox.io/embed/hooks-setinterval-error-w4qu6

如果某些特定值在两次重渲染之间没有发生变化,你可以通知 react 跳过对 effect 的调用。就是将第二个参数改成 [] ,类似于更接近类组件的 componentDidMount 和 componentWillUnmount 生命周期,只执行一次。 effect 的第二个参数中传入的值就是 它更改的话, effect 也会重新执行一遍的值。

因为 Effect 的第二个参数为 [] ,没有依赖, Effect 只会执行一次。 setInterval 中拿到的count 永远是 0 ,界面会一直显示 1 ,如下所示:

function Counter() {
  let [count, setCount] = useState(0);
  useEffect(() => {
    let id = setInterval(() => {
      setCount(0 + 1);
    }, 1000);
    return () => clearInterval(id);
  }, []);
 return <h1>{count}</h1>;
}

那有些小伙伴会说,如果我们直接往第二个参数加 count 呢

function Counter() {
//... 
useEffect(() => {
    let id = setInterval(() => {
      setCount(count + 1);
    }, 1000);
    return () => clearInterval(id);
  }, [count]);
//...
}

这样效果是对的,但是性能不好。每当 count 更改了, useEffect 就会渲染一次,定时器也会不停的被新增与移除。如下所示:

//第一次
function Counter() {
//... 
useEffect(() => {
    let id = setInterval(() => {
      setCount(0 + 1);
    }, 1000);
    return () => clearInterval(id);
  }, [0]);
//...
}
//第二次
function Counter() {
//... 
useEffect(() => {
    let id = setInterval(() => {
      setCount(1 + 1);
    }, 1000);
    return () => clearInterval(id);
  }, [1]);
//...
//第N次
}

那到底要怎么做才能有保障性能,定时器只监听一次,又使定时器起作用呢?


方案一、函数式更新

useState 中的set方法可接收函数,该函数将接收先前的 state ,并返回一个更新后的值。这样定时器每次拿到的是最新的值。

function Counter() {
let [count, setCount] = useState(0);
useEffect(() => {
    let id = setInterval(() => {
      setCount(v => {
        return v + 1;
      });
    }, 1000);
    return () => clearInterval(id);
  }, []);
return <h1>{count}</h1>;
}

https://codesandbox.io/embed/hooks-setinterval-usestate-grres


方案二、用useRef

useRef 返回一个可变的 ref 对象,返回的 ref 对象在组件的整个生命周期内保持不变。

将定时器函数提取出来,每次定时器触发时,都能取到最新到 count .

function Counter() {
  let [count, setCount] = useState(0);
  const myRef = useRef(null);
  myRef.current = () => {
    setCount(count + 1);
  };
  useEffect(() => {
    let id = setInterval(() => {
      myRef.current();
    }, 1000);
    return () => clearInterval(id);
  }, []);
  return <h1>{count}</h1>;
}

https://codesandbox.io/embed/hooks-setinterval-useref-cgif3

思考:为什么不直接 setInterval(myRef.current, 1000) 这样写不行呢,还要包个方法返回?

function Counter() {
  let [count, setCount] = useState(0);
  const myRef = useRef(null);
  myRef.current = () => {
    setCount(count + 1);
  };
  useEffect(() => {
    let id = setInterval(myRef.current, 1000);
    return () => clearInterval(id);
  }, []);
 return <h1>{count}</h1>;
}

https://codesandbox.io/embed/hooks-setinterval-useref-error-52dm0

下面的例子可以很好的解释。假如把 myRef.current 为 cur 变量,定时器的第一个参数为 interval 变量, cur 变量更改, interval 的取的还是之前赋值的值。

var cur=()=>{var count=0;console.log(count)};
var interval=cur;
var cur=()=>{var count=1;console.log(count)};
interval();//0

var cur=()=>{var count=0;console.log(count)};
var interval=()=>{cur()};
var cur=()=>{var count=1;console.log(count)};
interval();//1


方案三、自定义hook

可以写个自定义 hook ,方便重复使用。

function useInterval(fun) {
  const myRef = useRef(null);
  useEffect(() => {
    myRef.current = fun;
  }, [fun]);
  useEffect(() => {
    let id = setInterval(() => {
      myRef.current();
    }, 1000);
    return () => clearInterval(id);
  }, []);
}

function Counter() {
  let [count, setCount] = useState(0);
  useInterval(() => {
    setCount(count + 1);
  });
  return <h1>{count}</h1>;
}

https://codesandbox.io/embed/hooks-setinterval-ownhooks-0tpxe


方案四、用useReducer

将 count 变量存入 reducer 中,使用 useReducer 更新 count

function reducer(state, action) {
  switch (action.type) {
    case "increment":
      return state + 1;
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, 0);
  useEffect(() => {
    setInterval(() => {
      dispatch({ type: "increment" });
    }, 1000);
  }, []);
  return <h1>{state}</h1>;
}

https://codesandbox.io/embed/hooks-setinterval-usereducer-2byrm


本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://fly63.com/article/detial/6344

相关推荐

在hooks中使用Mobx

创建store;注入store,这样既可以在class中使用,也可以在hooks中使用了;在class中使用;在hooks中使用

使用react hooks实现自己的context-redux

我们将userReducer函数返回的原始dispath命名为origin_dispatch,自定义dispatch函数,当action为函数的时候,我们执行action函数,并将origin_dispatch当作参数传进去;action不是函数,直接调用origin_dispatch,不做处理

React将引入Hooks,你怎么看?

近日,据 MIT Technology Review 报道,一位名为“Repairnator”的机器人在 GitHub 上“卧底”数月,查找错误并编写和提交修复补丁,结果有多个补丁成功通过并被采纳,这位 Repairnator 到底是如何拯救程序员于水火的呢?

useEffect Hook 是如何工作的?

使用useEffect 就像瑞士军刀。它可以用于很多事情,从设置订阅到创建和清理计时器,再到更改ref的值。与 componentDidMount、componentDidUpdate 不同的是,在浏览器完成布局与绘制之后,传给 useEffect 的函数会延迟调用。

精通React今年最劲爆的新特性——React Hooks

你还在为该使用无状态组件(Function)还是有状态组件(Class)而烦恼吗?你还在为搞不清使用哪个生命周期钩子函数而日夜难眠吗?你在还在为组件中的this指向而晕头转向吗?这样看来,说React Hooks是今年最劲爆的新特性真的毫不夸张。

React Hooks 你真的用对了吗?

从 React Hooks 正式发布到现在,我一直在项目使用它。但是,在使用 Hooks 的过程中,我也进入了一些误区,导致写出来的代码隐藏 bug 并且难以维护。这篇文章中,我会具体分析这些问题,并总结一些好的实践,以供大家参考

React-Hooks

以下是上一代标准写法类组件的缺点,也正是hook要解决的问题,型组件很难拆分和重构,也很难测试。业务逻辑分散在组件的各个方法之中,导致重复逻辑或关联逻辑。

用 React Hooks 做一个搜索栏

以下是我们将要构建的搜索框的动图。这是一个简单的搜索框,我们可以用它来搜索联系人列表。我们将使用函数式组件,而不是基于类的组件来实现它。

react hooks系列之useRef

react hooks是 react 16.8 引入的特性,这里我们通过对react-hook-form进行分析来了解成熟的库是如何使用hook的。这将是一个系列,首先推荐 useRef

React Hooks 底层解析[译]

对于 React 16.7 中新的 hooks 系统在社区中引起的骚动,我们都有所耳闻了。人们纷纷动手尝试,并为之兴奋不已。一想到 hooks 时它们似乎是某种魔法,React 以某种甚至不用暴露其实例

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!