React18 的 useEffect 新特性为什么被疯狂吐槽?

Posted CSDN云计算

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React18 的 useEffect 新特性为什么被疯狂吐槽?相关的知识,希望对你有一定的参考价值。

作者 | 零一

来源 | 前端印象

react18 已经出来一段时间了,create-react-app 默认安装的 React 版本也已经是 18+,不知道有没有小伙伴发现自己有点看不懂 React 了?

import  useEffect, useState  from 'react'

function App () 
  const [data, setData] = useState(0)
  
  useEffect(() => 
    setData(preData => preData + 1)
  , [])
  
  return (
    <div>data</div>
  )

看一下这段简单的代码,页面最终展示的数字是几?

1 这样吗?我觉得应该也是这样,可事实就是在 React18 里,这并不是预期效果,最终展示的其实是 2,为什么呢?

useEffect 的"新特性"

根据 React 最新的文档[1] 中对于 useEffect 的介绍得知,之所以我们刚才的例子最终展示的是 2 而不是 1 的原因是,在 dev 环境下,React 会将每个组件挂载两次进行测试。测试什么?测试你的 useEffect 有没有潜在问题

大家都知道函数式组件挂载后,会执行 useEffect 定义的副作用;在组件卸载时,会执行 useEffect return 出来的回调执行一些组件卸载时的行为,即:

function App () 
  useEffect(() => 
    console.log('组件挂载了')
    return () => 
      console.log('组件卸载了')
    
  , [])
  
  return (
    <div>useEffect</div>
  )

从组件挂载到卸载就会依次打印:

组件挂载了
组件卸载了

而在 React18 里,是这样打印的:

组件挂载了
组件卸载了
组件挂载了

按照文档里所说的,之所有这么做的,是为了通过挂载两次组件来提早发现你的问题,例如:

import  useEffect, useState  from 'react'

function App () 
  const [data, setData] = useState(0)
  
  useEffect(() => 
    setInterval(() => 
      setData(preData => preData + 1)
    , 1000)
  , [])
  
  return (
    <div>data</div>
  )

这段代码时很多刚使用 React 的同学经常会犯的错误,在 useEffect 里定义了个定时器,但没有在任何地方去清除它,所以即使在组件卸载了,这个定时器仍然还在运作,不光造成了内存泄漏,还可能会导致程序出现问题

所以就基于这段错误的代码,React18 执行 挂载 => 卸载 => 挂载,你就会发现,实际是有两个定时器在跑的,所以原本你想每秒 data + 1,变成了每秒 data + 2,如此明显的问题一下就被我们发现了

那正确的做法就是在 useEffectreturn 一个用于卸载时执行的回调函数:

import  useEffect, useState  from 'react'

function App () 
  const [data, setData] = useState(0)
  
  useEffect(() => 
+   const timer = setInterval(() => 
      setData(preData => preData + 1)
    , 1000)
 
+   return () => clearInterval(timer)
  , [])
  
  return (
    <div>data</div>
  )

这样就没有问题了。谢谢 React18 这个"独特"的新特性(手动狗头)

单单基于这个出发点,我觉得是非常好的,能帮我们提早发现问题,解决问题,而不是等发到线上后造成了性能问题,回过头来再逐一排查。而且这只会在开发环境才会挂载两次,生产环境还是正常的

但真的是个完美的特性吗?根据网友的吐槽和我目前使用下来的感受,给我们造成的麻烦可能大于它本身的好处了

即使我的 useEffect 里根本没有需要在卸载时清理的对象,它也会被执行两次,比如请求两次、赋值两次 ... 这似乎是给我们造成了不少的负担啊,不知道的以为是别的地方出了 bug 呢!

关闭特性

我也可以手动关闭这个特性,找到入口文件 main.tsx,把 StrictMode 标签给去掉就好了

mport React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import './index.css'

ReactDOM.createRoot(document.getElementById('root')!).render(
- <React.StrictMode>
    <App />
- </React.StrictMode>
)

不过这样也把其它的提示给一并干掉了,其实我是不想这么做的

只是这样?

有很多人都在吐槽着!比如:初始化时 useEffect 会造成两次请求的话,似乎我们也不该在 useEffect 中发起请求?

然而 Dan 给出的解释就是说,你应该在服务端渲染时就请求到数据,而不是在客户端渲染挂载了 DOM 后才请求数据

其实 React18 将在之后推出一些别的功能,这个模拟组件重新挂载的特性只是为之后的功能做准备的,具体是什么功能呢?类似于 Vue 的 KeepAlive[2]

最后

简单总结一下:这个特性出发点是好的,同时也是为了之后的新特性做准备。但推出这个功能的同时也要考虑一下开发者的体验(起码是大部人的开发体验),不然真的是得不偿失。

对于 useEffect 这个新特性,你怎么看?欢迎在评论区留言!

参考资料

[1]

React 最新的文档: https://beta-reactjs-org-git-effects-fbopensource.vercel.app/learn/synchronizing-with-effects

[2]

KeepAlive: https://vuejs.org/guide/built-ins/keep-alive.html#basic-usage

往期推荐

协程到底有什么用?6种I/O模式告诉你!

快速入门 Docker 的五种网络模式

Redis 内存满了怎么办?这样置才正确!

Kubernetes 弃用 Docker ,我们该怎么办?

点分享

点收藏

点点赞

点在看

以上是关于React18 的 useEffect 新特性为什么被疯狂吐槽?的主要内容,如果未能解决你的问题,请参考以下文章

react hook 新特性汇总

如何使用/不使用 useEffect 钩子在 React JS 函数中获取更改的(新)状态值?

了解函数化组件 HooksuseState,useEffect,useContext,useReducer

了解函数化组件 HooksuseState,useEffect,useContext,useReducer

为啥 React 的 useReducer 在第一次在 useEffect 上获取数据时返回空状态?

精读《React 18》