4

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

 2 years ago
source link: https://www.fly63.com/article/detial/11586
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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 最新的文档 中对于 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,如此明显的问题一下就被我们发现了

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

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 中发起请求?

628f1a899ef5d.jpg

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

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

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

作者:零一
来源:https://mp.weixin.qq.com/s/8PaS0DnSvKAOVMeOSGz52w

链接: https://www.fly63.com/article/detial/11586


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK