我的应用程序使用 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>
);
}
答案 0 :(得分:2)
在以某种方式调用 setState 之前,您应该检查组件是否仍处于挂载状态。这是一个典型的 React 泄漏问题。您可以使用 isMounted
钩子实现 useRef
变量,尽管 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}>);
}