useEffect 模拟 componentWillUnmount 不返回更新状态
Posted
技术标签:
【中文标题】useEffect 模拟 componentWillUnmount 不返回更新状态【英文标题】:useEffect simulating componentWillUnmount does not return updated state 【发布时间】:2020-06-29 06:01:56 【问题描述】:我有一个用 useState 初始化状态的功能组件,然后通过输入字段更改此状态。
然后我有一个模拟 componentWillUnmount 的 useEffect 挂钩,以便在卸载组件之前,将当前更新的状态记录到控制台。但是,记录的是初始状态而不是当前状态。
这是我想要做的简单表示(这不是我的实际组件):
import React, useEffect, useState from 'react';
const Input = () =>
const [text, setText] = useState('aaa');
useEffect(() =>
return () =>
console.log(text);
, [])
const onChange = (e) =>
setText(e.target.value);
;
return (
<div>
<input type="text" value=text onChange=onChange />
</div>
)
export default Input;
我将状态初始化为“初始”。然后我使用输入字段来改变状态,比如说我输入“新文本”。但是,卸载组件时,控制台会记录“初始”而不是“新文本”。
为什么会这样?如何在卸载时访问当前更新的状态?
非常感谢!
编辑:
将文本添加到 useEffect 依赖数组并不能解决我的问题,因为在我的实际场景中,我想做的是根据当前状态触发异步操作,这样做效率不高每次“文本”状态发生变化时。
我正在寻找一种仅在组件卸载之前获取当前状态的方法。
【问题讨论】:
【参考方案1】:您已经有效地记住了初始状态值,因此当组件卸载时, 值就是返回的函数包含在其范围内的值。
Cleaning up an effect
清理功能在组件从 UI 中移除之前运行 以防止内存泄漏。此外,如果一个组件呈现多个 次(就像他们通常做的那样),之前的效果会在 执行下一个效果。在我们的示例中,这意味着一个新的 每次更新都会创建订阅。为了避免在 每次更新,请参阅下一节。
为了在调用清理函数时获得最新状态,您需要在依赖数组中包含text
,以便更新函数。
Effect hook docs
如果你传递一个空数组(
[]
),效果里面的props和state 将始终具有其初始值。同时通过[]
作为第二个 参数更接近于熟悉的componentDidMount
和componentWillUnmount
心智模型,通常有更好的解决方案 以避免过于频繁地重新运行效果。
这意味着返回的“清理”函数仍然只访问上一个渲染周期的状态和道具。
编辑
useRef
useRef
返回一个可变引用对象,其.current
属性为 初始化为传递的参数 (initialValue
)。返回的对象 将在组件的整个生命周期内持续存在。...
它可以方便地保留任何可变值,类似于您在类中使用实例字段的方式。
使用 ref 将允许您缓存可以在清理函数中访问的当前 text
引用。
/编辑
组件
import React, useEffect, useRef, useState from 'react';
const Input = () =>
const [text, setText] = useState('aaa');
// #1 ref to cache current text value
const textRef = useRef(null);
// #2 cache current text value
textRef.current = text;
useEffect(() =>
console.log("Mounted", text);
// #3 access ref to get current text value in cleanup
return () => console.log("Unmounted", text, "textRef", textRef.current);
// eslint-disable-next-line react-hooks/exhaustive-deps
, []);
useEffect(() =>
console.log("current text", text);
return () =>
console.log("previous text", text);
, [text])
const onChange = (e) =>
setText(e.target.value);
;
return (
<div>
<input type="text" value=text onChange=onChange />
</div>
)
export default Input;
使用返回的清理函数中的console.log,您会注意到输入的每次更改都会将先前的状态记录到控制台。
在这个演示中,我在效果中记录了当前状态和在清理函数中的先前状态。请注意,清理函数在下一个渲染周期的当前日志之前首先记录。
【讨论】:
我的问题是,我真正需要做的是在组件卸载之前调度一个异步操作,所以在这种情况下,我不能使用你建议的方法,因为该操作会在每个状态改变的时间,这将是低效的。我正在寻找的是一种在卸载组件之前只获取当前状态一次的方法。 @edbgl 更新解决方案。 谢谢你,德鲁。我测试了您的解决方案,但初始值仍然是控制台日志而不是更新日志。在控制台中,我得到“Dismounted aaa”。 啊,对,我犯了一个错误。具有空依赖数组的效果挂钩将永远只有初始状态和道具。更新答案。您要解决什么具体的卸载方案? @EdmundoBiglia 得到了一个可行的解决方案、更新的答案和沙箱。以上是关于useEffect 模拟 componentWillUnmount 不返回更新状态的主要内容,如果未能解决你的问题,请参考以下文章