聊聊 React 18 对 Hooks 的影响

开发 前端
在 ahooks 中的 useUnmountedRef、useSafeState等都是为了解决这个警告而生的。同时我们在 ahooks 中必要的地方,为了避免这个告警,也会在组件卸载后,忽略后续的 setState。

一、 译者前言

最近 React 18 发布后,部分改动对我们使用 React Hooks 有一些影响。这篇文章对官方的文档 Update to remove the "setState on unmounted component warning"[1] 做了翻译,好让大家清晰的认识到这个改动的背景和影响。这是 React 18 对 Hooks 的影响系列第一篇,后面我还会整理其它有影响的改动。

二、 翻译

1、 背景

之前在已经卸载的组件中调用 setState时,会有一个警告,本次我们将这个警告移除了。警告内容如下:

Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function。

警告:不能在已经卸载的组件中更改 state。这是一个无用的操作,它表明你的项目中存在内存泄漏。要解决这个问题,请在 useEffect 清理函数中取消所有订阅和异步任务。

不幸的是,这个警告经常被误解,并且会误导大家。

它原本是想保证如下示例能正常工作的:

useEffect(() => {
function handleChange() {
setState(store.getState())
}
store.subscribe(handleChange)
return () => store.unsubscribe(handleChange)
}, [])

在这个例子中,如果你忘记了在 effect 清理函数中调用 unsubscribe,那肯定是有内存泄漏的。

2、 为什么说这个警告会误导大家呢?

事实上,上面的场景并不常见。反而如下场景是更常见的:

async function handleSubmit() {
setPending(true)
await post('/someapi') // component might unmount while we're waiting
setPending(false)
}

在上面的代码中,如果发送请求时,组件卸载了,会抛出警告。但是,在这种场景下,警告误导了大家。

这里其实没有内存泄漏:

  • Promise 会很快完成执行,然后内存被垃圾回收机制回收。
  • 即使没有很快完成执行,这个警告也是没用的,因为垃圾回收也还是得等 Promise 执行完回收,你啥也不能做。

一般,我们会通过如下代码来消除警告:

let isMountedRef = useRef(false)
useEffect(() => {
isMountedRef.current = true
return () => {
isMountedRef.current = false
}
}, [])
async function handleSubmit() {
setPending(true)
await post('/someapi')
if (!isMountedRef.current) {
setPending(false)
}
}

实际上,这种写法是没用的,并没有解决所谓的“内存泄漏”,它仅仅只是抑制了警告。正如前面说的,这里其实是没有内存泄漏的。内存会随着 Promise 执行完而释放,并没有什么在无限执行。

3、 上述抑制警告方案比不处理更糟糕

上述抑制警告的解决方案,现在非常非常普遍。但它其实没任何好处,反而比不处理更糟糕:

  • 未来,React 会提供一个新能力,在组件卸载不可见时,我们会保存组件现在的 state,但仍会卸载组件。下次加载组件的时候,我们会用之前保存的 state 来渲染组件,以便恢复之前的页面。在组件卸载之后,setPending(false)不会被执行到,所以 pending会一直是 true,那下次恢复组件的时候,看起来是请求没有执行完成,会变的更糟糕。(译者注:关于这一块详细的行为和影响面,下一篇文章介绍)。
  • 假设用户点击一个按钮,发起一个网络请求,请求结束后更新 state。为了避免这个警告,有些人会将请求行为放到 useEffect 中,因为在 useEffect 中可以监听到组件卸载,以忽略后续的 state 更新,消除警告。这样代码变的非常不清晰,非常糟糕!就是因为这个错误的警告,会让大家写出更烂的代码。

4、 移除警告

最终,我们决定移除这个警告。这个警告想解决的订阅问题,在日常代码中并不常见。大部分情况下,它反而会误导大家,为了避免告警写出更烂的代码。希望这个警告的移除,可以让你移除代码中的 isMounted。

三、 译者总结

在 ahooks 中的 useUnmountedRef[2]、useSafeState[3] 等都是为了解决这个警告而生的。同时我们在 ahooks 中必要的地方,为了避免这个告警,也会在组件卸载后,忽略后续的 setState。

目前来看,这些代码是多余的,后续 ahooks 会陆续优化相关场景,但不会太快。因为在 react 16、17 中这个告警仍然会有,会对新人造成不必要的困扰。我们会等 React 18 覆盖面比较广之后,再进行代码优化。

以后在代码中大家应该不需要再考虑这个告警了,不需要再使用 useUnmountedRef[4]、useSafeState[5] 等 Hooks 了。

参考资料

[1]Update to remove the setState on unmounted component warning: https://github.com/reactwg/react-18/discussions/82。

[2]useUnmountedRef: https://ahooks.js.org/hooks/use-unmounted-ref。

[3]useSafeState: https://ahooks.js.org/hooks/use-safe-state。

[4]useUnmountedRef: https://ahooks.js.org/hooks/use-unmounted-ref。

[5]useSafeState: https://ahooks.js.org/hooks/use-safe-state。

责任编辑:姜华 来源: 前端技术砖家
相关推荐

2021-05-11 08:48:23

React Hooks前端

2019-08-20 15:16:26

Reacthooks前端

2023-11-06 08:00:00

ReactJavaScript开发

2020-10-28 09:12:48

React架构Hooks

2021-03-18 08:00:55

组件Hooks React

2024-09-23 14:52:49

2020-09-19 17:46:20

React Hooks开发函数

2022-08-21 09:41:42

ReactVue3前端

2019-03-13 10:10:26

React组件前端

2022-04-16 20:10:00

React Hookfiber框架

2023-05-08 07:52:29

JSXReactHooks

2022-02-10 19:15:18

React监听系统模式

2022-07-18 09:01:58

React函数组件Hooks

2021-11-05 10:36:19

性能优化实践

2020-08-10 06:31:01

React Hooks前端开发

2022-03-16 22:24:50

ReactstateHooks

2022-04-14 11:50:39

函数组件hook

2022-06-23 09:04:14

ReactHooks项目

2020-03-16 10:25:49

前端React Hooks响应式布局

2021-05-21 06:13:35

React Hooks react-refrReact
点赞
收藏

51CTO技术栈公众号