有没有办法检查反应组件是不是已卸载?
Posted
技术标签:
【中文标题】有没有办法检查反应组件是不是已卸载?【英文标题】:Is there a way to check if the react component is unmounted?有没有办法检查反应组件是否已卸载? 【发布时间】:2017-02-07 14:42:46 【问题描述】:我有一个用例需要卸载我的反应组件。但在某些情况下,特定的 react 组件会被不同的函数卸载。 因此,我需要在卸载之前检查组件是否已安装。
【问题讨论】:
【参考方案1】:建议您使用 useRef
挂钩来跟踪组件是否已安装,因为每当您更新状态时,react 都会重新渲染整个组件以及它将触发 useEffect 或其他钩子的执行。
function MyComponent(props: Props)
const isMounted = useRef(false)
useEffect(() =>
isMounted.current = true;
return () => isMounted.current = false
, []);
return (...);
export default MyComponent;
然后检查组件是否使用if (isMounted.current) ...
挂载
【讨论】:
对类组件使用***.com/a/39767963/1783174。将其用于钩子。 你的意思是说你做推荐使用useState
而不是“不”?因为您的解决方案似乎使用它
如果您在单个组件中有多个 useEffects,则将调用清理函数,因此这不起作用,因为重新渲染和重新安装之间没有区别。
“另外,如果一个组件渲染多次(通常是这样),前一个效果会在执行下一个效果之前被清除。” reactjs.org/docs/hooks-reference.html#cleaning-up-an-effect【参考方案2】:
使用@DerekSoike 回答,但是在我的情况下,使用useState
来控制挂载状态不起作用,因为状态在不需要时复活了
对我有用的是使用单个变量
myFunct
在setTimeout
中被调用,我的猜测是,当同一个组件在另一个页面中初始化钩子时,它恢复了导致内存泄漏再次出现的状态
所以这对我不起作用
const [isMounted, setIsMounted] = useState(false)
useEffect(() =>
setIsMounted(true)
return () => setIsMounted(false)
, [])
const myFunct = () =>
console.log(isMounted) // not always false
if (!isMounted) return
// change a state
这确实对我有用
let stillMounted = value: false
useEffect(() =>
stillMounted.value = true
return () => (stillMounted.value = false)
, [])
const myFunct = () =>
if (!stillMounted.value) return
// change a state
【讨论】:
这有效,而之前的策略(在许多地方更新)没有...谢谢@GWorking 如果我们将 isMounted 状态更新为 true 或 false,它将重新加载功能组件,我建议使用 let 变量。【参考方案3】:如果你使用钩子:
function MyComponent(props: Props)
const [isMounted, setIsMounted] = useState<boolean>(false);
useEffect(() =>
setIsMounted(true);
, []);
useEffect(() =>
return () =>
setIsMounted(false);
, []);
return (...);
export default MyComponent;
【讨论】:
这篇文章dev.to/trentyang/replace-lifecycle-with-hooks-in-react-3d4n 将有助于提供更多关于如何从 React Native Class 风格转换为 React Hooks 风格的细节 @ZiaUlRehmanMughal 第一个useEffect
挂钩在组件挂载时调用一次,因为一个空数组作为依赖项列表传递。第二个useEffect
钩子传递了一个清理函数,该函数在卸载时被调用。查看文档了解更多详情:reactjs.org/docs/hooks-reference.html#useeffect -
这是内存泄漏。您正在对已卸载的组件调用 setState
。我建议人们使用其他answer【参考方案4】:
由于isMounted()
已被正式弃用,您可以在您的组件中执行此操作:
componentDidMount()
this._ismounted = true;
componentWillUnmount()
this._ismounted = false;
这种维护自己的state
变量的模式在 ReactJS 文档中详细说明:isMounted is an Antipattern。
【讨论】:
感谢来自文档的提示...只需在 componentDidMount 中将 _isMounted 属性设置为 true 并在 componentWillUnmount 中将其设置为 false ...但是,使用 this.setState(mounted:错误的);可能为时已晚而无法阻止警告,因为状态更改不会立即发生 - 最好为此使用类属性 ... this.mounted = false - 谢谢,这为我指明了正确的方向 @danday74,是的,你是对的。当我写答案时,我可能错过了重点。如果有帮助,请考虑为答案投票 @KevinGhaboosi,不幸的是,只有提出这个问题的人才能接受这个答案【参考方案5】:你可以使用:
myComponent.updater.isMounted(myComponent)
"myComponent" 是你的反应组件的实例。 如果组件已安装,则返回“true”,如果未安装,则返回“false”..
这是不支持的方法。你最好取消订阅任何异步/事件 在 componentWillUnmount 上。【讨论】:
谢谢,这有助于我进行调试。【参考方案6】:相同的想法,但另一个实现
/**
* component with async action within
*
* @public
*/
class MyComponent extends Component
constructor ( ...args )
// do not forget about super =)
super(...args);
// NOTE correct store "setState"
let originSetState = this.setState.bind(this);
// NOTE override
this.setState = ( ...args ) => !this.isUnmounted&&originSetState(...args);
/**
* no necessary setup flag on component mount
* @public
*/
componentWillUnmount()
// NOTE setup flag
this.isUnmounted = true;
/**
*
* @public
*/
myCustomAsyncAction ()
// ... code
this.setState(any: 'data'); // do not care about component status
// ... code
render () /* ... */
【讨论】:
【参考方案7】:另一种解决方案是使用 Refs 。如果您使用的是 React 16.3+,请在渲染函数中对您的***项目进行引用。
然后简单地检查 ref.current 是否为空。
例子:
class MyClass extends React.Component
constructor(props)
super(props);
this.elementRef = React.createRef();
checkIfMounted()
return this.elementRef.current != null;
render()
return (
<div ref=this.elementRef />
);
【讨论】:
【参考方案8】:我来到这里是因为我正在寻找一种方法来阻止 polling
API。
react docs 确实涵盖了 websocket
案例,但不包括轮询案例。
我的工作方式
// React component
React.createClass(
poll ()
if (this.unmounted)
return
// otherwise, call the api
componentWillUnmount ()
this.unmounted = true
)
它有效。希望对你有帮助
如果你们知道任何失败的测试用例,请告诉我=]
【讨论】:
它可以工作,但是如果你有多个实例呢? all 将引用相同的allowPolling
是的,你最好不过在实例中,我将编辑代码 sn-p。【参考方案9】:
我认为 Shubham 的回答是 react 为需要转换代码以停止使用 isMounted
反模式的人们建议的解决方法。
这不一定是坏事,但值得列出这个问题的真正解决方案。
The article linked by Shubham 提供 2 条建议来避免这种反模式。您需要的取决于您在卸载组件时调用 setState 的原因。
如果您在组件中使用 Flux 存储,则必须在 componentWillUnmount 中取消订阅
class MyComponent extends React.Component
componentDidMount()
mydatastore.subscribe(this);
render()
...
componentWillUnmount()
mydatastore.unsubscribe(this);
如果您使用 ES6 Promise,您可能需要包装您的 Promise 以使其可取消。
const cancelablePromise = makeCancelable(
new Promise(r => component.setState(...))
);
cancelablePromise
.promise
.then(() => console.log('resolved'))
.catch((reason) => console.log('isCanceled', reason.isCanceled));
cancelablePromise.cancel(); // Cancel the promise
在链接文章中了解更多关于 makeCancelable
的信息。
总之,不要试图通过设置变量和检查组件是否已挂载来修补此问题,而是要找到问题的根源。如果您能提出任何其他常见案例,请发表评论。
【讨论】:
想象一个帖子列表。每个帖子都是一个单独的组件,并且旁边(内部)有一个删除按钮。当用户点击按钮时,post 设置isDeleting = true
(禁用按钮),告诉父组件删除帖子(使用通过 props 传递的回调),当他完成时(另一个回调,但到 post 组件)它可能需要启用按钮。例如。发生 HTTP 错误时(帖子尚未删除)。也就是说,post 组件可能需要根据它是否仍然挂载来更改状态。这有什么问题吗?
只是为了指出仍然来这里的人。参考文章中的makeCancelable
方法是设置一个局部变量——就像Shubham 的例子一样。没有办法真正取消 Promise。我很难理解为什么在其他地方设置一个布尔值比在 React 组件中清楚地设置它更好。不过,关于取消订阅的观点是有效的。
我的组件在对象的属性上设置了代理。当该对象更新时,代理会调用setState
,以便属性和组件的状态同步。所以在这种情况下,这些建议是无关紧要的。【参考方案10】:
我发现组件会被卸载,生成填充这个var
if(!this._calledComponentWillUnmount)this.setState(vars);
【讨论】:
这是一个内部属性,我认为与它们一起玩并不安全,因为它将来可能会改变。 好吧,老实说,在你更新库之前它是安全的,所以在某些情况下,这是一个很好的解决方案。但应该有一个条件!== undefined
以确保该属性存在。
当然,最好还是听从官方的建议。以上是关于有没有办法检查反应组件是不是已卸载?的主要内容,如果未能解决你的问题,请参考以下文章