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

更新日期: 2022-05-26阅读: 1.8k标签: 特性

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


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

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

最后

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

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


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

CSS新特性contain,控制页面的重绘与重排

contain 属性允许我们指定特定的 DOM 元素和它的子元素,让它们能够独立于整个 DOM 树结构之外。目的是能够让浏览器有能力只对部分元素进行重绘、重排,而不必每次都针对整个页面。

Html5、Css3、ES6的新特性

Html5的新特性语义化标签:有利于SEO,有助于爬虫抓取更多的有效信息,爬虫是依赖于标签来确定上下文和各个关键字的权重。表单新特性,多媒体视频(video)和音频(audio)

ES6新特性--var、let、const

var不存在块级作用域,具有变量提升机制。 let和const存在块级作用域,不存在变量提升。在同一作用域内只能声明一次。const在声明时需要赋值且无法修改,但如果常量是对象,则对象的属性可以修改。

Js即将到来的3个新特性

Optional Chaining(可选链式调用);Nullish coalescing(空值合并);Pipeline operator(管道运算符)通过三个函数对字符串进行处理;

Angular 8的新特性介绍

在今天早些时候Angular团队发布了8.0.0稳定版。其实早在NgConf 2019大会上,演讲者就已经提及了从工具到差分加载的许多内容以及更多令人敬畏的功能。下面是我对8.0.0一些新功能的简单介绍,希望可以帮助大家快速了解新版本

使用 React 要懂的 Js特性

与我使用的其他框架相比,我最喜欢 React 的原因之一就是它对 JavaScript 的暴露程度。没有模板DSL( JSX 编译为合理的 JavaScript),组件 API 只是通过添加 React Hooks 变得更简单,并且该框架为解决的核心 UI 问题提供非常少的抽象概念

ES2019 新特性汇总

最近 ECMAScript2019,最新提案完成:tc39 Finished Proposals,我这里也是按照官方介绍的顺序进行整理,如有疑问,可以查看官方介绍啦~另外之前也整理了 《ES6/ES7/ES8/ES9系列》,可以一起看哈。

Js的用途和特性

JavaScript 最初的目的是为了“赋予网页生命”。这种编程语言我们称之为脚本。它们可以写在 HTML 中,在页面加载的时候会自动执行。脚本作为纯文本存在和执行。它们不需要特殊的准备或编译即可运行。

十个超级实用的 JS 特性

你可能刚上手 JavaScript,或者只是曾经偶尔用过。不管怎样,JavaScript 改变了很多,有些特性非常值得一用。 这篇文章介绍了一些特性,在我看来,一个严肃的 JavaScript 开发者每天都多多少少会用到这些特性

解密HTTP/2与HTTP/3 的新特性

HTTP/2 相比于 HTTP/1.1,可以说是大幅度提高了网页的性能,只需要升级到该协议就可以减少很多之前需要做的性能优化工作,当然兼容问题以及如何优雅降级应该是国内还不普遍使用的原因之一。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!