设为首页 - 加入收藏  
您的当前位置:首页 >应用开发 >从根上理解 React Hooks 的闭包陷阱(续集) 正文

从根上理解 React Hooks 的闭包陷阱(续集)

来源:亿华互联编辑:应用开发时间:2025-11-05 10:24:54

​​上篇文章​​我们知道了什么是从根 hooks 的闭包陷阱,它的上理产生原因和解决方式,并通过一个案例做了演示。闭包

其实那个案例的陷阱续集闭包陷阱的解决方式不够完善,这篇文章我们再完善一下。从根

首先我们先来回顾下什么是上理闭包陷阱:

hooks 的闭包陷阱是指 useEffect 等 hook 中用到了某个 state,但是闭包没有把它加到 deps 数组里,导致 state 变了,陷阱续集但是从根执行的函数依然引用着之前的 state。

它的上理解决方式就是正确设置 deps 数组,把用到的闭包 state 放到 deps 数组里,这样每次 state 变了就能执行最新的陷阱续集函数,引用新的从根 state。同时要清理上次的上理定时器、事件监听器等。闭包

我们举了这样一个例子:

import { useEffect, useState } from react;

function Dong() {

const [count,setCount] = useState(0);

useEffect(() => {

setInterval(() => {

setCount(count + 1);

}, 500);

}, []);

useEffect(() => {

setInterval(() => {

console.log(count);

}, 500);

}, []);

return

guang;

}

export default Dong;

每次打印都是服务器租用 0 :

解决方式就是把 count 设置到 deps 里,并添加清理函数:

import { useEffect, useState } from react;

function Dong() {

const [count,setCount] = useState(0);

useEffect(() => {

const timer = setInterval(() => {

setCount(count + 1);

}, 500);

return () => clearInterval(timer);

}, [count]);

useEffect(() => {

const timer = setInterval(() => {

console.log(count);

}, 500);

return () => clearInterval(timer);

}, [count]);

return

guang;

}

export default Dong;

这样就能解决闭包陷阱:

但是这种解决闭包陷阱的方式用在定时器上不是很合适。

为什么呢?

因为现在每次 count 变了就会重置定时器,那之前的计时就重新计算,这样就会导致计时不准。

所以,这种把依赖的 state 添加到 deps 里的方式是能解决闭包陷阱,但是定时器不能这样做。

那还有什么方式能解决闭包陷阱呢?

useRef。

闭包陷阱产生的原因就是 useEffect 的函数里引用了某个 state,形成了闭包,那不直接引用不就行了?

useRef 是在 memorizedState 链表中放一个对象,current 保存某个值。

它的源码是这样的:

初始化的时候创建了一个对象放在 memorizedState 上,后面始终返回这个对象。

这样通过 useRef 保存回调函数,网站模板然后在 useEffect 里从 ref.current 来取函数再调用,避免了直接调用,也就没有闭包陷阱的问题了。

也就是这样:

const fn = () => {

console.log(count);

};

const ref = useRef(fn);

useLayoutEffect(() => {

ref.current = fn;

});

useEffect(() => {

setInterval(() => ref.current(), 500);

}, []);

useEffect 里执行定时器,deps 设置为了 [],所以只会执行一次,回调函数用的是 ref.current,没有直接依赖某个 state,所以不会有闭包陷阱。

用 useRef 创建个 ref 对象,初始值为打印 count 的回调函数,每次 render 都修改下其中的函数为新创建的函数,这个函数里引用的 count 就是最新的。

这里用了 useLayoutEffect 而不是 useEffect 是因为 useLayoutEffect 是在 render 前同步执行的,useEffect 是在 render 后异步执行的,所以用 useLayoutEffect 能保证在 useEffect 之前被调用。

这种方式避免了 useEffect 里直接对 state 的高防服务器引用,从而避免了闭包问题。

另外,修改 count 的地方,可以用 setCount(count => count + 1) 代替 setCount(count + 1),这样也就避免了闭包问题:

useEffect(() => {

setInterval(() => {

setCount(count => count + 1);

}, 500);

}, []);

现在组件的代码是这样的:

import { useEffect, useLayoutEffect, useState, useRef } from react;

function Dong() {

const [count, setCount] = useState(0);

useEffect(() => {

setInterval(() => {

setCount(count => count + 1);

}, 500);

}, []);

const fn = () => {

console.log(count);

};

const ref = useRef(fn);

useLayoutEffect(() => {

ref.current = fn;

});

useEffect(() => {

setInterval(() => ref.current(), 500);

}, []);

return

guang;

}

export default Dong;

测试下:

确实,打印也是正常的,这就是解决闭包陷阱的第二种方式,通过 useRef 避免直接对 state 的引用,从而避免闭包问题。

这段逻辑用到了多个 hook,可以封装成个自定义 hook:

function useInterval(fn, time) {

const ref = useRef(fn);

useLayoutEffect(() => {

ref.current = fn;

});

useEffect(() => {

setInterval(() => ref.current(), time);

}, []);

}

然后组件代码就可以简化了:

