有没有办法检查反应组件是否已卸载?

Dha*_*i S 55 javascript reactjs

我有一个用例,我需要卸载我的反应组件.但在某些情况下,特定的反应组分是由不同的功能卸载的.因此,我需要在卸载之前检查组件是否已安装.

Shu*_*tri 92

由于isMounted()正式弃用,您可以在组件中执行此操作:

componentDidMount() { 
  this._ismounted = true;
}

componentWillUnmount() {
   this._ismounted = false;
}
Run Code Online (Sandbox Code Playgroud)

stateReactJS文档中详细介绍了维护自己的变量的这种模式:https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html

  • 感谢文档中的提示......只需在componentDidMount中将_isMounted属性设置为true,并在componentWillUnmount中将其设置为false ...但是,使用this.setState({mounted:false}); 可能触发太晚以至于无法阻止警告,因为状态更改不会立即发生 - 最好只使用类属性... this.mounted = false - 谢谢你,这指向了我正确的方向 (3认同)
  • @ danday74,是的,你是对的。当我写答案时,我可能没有抓住要点。如果有帮助,请考虑对答案进行投票 (2认同)
  • @KevinGhaboosi,不幸的是只有提出问题的人才能接受这个作为答案 (2认同)

Nic*_*tti 22

我认为Shubham的回答是一种解决方法,建议人们需要转换代码以停止使用isMounted反模式.

这不一定是坏事,但值得列出这个问题的真正解决方案.

由Shubham联系的文章提供了2个避免这种反模式的建议.您需要的那个取决于卸载组件时调用setState的原因.

如果您在组件中使用Flux存储,则必须取消订阅componentWillUnmount

class MyComponent extends React.Component {
  componentDidMount() {
    mydatastore.subscribe(this);
  }
  render() {
    ...
  }
  componentWillUnmount() {
    mydatastore.unsubscribe(this);
  }
}
Run Code Online (Sandbox Code Playgroud)

如果您使用ES6承诺,您可能需要包装您的承诺以使其可取消.

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
Run Code Online (Sandbox Code Playgroud)

阅读makeCancelable链接文章中的更多信息.

总之,不要尝试通过设置变量并检查组件是否已挂载来修补此问题,转到问题的根源.如果您能想出任何其他常见案例,请发表评论.

  • 只是为了向仍然来到这里的人指出。引用的文章中的“makeCancelable”方法正在设置一个局部变量 - 就像 Shubham 的示例一样。没有办法真正取消 Promise。我很难理解为什么在其他地方设置布尔值会比在 React 组件中清楚地设置它更好。不过,关于取消订阅的观点是有效的。 (3认同)

Sag*_*ane 22

我建议您使用useRef钩子来跟踪组件是否已安装,因为无论何时更新状态,react 都会重新渲染整个组件,并且它还会触发 useEffect 或其他钩子的执行。

function MyComponent(props: Props) {
  const isMounted = useRef(false)

  useEffect(() => {
    isMounted.current = true;
    return () => { isMounted.current = false }
  }, []);

  return (...);
}

export default MyComponent;
Run Code Online (Sandbox Code Playgroud)


GWo*_*ing 13

使用@DerekSoike 回答,但是在我的情况下,useState用于控制挂载状态不起作用,因为状态在不需要时复活了

对我有用的是使用单个变量

myFunct在 a 中被调用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
  }
Run Code Online (Sandbox Code Playgroud)

这对我有用

  let stillMounted = { value: false }
  useEffect(() => {
    stillMounted.value = true
    return () => (stillMounted.value = false)
  }, [])

  const myFunct = () => {
    if (!stillMounted.value) return
    // change a state
  }
Run Code Online (Sandbox Code Playgroud)


Tar*_*aut 8

另一种解决方案是使用Refs.如果您使用的是React 16.3+,请在渲染函数中为您的顶级项目创建一个引用.

然后只需检查ref.current是否为null.

例:

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} />
    );
  }
}
Run Code Online (Sandbox Code Playgroud)


Der*_*ike 5

如果您使用钩子:

function MyComponent(props: Props) {
  const [isMounted, setIsMounted] = useState<boolean>(false);

  useEffect(() => {
    setIsMounted(true);
  }, []);

  useEffect(() => {
    return () => {
      setIsMounted(false);
    }
  }, []);

  return (...);
}

export default MyComponent;
Run Code Online (Sandbox Code Playgroud)