如何检查组件是不是已卸载到功能组件中?
Posted
技术标签:
【中文标题】如何检查组件是不是已卸载到功能组件中?【英文标题】:How can I check if the component is unmounted in a functional component?如何检查组件是否已卸载到功能组件中? 【发布时间】:2019-12-28 15:31:54 【问题描述】:回调函数设置组件状态。但有时提供数据的订阅需要结束。因为回调是异步执行的,所以它不知道订阅是否在服务调用(它执行回调函数)之后结束。
然后我在控制台中看到以下错误:
警告:无法对未安装的组件执行 React 状态更新。 这是一个空操作,但它表明您的应用程序中存在内存泄漏。 要修复,请取消 useEffect 中的所有订阅和异步任务 清理功能。
有没有办法访问组件状态,即使我在回调函数中?
这将是步骤:
带参数订阅 退订 组件已卸载 订阅的服务执行回调函数 回调函数在未安装的组件中设置状态,并在上面给出错误【问题讨论】:
我猜你使用hooks useState 你可以使用useEffect (错误提示)返回一个cleanup function 来改变一个mounted variable 并检查that variable before calling the useState function 【参考方案1】:你可以使用这样的引用:
const mounted = useRef(false);
useEffect(() =>
mounted.current = true;
return () => mounted.current = false; ;
, []);
然后在你的回调中你可以检查mounted.current === false
并避免设置状态
【讨论】:
非常干净和简单的解决方案!【参考方案2】:这里有一些伪代码,您可以使用useEffect 查看组件是否已挂载。
它使用 useEffect 来侦听someService
,当它收到消息时它会检查组件是否已挂载(组件卸载时也会调用清理函数),如果是,则使用由useState 创建的setServiceMessage
设置服务接收的消息:
import useState, useEffect from 'react';
import someService from 'some-service';
export default props =>
const userId = props.userId;
const [serviceMessage, setServiceMessage] = useState([]);
useEffect(
() =>
const mounted = current: true ;
someService.listen(
//listen to messages for this user
userId,
//callback when message is received
message =>
//only set message when component is mounted
if (mounted.current)
setServiceMessage(serviceMessage.concat(message));
);
//returning cleanup function
return () =>
//do not listen to the service anymore
someService.stopListen(userId);
//set mounted to false if userId changed then mounted
// will immediately be set to true again and someService
// will listen to another user's messages but if the
// component is unmounted then mounted.current will
// continue to be false
mounted.current = false;
;
,//<-- the function passed to useEffects
//the function passed to useEffect will be called
//every time props.userId changes, you can pass multiple
//values here like [userId,otherValue] and then the function
//will be called whenever one of the values changes
//note that when this is an object then hi:1 is not hi:1
//referential equality is checked so create this with memoization
//if this is an object created by mapStateToProps or a function
[userId]
);
;
【讨论】:
【参考方案3】:我发现这个问题的公认答案很难阅读,React 提供了他们自己的documentation on just this question。他们的例子是:
import React, useState, useEffect from 'react';
function FriendStatus(props)
const [isOnline, setIsOnline] = useState(null);
useEffect(() =>
function handleStatusChange(status)
setIsOnline(status.isOnline);
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
// Specify how to clean up after this effect:
return function cleanup()
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
;
);
if (isOnline === null)
return 'Loading...';
return isOnline ? 'Online' : 'Offline';
我创建了一个名为<Fade>
的组件,它将淡入/淡出它给定的任何子元素。请注意,它依赖于 bootstrap 的 .fade
和 .show
类,尽管这些可以在没有 bootstrap 的情况下轻松实现。
const Fade: React.FC<> = ( children ) =>
const [ className, setClassName ] = useState('fade')
const [ newChildren, setNewChildren ] = useState(children)
useEffect(() =>
setClassName('fade')
const timerId = setTimeout(() =>
setClassName('fade show')
setNewChildren(children)
, TIMEOUT_DURATION)
return () =>
clearTimeout(timerId)
, [children])
return <Container fluid className=className + ' p-0'>newChildren</Container>
这一切都归结为一条规则:在useEffect
返回的清理函数中取消订阅您的异步任务。
【讨论】:
【参考方案4】:您可以从 useEffect 返回一个函数,该函数将在卸载功能组件时触发。 Please read this
import React, useEffect from 'react';
const ComponentExample = () =>
useEffect(() =>
// Anything in here is fired on component mount.
return () =>
// Anything in here is fired on component unmount.
, [])
【讨论】:
【参考方案5】:这个钩子(来自 Mohamed 的回答)以更优雅的方式解决了这个问题:
function useMounted()
const mounted = useMemo(() => ( current: true ), []);
useEffect(() =>
return () => mounted.current = false
, [mounted]);
return mounted;
(更新为使用 useMemo 而不是 useRef 以提高可读性)。
【讨论】:
这个解决方案看起来很干净,但我不明白 useRef 在这种情况下的用户。 我已经更新了代码示例。它不再使用 useRef。以上是关于如何检查组件是不是已卸载到功能组件中?的主要内容,如果未能解决你的问题,请参考以下文章
如何将状态值从一个组件访问到位于单独文件中的其他功能(不是组件)?反应js