【发布时间】:2017-02-07 14:42:46
【问题描述】:
我有一个用例需要卸载我的反应组件。但在某些情况下,特定的 react 组件会被不同的函数卸载。 因此,我需要在卸载之前检查组件是否已安装。
【问题讨论】:
标签: javascript reactjs
我有一个用例需要卸载我的反应组件。但在某些情况下,特定的 react 组件会被不同的函数卸载。 因此,我需要在卸载之前检查组件是否已安装。
【问题讨论】:
标签: javascript reactjs
建议您使用 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) ... 挂载
【讨论】:
useState而不是“不”?因为您的解决方案似乎使用它
使用@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
}
【讨论】:
如果你使用钩子:
function MyComponent(props: Props) {
const [isMounted, setIsMounted] = useState<boolean>(false);
useEffect(() => {
setIsMounted(true);
}, []);
useEffect(() => {
return () => {
setIsMounted(false);
}
}, []);
return (...);
}
export default MyComponent;
【讨论】:
useEffect 挂钩在组件挂载时调用一次,因为一个空数组作为依赖项列表传递。第二个useEffect 钩子传递了一个清理函数,该函数在卸载时被调用。查看文档了解更多详情:reactjs.org/docs/hooks-reference.html#useeffect -
setState。我建议人们使用其他answer
由于isMounted() 已被正式弃用,您可以在您的组件中执行此操作:
componentDidMount() {
this._ismounted = true;
}
componentWillUnmount() {
this._ismounted = false;
}
这种维护自己的state 变量的模式在 ReactJS 文档中详细说明:isMounted is an Antipattern。
【讨论】:
你可以使用:
myComponent.updater.isMounted(myComponent)
"myComponent" 是你的反应组件的实例。 如果组件已安装,则返回“true”,如果未安装,则返回“false”..
【讨论】:
相同的想法,但另一个实现
/**
* 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 () { /* ... */ }
}
【讨论】:
另一种解决方案是使用 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} />
);
}
}
【讨论】:
我来到这里是因为我正在寻找一种方法来阻止 polling API。
react docs 确实涵盖了 websocket 案例,但不包括轮询案例。
我的工作方式
// React component
React.createClass({
poll () {
if (this.unmounted) {
return
}
// otherwise, call the api
}
componentWillUnmount () {
this.unmounted = true
}
})
它有效。希望对你有帮助
如果你们知道任何失败的测试用例,请告诉我=]
【讨论】:
allowPolling
我认为 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 的信息。
总之,不要试图通过设置变量和检查组件是否已挂载来修补此问题,而是要找到问题的根源。如果您能提出任何其他常见案例,请发表评论。
【讨论】:
isDeleting = true(禁用按钮),告诉父组件删除帖子(使用通过 props 传递的回调),当他完成时(另一个回调,但到 post 组件)它可能需要启用按钮。例如。发生 HTTP 错误时(帖子尚未删除)。也就是说,post 组件可能需要根据它是否仍然挂载来更改状态。这有什么问题吗?
makeCancelable 方法是设置一个局部变量——就像Shubham 的例子一样。没有办法真正取消 Promise。我很难理解为什么在其他地方设置一个布尔值比在 React 组件中清楚地设置它更好。不过,关于取消订阅的观点是有效的。
setState,以便属性和组件的状态同步。所以在这种情况下,这些建议是无关紧要的。
我发现组件会被卸载,生成填充这个var
if(!this._calledComponentWillUnmount)this.setState({vars});
【讨论】:
!== undefined 以确保该属性存在。