useRef 为什么不能作为 useEffect 的依赖项

先聊聊 useEffect,众所周知,该 hook 的第二个参数将决定执行规则:

  • 空数组:只在组件加载时进行执行,类似 onMounted
  • 数组中有变量时:当变量变化时执行,类似 watch

若在 useEffect 的函数中,可以 return 回一个函数,该函数将会在组件卸载时执行,类似 onUnmounted

js 复制代码
useEffect(() => {
  // 副作用逻辑
  return () => {
    // 清理逻辑
  };
}, [依赖项]);

这里需要注意,刚才提到了变量变化时进行执行

数组中的变量变化,采用的是浅比较,也就是 Object.isObject.is 基本等同于 ===,所以如果是对象中的属性发生变化时,是不会引起函数的重新执行的。

  • 基本类型(number、string、boolean):值变化触发执行。
  • 引用类型(object、array、function):只有引用地址变化才会触发
js 复制代码
const [obj, setObj] = useState({ name: 'zhangsan', age: 18 })

不能 obj.name = 'lisi',这样修改是无效的
必须 setObj({ ...obj, name: 'lisi' }),也就是传入了一个新对象

所以我们平时往 useEffect 中的依赖项中写对象时,可以正常进行监听,因为每次 object 类型的 state 更改时,对象都整个换了新的,也就是引用地址更新了,所以 useEffect 的函数正常执行,也就符合预期。

但如果往 useEffect 的依赖项里写入了一个 useRef,也就是

js 复制代码
const num = useRef(1);
useEffect(() => {
    console.log(name.current);
}, [num]);

return <div onClick="num.current += 1">现在是 { num.current },点击加一</div>

此时是完全没有效果的,因为 useRef 里是个对象,而且是保持引用地址的对象,所以相当于完全没有变化。 不过在此例子中,即使将依赖项的 num 改为 num.current,也就是

js 复制代码
const num = useRef(1);
useEffect(() => {
    console.log(name.current);
}, [num.current]);

return <div onClick="num.current += 1">现在是 { num.current },点击加一</div>

点击后,也是无效的。不过根本原因却是组件不会发生重新渲染,useEffect 自然不会执行,我们再来复习能引起重新渲染的几个情况:

  • useState 中的 state 发生了更新
  • 父组件发生了更新
  • props 发生了更新
  • 订阅了 useContext,并且 context 的数据发生了更新

可以看到,只有 useState 的 state 发生更新,才会引起重新渲染,重新渲染才会引起 useEffect 的判断是否更新,之后才执行对应函数。

所以尽管 Object.is 能检测到变更,但组件没有重新渲染,也就执行不了对应的函数。

相关推荐
汉得数字平台13 分钟前
汉得H-AI飞码——前端编码助手V1.1.2正式发布:融业务知识,提开发效能
前端·人工智能·智能编码
前端小万13 分钟前
Jenkins 打包崩了?罪魁是 package.json 里的 ^
前端·jenkins
编程小白gogogo13 分钟前
苍穹外卖前端环境搭建
前端
光影少年17 分钟前
web端安全问题有哪些?
前端·安全
行走的陀螺仪24 分钟前
Vite & Webpack 插件/Loader 封装完全指南
前端·webpack·node.js·vite·前端工程化·打包构建
1024肥宅29 分钟前
浏览器网络请求 API:全面解析与高级封装(1)
前端·websocket·axios
小费的部落31 分钟前
Excel 在Sheet3中 匹配Sheet1的A列和Sheet2的A列并处理空内容
java·前端·excel
霍格沃兹测试学院-小舟畅学32 分钟前
Cypress 入门与优势分析:前端自动化测试的新利器
前端
1024肥宅32 分钟前
浏览器网络请求 API:全面解析与高级封装(2)
前端·websocket·axios
幼儿园技术家38 分钟前
深入理解 CSR / SSR / SSG:前端三种渲染模式的本质与选型
前端