您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
React Hooks与setInterval
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
React Hooks与setInterval
京东ZERO团队
2021-01-08
IP归属:未知
40200浏览
React
### 前言 Hooks出来已经有段时间了,相信大家都用过段时间了,有没有小伙伴们遇到坑呢,我这边就有个`setInterval`的坑,和小伙伴们分享下解决方案。 ### 问题 写个`count`每秒自增的定时器,如下写法结果,界面上`count`为`1`? ```javascript 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](https://codesandbox.io/embed/hooks-setinterval-error-w4qu6) > 如果某些特定值在两次重渲染之间没有发生变化,你可以通知` React` 跳过对 `effect` 的调用。就是将第二个参数改成`[]`,类似于更接近类组件的` componentDidMount` 和` componentWillUnmount `生命周期,只执行一次。 `effect`的第二个参数中传入的值就是 它更改的话, `effect`也会重新执行一遍的值。 因为`Effect`的第二个参数为`[]`,没有依赖,`Effect`只会执行一次。`setInterval`中拿到的是第一次渲染时的闭包`count`,所以`count`永远是`0`,界面会一直显示`1`,如下所示: ```javascript function Counter() { let [count, setCount] = useState(0); useEffect(() => { let id = setInterval(() => { setCount(0 + 1); }, 1000); return () => clearInterval(id); }, []); return <h1>{count}</h1>; } ``` 那有些小伙伴会说,如果我们直接往第二个参数加`count`呢 ```javascript function Counter() { //... useEffect(() => { let id = setInterval(() => { setCount(count + 1); }, 1000); return () => clearInterval(id); }, [count]); //... } ``` 这样效果是对的,但是性能不好。每当`count`更改了,`useEffect`就会渲染一次,定时器也会不停的被新增与移除。如下所示: ```javascript //第一次 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`,并返回一个更新后的值。这样定时器每次拿到的是最新的值。 ```javascript 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](https://codesandbox.io/embed/hooks-setinterval-usestate-grres) ### 方案二、用useRef `useRef`返回一个可变的`ref`对象,返回的`ref`对象在组件的整个生命周期内保持不变。 将定时器函数提取出来,每次定时器触发时,都能取到最新到`count`. ```javascript 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](https://codesandbox.io/embed/hooks-setinterval-useref-cgif3) > 思考:为什么不直接`setInterval(myRef.current, 1000)`这样写不行呢,还要包个方法返回? ```javascript 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](https://codesandbox.io/embed/hooks-setinterval-useref-error-52dm0) 下面的例子可以很好的解释。假如把`myRef.current`为`cur`变量,定时器的第一个参数为`interval`变量,`cur`变量更改,`interval`的取的还是之前赋值的值。 ```javascript 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`,方便重复使用。 ```javascript 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](https://codesandbox.io/embed/hooks-setinterval-ownhooks-0tpxe) ### 方案四、用useReducer 将`count`变量存入`reducer`中,使用`useReducer`更新`count` ```javascript 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://codesandbox.io/embed/hooks-setinterval-usereducer-2byrm) 还有什么好的方案欢迎小伙伴们留言评论~~ Happy coding .. :) ### 相关链接 [https://zh-hans.reactjs.org/docs/hooks-effect.html](https://zh-hans.reactjs.org/docs/hooks-effect.html) [https://zh-hans.reactjs.org/docs/hooks-reference.html](https://zh-hans.reactjs.org/docs/hooks-reference.html) [https://overreacted.io/making-setinterval-declarative-with-react-hooks/](https://overreacted.io/making-setinterval-declarative-with-react-hooks/)
原创文章,需联系作者,授权转载
上一篇:凹凸技术揭秘 · 夸克设计资产 · 打造全矩阵优质物料
下一篇:凹凸技术揭秘 · Tide 研发平台 · 布局研发新基建
相关文章
【技术干货】企业级扫描平台EOS关于JS扫描落地与实践!
开发也要防沉迷--IDEA插件教程
京东mPaaS平台之Android组件化系统私有化部署改造实践!
京东ZERO团队
文章数
39
阅读量
91750
作者其他文章
01
webpack打包组件配置(React版本)
这篇文章是以打包react插件的形式,介绍webpack的一些配置信息。如果写简单插件的话还是推荐使用rollup,但是可以用写插件的形式去学习一下webpack的一些东西。(适用于初中级webpack学者)
01
webpack核心概念与基本实现
webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。**
01
Typescript合成Webpack中
TypeScript是JavaScript类型的超集,它可以编译成纯JavaScript,简称ts。相对于ES6,TypeScript最大的改善是增加了类型系统,国内外很多大型工程都用它,如AngularJs,白鹭引擎、Antd。
01
小程序加载svg图片
小程序的[组件](https://developers.weixin.qq.com/miniprogram/dev/component/)中是没有支持`SVG`标签的。 但是在前端小伙伴的实际开发中,UED经常提供SVG图片过来,如果不想用引入`iconfont`的话,那么妹子我将介绍个很好用的方法。
最新回复
丨
点赞排行
共0条评论
京东ZERO团队
文章数
39
阅读量
91750
作者其他文章
01
webpack打包组件配置(React版本)
01
webpack核心概念与基本实现
01
Typescript合成Webpack中
01
小程序加载svg图片
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号