Issue
I'm trying to abstract my fetch out to a hook in my expo react native app. The fetch must be able to do POST method. I started out by trying to use and then modify the useHook() effect found at https://usehooks-ts.com/react-hook/use-fetch. It caused too many re-renders. So in searching Stack Overflow I found this article that recommended that I do it as a function. Since, I anticipate almost every fetch request is going to be a POST and done with a submit, that looked like a good idea. But it is still giving me too many re-renders. Then I found this article that said to do it with a useCallBack. Still no success...
Here is the hook I'm trying to use:
import { useCallback, useState } from "react";
import { FetchQuery } from "../interfaces/FetchQuery";
interface State<T> {
data?: T;
error: Error | string | null;
fetchData: any;
}
const useFetch2 = <T = unknown,>(): State<T> => {
const [data, setData] = useState<T>();
const [error, setError] = useState<Error | null>(null);
const fetchData = useCallback(async (query: FetchQuery) => {
const { url, options } = query;
if (!url) return;
const response = await fetch(url, options);
const data = await response.json();
if (data.error) {
setData(undefined);
setError(data.error);
} else {
setData(data);
setError(null);
}
}, []);
return { fetchData, data, error };
};
export default useFetch2;
This is the component calling the code (right now it assigns all of the data to the error field so I can validate the data coming back):
export default function AdminLogin() {
// screen controls
const [err, setErr] = useState<Error | string | undefined>();
// form controls
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
// fetch information
// const [fetchQuery, setFetchQuery] = useState<FetchQuery>({});
const { fetchData, data, error } = useFetch2<User>();
if (error) {
setErr(error);
}
if (data?.user) {
setErr(JSON.stringify(data.user));
}
const handleSignIn = async () => {
const options = {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"Cache-control": "no-cache",
},
body: JSON.stringify({ email, password }),
};
const url = BASE_API + "users/login";
// setFetchQuery({ url, options }); <-- attempted to use this with useEffect
fetchData({ url, options });
};
// useEffect(() => {
// fetchData();
// }, [fetchQuery]);
return (
<View style={styles.container}>
<Text style={styles.msg}>Maps can only be edited by Administers.</Text>
<View style={styles.controlGroup}>
<Text style={styles.UnPw}>Email</Text>
<TextInput
style={styles.txInput}
keyboardType="email-address"
placeholder="Email"
value={email}
onChangeText={(value) => setEmail(value)}
/>
</View>
<View style={styles.controlGroup}>
<Text style={styles.UnPw}>Password</Text>
<TextInput
style={styles.txInput}
maxLength={18}
placeholder="Password"
value={password}
onChangeText={(value) => setPassword(value)}
secureTextEntry
/>
</View>
<TouchableOpacity onPress={handleSignIn}>
<Text>Sign in</Text>
</TouchableOpacity>
{/* {status === "loading" && <Text>Loading...</Text>} */}
{err && (
<>
<Text style={styles.errText}>Error:</Text>
<Text style={styles.errText}>{err}</Text>
</>
)}
</View>
);
}
const styles = StyleSheet.create({});
Adding some console.log
commands does show that states are continually updating after the fetch command, but I don't understand why nor how to fix it.
Solution
You should not update state directly in the component. This may cause the state to be updated on every render, which forces re-render and creates an infinite render loop.
Instead, state should be updated only in callbacks or useEffect
.
Changes you have to make are convert following
if (error) {
setErr(error);
}
if (data?.user) {
setErr(JSON.stringify(data.user));
}
to
useEffect(() => {
if (error) {
setErr(error);
}
if (data?.user) {
setErr(JSON.stringify(data.user));
}
}, [error, data?.user])
Or, even better would be to remove err
state and convert it to const variable like
// remove following
// const [err, setErr] = useState<Error | string | undefined>();
// ..some code
const { fetchData, data, error } = useFetch2<User>();
// err is not required to be a state
const err = error || JSON.strigify(data?.user);
Answered By - Avinash Thakur
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.