我每次运行时都会收到两次此错误。我进行了搜索并尝试在清理函数中取消 onAuthStateChanged()
到 useEffect()
的订阅,但仍然出现相同的错误。此外,uploadAndCreate()
函数运行良好,但 createPlan()
甚至在 createPlan()
完成之前就被调用,因此我得到了 undefined
的 fileUrl
。>
import React, { useState, useEffect } from "react";
import {
Image,
StyleSheet,
Text,
TextInput,
TouchableOpacity,
View,
} from "react-native";
import firebase from "firebase/app";
import "firebase/auth";
import "firebase/storage";
import { Link, Redirect } from "react-router-native";
import { getDocumentAsync } from "expo-document-picker";
import plus from "../../assets/plus.png";
import cross from "../../assets/cross.png";
const NewPlan = (props) => {
const [planName, setPlanName] = useState();
const [categoryName, setCategoryName] = useState();
const [isPlanCreated, setIsPlanCreated] = useState(false);
const [currUserId, setCurrUserId] = useState();
const [uploadProgress, setUploadProgress] = useState(0);
const [blob, setBlob] = useState({});
const [isPlanNameValid, setIsPlanNameValid] = useState(true);
const [ref, setRef] = useState();
const [fileUrl, setFileUrl] = useState();
useEffect(() => {
let mounted = true;
if (mounted) {
firebase.auth().onAuthStateChanged((user) => {
if (user) {
setCurrUserId(user.uid);
}
});
}
return () => {
mounted = false;
};
}, []);
const validate = () => {
if (planName) {
setIsPlanNameValid(true);
uploadAndCreate();
} else {
setIsPlanNameValid(false);
}
};
const uploadAndCreate = async () => {
if (Object.keys(blob).length !== 0) {
let task = ref.put(blob);
await task.on(
"state_changed",
(snapshot) => {
let progress =
(snapshot.bytesTransferred / snapshot.totalBytes) * 100;
if (firebase.storage.TaskState.RUNNING)
setUploadProgress(progress);
},
(error) => {
console.log("Error uploading file: " + error);
},
() => {
task.snapshot.ref.getDownloadURL().then((downloadURL) => {
setFileUrl(downloadURL);
});
}
);
}
createPlan();
setIsPlanCreated(true);
};
const createPlan = () => {
let plansRef = firebase.database().ref().child("plans");
let newPlanRef = plansRef.push();
newPlanRef.set({
plan_id: newPlanRef.key,
plan_name: planName,
created_by: currUserId,
project_id: props.match.params.id,
status: "active",
category: categoryName || "uncategorized",
file_url: fileUrl || "",
});
};
const handlePlanName = (_planName) => setPlanName(_planName);
return isPlanCreated ? (
<Redirect to={"/project/" + props.match.params.id} />
) : (
<View style={styles.container}>
<Link
to={"/project/" + props.match.params.id}
style={styles.crossContainer}
>
<Image source={cross} style={styles.cross} />
</Link>
<View style={styles.topArea}>
<Image source={plus} style={styles.plus} />
<Text style={styles.title}>New Plan</Text>
{!isPlanNameValid ? (
<Text style={styles.errorText}>
Please enter a valid name.
</Text>
) : null}
<TextInput
placeholder="Plan Name"
style={styles.input}
placeholderTextColor="#fff"
onChangeText={handlePlanName}
/>
{uploadProgress > 0 && uploadProgress < 100 ? (
<Text>Upload Progress: {uploadProgress.toFixed(0)}%</Text>
) : null}
{uploadProgress === 100 ? <Text>Upload Complete</Text> : null}
<TouchableOpacity
style={styles.button}
onPress={() => {
getDocumentAsync().then(async (response) => {
try {
let storageRef = firebase.storage().ref();
setRef(
storageRef.child(
response.uri.split("/")[14]
)
);
let fetchResponse = await fetch(response.uri);
let blob = await fetchResponse.blob();
setBlob(blob);
} catch (error) {
console.log(error.message);
}
});
}}
>
<Text style={styles.buttonText}>Upload Plan</Text>
</TouchableOpacity>
</View>
<TouchableOpacity
style={styles.bottomArea}
onPress={() => {
validate();
}}
>
<Text style={styles.createText}>Create</Text>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
// -- STYLES --
});
export default NewPlan;
答案 0 :(得分:3)
您的 mounted
变量没有任何作用。它唯一检查的地方是 useEffect
的开头,这里肯定是 true
。
虽然您可以在 onAuthStateChanged
回调中检查它:
firebase.auth().onAuthStateChanged((user) => {
if (mounted && user) {
setCurrUserId(user.uid);
}
});
最好使用 firebase 返回的取消订阅功能:
useEffect(() => firebase.auth().onAuthStateChanged((user) => {
if (user) {
setCurrUserId(user.uid);
}
}), []);
或者,脱糖:
useEffect(() => {
const unsubscribe = firebase.auth().onAuthStateChanged((user) => {
if (user) {
setCurrUserId(user.uid);
}
});
return unsubscribe;
}, []);
看起来您有多个异步操作可能会在组件卸载时进行,而这些操作实际上并不容易取消。您可以添加一个 ref 来指示组件当前是否已安装,并在调用任何 setter 函数之前进行检查:
const mountedRef = useRef(true);
useEffect(() => {
const unsubscribe = firebase.auth().onAuthStateChanged((user) => {
if (user) {
setCurrUserId(user.uid);
}
});
return () => {
mountedRef.current = false;
unsubscribe();
}
}, []);
然后,例如,代替
setBlob(blob);
做
if (mountedRef.current) {
setBlob(blob);
};
并在可能异步运行的 setter 的任何地方遵循相同的模式。
答案 1 :(得分:1)
我猜您想使用 onAuthStateChanged
函数的检查返回值。
useEffect(() => {
const unsubscribe = firebase.auth().onAuthStateChanged((user) => {
if (user) {
setCurrUserId(user.uid);
}
});
return unsubscribe;
}, []);
检查来自CertainPerformance的其他答案