【问题标题】:React state update memory leak due to an unmounted component由于未安装的组件,React 状态更新内存泄漏
【发布时间】:2021-03-29 17:36:07
【问题描述】:

我的应用使用 firebase 进行身份验证。在登录过程中,我收到“无法对未安装的组件执行 React 状态更新”,它建议在 useEffect 中使用清理功能。我以为我正在使用

清理异步函数中的函数
finally {
      setLoading(false);
    }

任何帮助将不胜感激。代码如下:

import React, { useState, useContext } from "react";
import styled from "styled-components/native";
import { Image, Text, StyleSheet } from "react-native";

import { FirebaseContext } from "../context/FirebaseContext";
import { UserContext } from "../context/UserContext";

export default function SignInScreen() {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [loading, setLoading] = useState(false);
  const firebase = useContext(FirebaseContext);
  const [_, setUser] = useContext(UserContext);

  const signIn = async () => {
    setLoading(true);
    try {
      await firebase.signIn(email, password);
      const uid = firebase.getCurrentUser().uid;
      const userInfo = await firebase.getUserInfo(uid);
      const emailArr = userInfo.email.split("@");
      setUser({
        username: emailArr[0],
        email: userInfo.email,
        uid,
        isLoggedIn: true,
      });
    } catch (error) {
      alert(error.message);
    } finally {
      setLoading(false);
    }
  };

  return (
    <Container>
      <Main>
        <Text style={styles.welcomeText}>Welcome</Text>
      </Main>

      <Auth>
        <AuthContainer>
          <AuthTitle>Email Address</AuthTitle>
          <AuthField
            autoCapitalize="none"
            autoCompleteType="email"
            autoCorrect={false}
            autoFocus={true}
            keyboardType="email-address"
            onChangeText={(email) => setEmail(email.trim())}
            value={email}
          />
        </AuthContainer>

        <AuthContainer>
          <AuthTitle>Password</AuthTitle>
          <AuthField
            autoCapitalize="none"
            autoCompleteType="password"
            autoCorrect={false}
            autoFocus={true}
            secureTextEntry={true}
            onChangeText={(password) => setPassword(password.trim())}
            value={password}
          />
        </AuthContainer>
      </Auth>

      <SignInContainer onPress={signIn} disabled={loading}>
        {loading ? <Loading /> : <Text style={styles.text}>Sign In</Text>}
      </SignInContainer>
      <HeaderGraphic>
        <Image
          source={require("../images/heritage-films-logo.png")}
          style={{ height: 150, width: 300, resizeMode: "contain" }}
        />
      </HeaderGraphic>
    </Container>
  );
}

【问题讨论】:

  • 您是否尝试在组件卸载期间添加该语句? useEffect(()=&gt;{ return () =&gt; {setLoading(false)}},[])
  • 我刚刚尝试添加您的 useEffect 并仅返回并且错误仍然存​​在。是否需要在return语句之前将函数signIn移到useEffect中?

标签: reactjs react-native async-await


【解决方案1】:

您应该在以某种方式调用 setState 之前检查组件是否仍然挂载。这是一个典型的 React 泄漏问题。您可以使用 useRef 钩子实现 isMounted 变量,尽管 React 的作者称其为反模式,因为您应该在组件卸载时取消异步例程。

function Component() {
    const isMounted = React.useRef(true);

    React.useEffect(() => () => (isMounted.current = false), []);

      const signIn = async () => {
        setLoading(true);
        try {
          await firebase.signIn(email, password);
          const uid = firebase.getCurrentUser().uid;
          const userInfo = await firebase.getUserInfo(uid);
          const emailArr = userInfo.email.split("@");
          isMounted.current && setUser({
            username: emailArr[0],
            email: userInfo.email,
            uid,
            isLoggedIn: true,
          });
        } catch (error) {
          alert(error.message);
        } finally {
          isMounted.current && setLoading(false);
        }
   };
}

或者另一种有点神奇的方式:

import { useAsyncCallback, E_REASON_UNMOUNTED } from "use-async-effect2";
import { CanceledError } from "c-promise2";

export default function SignInScreen() {
  //...

  const signIn = useAsyncCallback(function*() {
    setLoading(true);
    try {
      yield firebase.signIn(email, password);
      const uid = firebase.getCurrentUser().uid;
      const userInfo = yield firebase.getUserInfo(uid);
      const emailArr = userInfo.email.split("@");
      setUser({
        username: emailArr[0],
        email: userInfo.email,
        uid,
        isLoggedIn: true,
      });
      setLoading(false);
    } catch (error) {
      CanceledError.rethrow(error, E_REASON_UNMOUNTED);
      setLoading(false);
      alert(error.message);
    }
  }, []);

  return (<YourJSX onPress={signIn}>);
}

【讨论】:

    猜你喜欢
    • 2020-01-24
    • 2021-06-29
    • 2021-11-25
    • 2017-10-24
    • 2023-03-03
    • 2020-03-28
    • 2023-03-19
    • 2019-12-31
    • 1970-01-01
    相关资源
    最近更新 更多