【问题标题】:React useEffect warning: Can't perform a React state update on an unmounted component causes switch statement to render wrong componentReact useEffect 警告:无法在未安装的组件上执行 React 状态更新导致 switch 语句呈现错误的组件
【发布时间】:2020-10-25 04:00:03
【问题描述】:

我构建了一个确认组件来显示来自我的服务器的用户是否通过注册注册的信息:

将通过 react 显示的结果是:

a) 通过点击我发送的确认电子邮件中的链接成功验证了他们的帐户

b) 帐户已经过验证

c) 无法找到此令牌的用户

乍一看,我认为它有效,但我注意到两件我认为与错误有关的事情:

一个是错误:

警告:无法对未安装的组件执行 React 状态更新。 这是一个空操作,但它表明您的应用程序中存在内存泄漏。 要修复,请取消 useEffect 中的所有订阅和异步任务 清理功能。

此外,尽管使用了 switch 语句,但还是会挂载否定组件而不是成功组件?我正在使用 break 关键字,但不知何故,组件的渲染失败了。

这里是组件:

export function Confirmation({
  match,
  setError,
  setResponseMessage,
  resetUserAcoountVerified,
  userHasBeenVerified,
  error,
  responseMessage,
  accountNotVerified
}) {
  // var [mountedComponent, setMountedComponent] = useState(false);
  useEffect(() => {
    let isSubscribed = true;
    axios
      .get(`/users/confirmation/${match.params.token}`)
      .then(response => {
        if (isSubscribed) {
          if (response.status === 200) {
            isSubscribed ? setResponseMessage(response.data.msg) : null;
          }
        }
      })
      .catch(function(error) {
        if (error.response.status === 404) {
          resetUserAcoountVerified();
          setError(true);
          isSubscribed ? setResponseMessage(error.response.data.msg) : null;
        }
        if (error.response.status === 400) {
          userHasBeenVerified();
          setError(true);

          isSubscribed ? setResponseMessage(error.response.data.msg) : null;
        }
      });
    return () => (isSubscribed = false);
  }, []);

  const isNull = value => typeof value === 'object' && !value;

  console.log('error ', error);

  console.log('accountNotVerified ', accountNotVerified);

  let response;
  switch (error) {
    case accountNotVerified == true && error === false:
      response = <Message positive header={responseMessage[0]} />;
      break;
    case accountNotVerified == false && error == true:
      response = <Message negative header={responseMessage[0]} />;
      break;
    case isNull(accountNotVerified) && error:
      response = <Message negative header={responseMessage[0]} />;
  }

  return <>{response}</>;

}

提前谢谢你!

更新

这是我基于 Evert 和 Peter 的善意贡献和洞察力得出的结论:

function isConfirmation() {
    var [showApi, setShowApi] = useState(true);

    function conirmationCall() {
      return axios
        .get(`/users/confirmation/${match.params.token}`)
        .then(response => {
          if (response.status === 200) {
            return response;
          }
        })
        .catch(function(error) {
          if (error.response.status === 404) {
            resetUserAcoountVerified();
            setError(true);
            setResponseMessage(error.response.data.msg);
            return error;
          }
          if (error.response.status === 400) {
            userHasBeenVerified();
            setError(true);
            setResponseMessage(error.response.data.msg);
            return error;
          }
        });
    }

    useEffect(() => {
      var mounted = true;

      conirmationCall()
        .then(status => {
          () => setShowApi(prev => !prev);
          console.log('status', status);
        })
        .catch(err => console.log(err));
      return function() {
        mounted = false;
      };
    }, []);

    if (error) {
      return showApi && <Message negative header={responseMessage[0]} />;
    }
    return showApi && <Message positive header={responseMessage[0]} />;
  }

【问题讨论】:

  • 哦,谢谢你,Sunil!我得去看看!

标签: reactjs axios use-effect


【解决方案1】:

这不是 switch 的工作方式。 Switch 将 1 个值与 1 个或多个其他值进行比较。

所以这个:

  switch (error) {
    case accountNotVerified == true && error === false:
      response = <Message positive header={responseMessage[0]} />;
      break;
    case accountNotVerified == false && error == true:
      response = <Message negative header={responseMessage[0]} />;
      break;
    case isNull(accountNotVerified) && error:
      response = <Message negative header={responseMessage[0]} />;
  }

