本文介绍了如何取消对 componentWillUnmount 的提取的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我认为标题说明了一切.每次卸载仍在获取的组件时都会显示黄色警告.

I think the title says it all. The yellow warning is displayed every time I unmount a component that is still fetching.

警告:无法在未安装的组件上调用 setState(或 forceUpdate).这是一个空操作,但是......要修复,请取消 componentWillUnmount 方法中的所有订阅和异步任务.

  constructor(props){
    super(props);
    this.state = {
      isLoading: true,
      dataSource: [{
        name: 'loading...',
        id: 'loading',
      }]
    }
  }

  componentDidMount(){
    return fetch('LINK HERE')
      .then((response) => response.json())
      .then((responseJson) => {
        this.setState({
          isLoading: false,
          dataSource: responseJson,
        }, function(){
        });
      })
      .catch((error) =>{
        console.error(error);
      });
  }

推荐答案

当您触发 Promise 时,它​​可能需要几秒钟才能解决,到那时用户可能已经导航到您应用中的另一个位置.因此,当 Promise resolves setState 在卸载的组件上执行时,您会收到错误 - 就像您的情况一样.这也可能导致内存泄漏.

When you fire a Promise it might take a few seconds before it resolves and by that time user might have navigated to another place in your app. So when Promise resolves setState is executed on unmounted component and you get an error - just like in your case. This may also cause memory leaks.

这就是为什么最好将一些异步逻辑移出组件的原因.

否则,您将需要以某种方式取消您的承诺.或者 - 作为最后的手段(这是一种反模式) - 您可以保留一个变量来检查组件是否仍然安装:

Otherwise, you will need to somehow cancel your Promise. Alternatively - as a last resort technique (it's an antipattern) - you can keep a variable to check whether the component is still mounted:

componentDidMount(){
  this.mounted = true;

  this.props.fetchData().then((response) => {
    if(this.mounted) {
      this.setState({ data: response })
    }
  })
}

componentWillUnmount(){
  this.mounted = false;
}

我要再次强调——这个是一种反模式但在您的情况下可能就足够了(就像他们对 Formik 实现).

I will stress that again - this is an antipattern but may be sufficient in your case (just like they did with Formik implementation).

GitHub 上的类似讨论

这可能是我如何使用 Hooks 解决同样的问题(除了 React 什么都没有)一个>:

This is probably how would I solve the same problem (having nothing but React) with Hooks:

选项 A:

import React, { useState, useEffect } from "react";

export default function Page() {
  const value = usePromise("https://something.com/api/");
  return (
    <p>{value ? value : "fetching data..."}</p>
  );
}

function usePromise(url) {
  const [value, setState] = useState(null);

  useEffect(() => {
    let isMounted = true; // track whether component is mounted

    request.get(url)
      .then(result => {
        if (isMounted) {
          setState(result);
        }
      });

    return () => {
      // clean up
      isMounted = false;
    };
  }, []); // only on "didMount"

  return value;
}

选项 B: 或者与 useRef 一起使用,它的行为类似于类的静态属性,这意味着它不会在值更改时重新渲染组件:

OPTION B: Alternatively with useRef which behaves like a static property of a class which means it doesn't make component rerender when it's value changes:

function usePromise2(url) {
  const isMounted = React.useRef(true)
  const [value, setState] = useState(null);


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

  useEffect(() => {
    request.get(url)
      .then(result => {
        if (isMounted.current) {
          setState(result);
        }
      });
  }, []);

  return value;
}

// or extract it to custom hook:
function useIsMounted() {
  const isMounted = React.useRef(true)

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

  return isMounted; // returning "isMounted.current" wouldn't work because we would return unmutable primitive
}

示例:https://codesandbox.io/s/86n1wq2z8

这篇关于如何取消对 componentWillUnmount 的提取的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-29 03:20