function Dong() {

const [count, setCount] = useState(0);

useInterval(() => {

setCount(count + 1);

}, 500);

useInterval(() => {

console.log(count);

}, 500);

return

guang;

}

这样我们就用 useRef 的方式解决了闭包陷阱问题。

总结

上篇文章我们通过把依赖的 state 添加到 deps 数组中的方式,使得每次 state 变了就执行新的函数,引用新的 state,从而解决了闭包陷阱问题。

这种方式用在定时器上是不合适的,因为定时器一旦被重置和重新计时,那计时就不准确了。

所以我们才用了避免闭包陷阱的第二种方式:使用 useRef。

useRef 能解决闭包陷阱的原因是 useEffect 等 hook 里不直接引用 state,而是引用 ref.current,这样后面只要修改了 ref 中的值,这里取出来的就是最新的。

然后我们把这段逻辑封装成了个自定义 hook,这样可以方便复用。

解决 hooks 的闭包陷阱有两种方式:

设置依赖的 state 到 deps 数组中并添加清理函数。不直接引用 state,把 state 放到 useRef 创建的 ref 对象中再引用。

处理定时器的时候,为保证计时的准确,最好使用 useRef 的方式,其余情况两种都可以。

上一篇:Nginx (engine-x)是一个开源的高性能 HTTP 服务器、反向代理和 IMAP/POP3 代理服务器。nginx 杰出的功能有:稳定、丰富的功能集、简单的配置和低资源消耗。nginx 被用于一些高性能网站并在站长之间变得越来越流行。本教程会从源码构建一个带有 google paespeed 模块的用于 Ubuntu 15.04 的 nginx .deb 安装包。pagespeed 是一个由 google 开发的 web 服务器模块来加速网站响应时间、优化 html 和减少页面加载时间。ngx_pagespeed 的功能如下: 图像优化:去除元数据、动态缩放、重压缩。 CSS 与 JavaScript 压缩、串联、内联、外联。 小资源内联 图像与 JavaScript 延迟加载 HTML 重写 缓存生命期插件前置要求 Ubuntu Server 15.04 64位 root 权限该文我们将要: 安装必备软件包 安装带 ngx_pagespeed 的 nginx 测试安装必备包复制代码代码如下:sudo apt-get install dpkg-dev build-essential zlib1g-dev libpcre3 libpcre3-dev安装带 ngx_pagespeed 的 nginx第一步 - 添加nginx仓库复制代码代码如下: vim /etc/apt/sources.list.d/nginx.list加入下面的行:复制代码代码如下:deb http://nginx.org/packages/ubuntu/ trusty nginx deb-src http://nginx.org/packages/ubuntu/ trusty nginx更新仓库:复制代码代码如下:sudo apt-get update注意:假如你看到信息:GPG error [...] NO_PUBKEY [...] 等等请添加key:复制代码代码如下:sudo sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys KEYNUMBER sudo apt-get update第二步 - 从仓库下载 nginx 1.8复制代码代码如下: sudo su cd ~ mkdir -p ~/new/nginx_source/ cd ~/new/nginx_source/ apt-get source nginx apt-get build-dep nginx第三步 - 下载 Pagespeed复制代码代码如下:cd ~ mkdir -p ~/new/ngx_pagespeed/ cd ~/new/ngx_pagespeed/ ngx_version=1.9.32.3 wget https://github.com/pagespeed/ngx_pagespeed/archive/release-${ngx_version}-beta.zip unzip release-${ngx_version}-beta.zip cd ngx_pagespeed-release-1.9.32.3-beta/ wget https://dl.google.com/dl/page-speed/psol/${ngx_version}.tar.gz tar -xzf 1.9.32.3.tar.gz第四步 - 配置 nginx 来编译 Pagespeed复制代码代码如下:cd ~/new/nginx_source/nginx-1.8.0/debin/ vim rules在两处 CFLAGS .configure 下添加模块: --add-module=../../ngx_pagespeed/ngx_pagespeed-release-1.9.32.3-beta adding pagespeed to nginx adding pagespeed to nginx  第五步 - 打包 nginx 软件包并安装复制代码代码如下:cd ~/new/nginx_source/nginx-1.8.0/ dpkg-buildpackage -bdpkg-buildpackage 会编译 ~/new/ngix_source/ 为 nginx.deb。打包完成后,看一下目录:复制代码代码如下:cd ~/new/ngix_source/ ls nginx builded with pagespeed    接着安装 nginx。复制代码代码如下:dpkg -i nginx_1.8.0-1~trusty_amd64.deb    测试运行 nginx -V 测试 nginx 是否已经自带 ngx_pagespeed。复制代码代码如下:nginx -V   总结稳定、快速、开源的 nginx 支持许多不同的优化模块。这其中之一是 google 开发的‘pagespeed’。不像 apache,nginx 模块不是动态加载的,因此你必须在编译之前就选择好需要的模块。
下一篇:探索超频魅力(以速龙260超频为主题的终极挑战,尽享无尽可能性)

相关推荐:

4.6197s , 11737.703125 kb

Copyright © 2025 Powered by 从根上理解 React Hooks 的闭包陷阱(续集),亿华互联  滇ICP备2023000592号-16

sitemap

Top