变成这样:

  switch (error) {
    case true:
      response = <Message positive header={responseMessage[0]} />;
      break;
    case false:
      response = <Message negative header={responseMessage[0]} />;
      break;
    case true:
      response = <Message negative header={responseMessage[0]} />;
  }

因此,您将error 与真和/或假(松散地)进行比较。

我建议您重新阅读switch 声明中的文档,但在您的情况下,我认为我建议不要使用switch 而只是返回。

    if (accountNotVerified && !error) {
      return <Message positive header={responseMessage[0]} />;
    }
    if (!accountNotVerified && error) {
      return <Message negative header={responseMessage[0]} />;
    }
    return <Message negative header={responseMessage[0]} />;

但再看一遍,我认为这可以进一步简化:

    if (error) {
      return <Message negative header={responseMessage[0]} />;
    }
    return <Message positive header={responseMessage[0]} />;

【讨论】:

  • 谢谢 Evert 我想我会用这个逻辑进行渲染!更容易阅读。我有把事情复杂化的习惯!!!
【解决方案2】:

我认为你的问题是async api调用,当响应返回时,组件可能是unmounted。这种情况是setError()(并且您不能在未安装的组件上设置任何状态。也没有setError())。
我建议您定义一个新变量let mounted = true;,并检查组件是否为mounted。我看到你有一个变量,到底在做什么。你可以使用let isSubscribed = true;

export function Confirmation({
  match,
  setError,
  setResponseMessage,
  resetUserAcoountVerified,
  userHasBeenVerified,
  error,
  responseMessage,
  accountNotVerified
}) {
  // var [mountedComponent, setMountedComponent] = useState(false);
  useEffect(() => {
    let isSubscribed = true;
    let mounted = true;
    axios
      .get(`/users/confirmation/${match.params.token}`)
      .then(response => {
        if (isSubscribed && mounted) {
          if (response.status === 200) {
            isSubscribed ? setResponseMessage(response.data.msg) : null;
          }
        }
      })
      .catch(function(error) {
        if (error.response.status === 404 && mounted) {
          resetUserAcoountVerified();
          setError(true);
          isSubscribed ? setResponseMessage(error.response.data.msg) : null;
        }
        if (error.response.status === 400 && mounted) {
          userHasBeenVerified();
          setError(true);

          isSubscribed ? setResponseMessage(error.response.data.msg) : null;
        }
      });
    return () => {
      isSubscribed = false;
      mounted = false;
    }
  }, []);

  const isNull = value => typeof value === 'object' && !value;

  console.log('error ', error);

  console.log('accountNotVerified ', accountNotVerified);

  let response;
  switch (error) {
    case accountNotVerified == true && error === false:
      response = <Message positive header={responseMessage[0]} />;
      break;
    case accountNotVerified == false && error == true:
      response = <Message negative header={responseMessage[0]} />;
      break;
    case isNull(accountNotVerified) && error:
      response = <Message negative header={responseMessage[0]} />;
  }

  return <>{response}</>;

【讨论】:

  • 感谢彼得的帮助。在阅读了您的逻辑后,我在想“啊,是的,这是有道理的”但是我仍然在想。 index.js:1 Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function. 令人费解!
  • 您可以检查其他组件吗?我已经为您创建了一个沙箱,您可以在其中检查它是如何工作的:https://codesandbox.io/s/interesting-tereshkova-5bjbv?file=/src/App.js 如果您在 fakeCall 完成之前切换组件,有和没有安装检查,您会看到差异。
  • 我已经在第29行评论// remove this check for test purpose
  • 忘记清除unmount 上的useEffectreturn () =&gt; (mounted = false);。现在你可以检查它了
  • 或者你应该在mounted检查之后设置thencatch中的状态。不在conirmationCall
猜你喜欢
  • 2021-12-03
  • 2019-07-24
  • 2020-08-03
  • 1970-01-01
  • 2023-03-06
  • 1970-01-01
  • 2021-03-27
  • 2019-09-11
  • 2021-08-07
相关资源
最近更新 更多