I'm following a mern course. As the guy fetches movie data from backend to react app. But when he logout from homepage, his console showed many error. While mine shows none. What can be the reason to it? As it will cause me huge trouble in future if my console doesn't show such errors. The errors are of memory leaks as he didn't use cleanup function for fetchMovies() function.
image of his console
function TopRatedMovies() {
const { updateNotification } = useNotification();
const [movies, setMovies] = useState([]);
const fetchMovies = async () => {
const { movies, error } = await getTopRatedMovies();
if (error) return updateNotification("error", error);
setMovies([...movies]);
};
useEffect(() => {
fetchMovies();
}, []);
return <MovieList movies={movies} title="Viewers Choice (Movies)" />;
}
export const getTopRatedMovies = async (type) => {
try {
let endpoint = "/movie/top-rated";
if (type) endpoint = endpoint + "?type=" + type;
const { data } = await client(endpoint);
return data;
} catch (error) {
console.log(error);
const { response } = error;
return { error: response?.data.msg };
}
};
You can try to wrap fetchMovies by useCallback and add dependencyfetchMovies for useEffect :
function TopRatedMovies() {
const { updateNotification } = useNotification();
const [movies, setMovies] = useState([]);
const fetchMovies = useCallback(async () => {
const { movies, error } = await getTopRatedMovies();
if (error) return updateNotification("error", error);
setMovies([...movies]);
}, [])
useEffect(() => {
fetchMovies();
}, [fetchMovies]);
return <MovieList movies={movies} title="Viewers Choice (Movies)" />;
}
If still the error, can you show your useNotification hook?
Related
I'm trying to understand how useEffect works.
I have two callApi: "callApiDialer" is based on response of "callApiManager", for get id from list.
But "currentLeadId" state at first called obviously is null.
How can call "callApiDialer" when currentLeadId is not null?
import React, { useState, useEffect } from 'react';
const [loading, setLoading] = useState(true);
const [apiManager, setApiManager] = useState([]);
const [apiDialer, setApiDialer] = useState([]);
const [currentLeadId, setCurrentLeadId] = useState(null);
// CALL API
const callApiManager = async () => {
try {
const response = await api.get(`/api/manager/op/1`);
setCurrentLeadId(response.data.dialer_list[0].id);
setApiManager(response.data);
} catch (err) {
alert("fetchApiManager " + err.response.status);
}
}
const callApiDialer = async () => {
try {
const response = await api.get(`/api/manager/lead/${currentLeadId}`);
setApiDialer(response.data.lead);
setLoadingModal(false);
} catch (err) {
alert("fetchApiSources " + err.response.status);
}
}
useEffect(() => {
callApiManager();
}, [])
useEffect(() => {
console.log(currentLeadId); // <-- here get first null and after currentLeadId
if(currentLeadId) {
callApiDialer();
setLoading(false);
}
}, [currentLeadId])
You could have just one function that call both, therefore there would be only one useEffect.
// CALL API
const callBothApisAtOnce= async () => {
try {
const op = await api.get(`/api/manager/op/1`);
const response = await api.get(`/api/manager/lead/${op.data.dialer_list[0].id}`);
// rest of your logic...
} catch (err) {
alert("err" + err);
}
}
useEffect(() => {
callBothApisAtOnce()
}, [])
you can use axios's promise base functionality
axios.get(`/api/manager/op/1`).then(res => {
setCurrentLeadId(response.data.dialer_list[0].id);
setApiManager(response.data);
axios.get(`/api/manager/lead/${response.data.dialer_list[0].id}`).then(res1 =>{
setApiDialer(res1.data.lead);
setLoadingModal(false);
}
}
const API_URL = 'http://www.omdbapi.com/?i=tt3896198&apikey=caca2ca6';
const App = () => {
const searchMovies = async (title) => {
const response = await fetch(`${API_URL}&s=${title}`);
const data = await response.json();
console.log(data.Search);
}
useEffect=(() => {
searchMovies('Batman');
}, []);
return(
<h1>Apppi</h1>
);
}
export default App;
the code above should show the movies title in the api on console, the tutorial im watching has title showed up. Can someone solve this for me? Thanks
there is syntax error in useEffct
remove = between useEffect and parentheses
useEffect(() => {
searchMovies('Batman');
}, []);
please always call the API in try-catch, this way your app will not crash and you may log error and you have to remove the = in useEffect before paranthesis.
const API_URL = 'http://www.omdbapi.com/?i=tt3896198&apikey=caca2ca6';
const App = () => {
const searchMovies = async (title) => {
try {
const response = await fetch(`${API_URL}&s=${title}`);
const data = await response.json();
console.log(data.Search);
} catch (error) {
console.log("error", error)
}
}
useEffect(() => {
searchMovies('Batman');
}, []);
return(
<h1>Apppi</h1>
);
}
export default App;
I would like to fetch data when the user changes.
To do this I have a useEffect that triggers when the user changes, which calls a function to get the data.
The problem is that the useEffect is called too often because it has a dependency on getData and getData changes because it both uses and sets loading.
Are there ways around this, while still retaining getData as a function, as I call it elsewhere.
const getData = useCallback(async () => {
if (!loading) {
try {
setLoading(true);
const { error, data } = await getDataHook();
if (error) {
throw new Error("blah!");
}
} catch (error) {
const message = getErrorMessage(error);
setErrorMessage(message);
setLoading(false);
}
}
}, [loading]);
...
useEffect(() => {
const callGetData = async () => {
await getData();
};
callGetData();
}, [user, getData]);
Try moving loading from useCallback to useEffect. Something like this:
const getData = useCallback(async () => {
try {
const { error, data } = await getDataHook();
if (error) {
throw new Error("blah!");
}
} catch (error) {
const message = getErrorMessage(error);
setErrorMessage(message);
}
}, []);
...
useEffect(() => {
const callGetData = async () => {
await getData();
};
if (!loading) {
setLoading(true);
callGetData();
setLoading(false);
}
}, [user, getData, loading]);
The loading flag is something that the call sets, and shouldn't be effected by it, so remove it from the useEffect(), and getData() functions.
const getData = useCallback(async () => {
try {
setLoading(true);
const { error, data } = await getDataHook();
if (error) {
throw new Error("blah!");
}
} catch (error) {
const message = getErrorMessage(error);
setErrorMessage(message);
} finally {
setLoading(false); // not related, but this would remove loading after an error as well
}
}, []);
useEffect(() => {
const callGetData = async () => {
await getData(user);
};
callGetData();
}, [user, getData]);
I'm currently trying to understand how to work with async/await in React JS. In this demo app, I'm trying to get full border names of the chosen country by calling https://restcountries.eu/. I make first API call to get info about country and the second one to get full name of its borders since first API call returns
only short border names. I believe there is a way to combine those calls inside one useEffect however everything I tried gave me some sort of an error or getting stuck in infinite loop. What is the proper way to combine those calls with async/await approach?
import React, { useState, useEffect } from "react";
import Axios from "axios";
const App = () => {
const [loading, setLoading] = useState(true);
const [country, setCountry] = useState({});
const [fullBorderNames, setFullBorderNames] = useState([]);
//FIRST API CALL
useEffect(() => {
const source = Axios.CancelToken.source();
const fetchData = async () => {
setLoading(true);
try {
const response = await Axios(
`https://restcountries.eu/rest/v2/name/canada?fullText=true`,
{ cancelToken: source.token }
);
setCountry(response.data[0]);
} catch (err) {
if (Axios.isCancel(err)) {
console.log("Axios canceled");
} else {
console.log(err);
}
}
};
fetchData();
return () => source.cancel();
}, []);
//SECOND API CALL
useEffect(() => {
const source = Axios.CancelToken.source();
let borders = [];
if (country.borders) {
const fetchData = async () => {
try {
country.borders.forEach(async border => {
const response = await Axios(
`https://restcountries.eu/rest/v2/alpha?codes=${border}`,
{ cancelToken: source.token }
);
borders.push(response.data[0].name);
if (borders.length === country.borders.length)
setFullBorderNames(borders);
});
} catch (err) {
if (Axios.isCancel(err)) {
console.log("Axios canceled");
} else {
console.log(err);
}
}
setLoading(false);
};
fetchData();
}
return () => source.cancel();
}, [country.borders]);
if (loading) {
return <h2>Loading</h2>;
} else {
return (
<>
<pre>{JSON.stringify(country, null, 2)}</pre>
<pre>{JSON.stringify(fullBorderNames, null, 2)}</pre>
</>
);
}
};
export default App;
You can simply just make the requests right after the first one.
try {
const response = await Axios(`https://restcountries.eu/rest/v2/name/canada?
fullText=true`, { cancelToken: source.token });
const country = response.data[0];
setCountry(country);
/* all the other fetch calls*/
Can you tell me what kind of errors you get because I don't see an issue with doing them in the same useEffect? It just gets a little messy which can be refactored anyway.
I am executing useEffect() to update a state with JSON data. However the fetch request sometimes fails, so I want to re-execute the useEffect hook if that happens:
...
import React, {useState, useEffect} from 'react';
import {getJsonData} from './getJsonData';
const myApp = () => {
var ErrorFetchedChecker = false;
const [isLoading,setIsLoading] = useState(true);
const [data,setData] = useState(null);
const updateState = jsonData => {
setIsloading(false);
setData(jsonData);
};
useEffect(() => {
//console.log('EXECUTING');
getJsonData().then(
data => updateState(data),
error => {
Alert.alert('DATA FETCHING ERROR !', 'Refreshing ?...');
ErrorFetchedChecker = !ErrorFetchedChecker;
//console.log('LOG__FROM_CountriesTable: Executed');
},
);
}, [ErrorFetchedChecker]);//Shouldn't the change on this variable
//be enough to re-execute the hook ?
return (
<View>
<Text>{state.data.title}</Text>
<Text>{data.data.completed}</Text>
</View>
);
}
Here's the getJsonData() function just in case:
export async function getJsonData() {
try {
let response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
let responseJson = await response.json();
return responseJson;
} catch (error) {
throw error;
// Also, is this the correct way to handle the error ?
// As the Alert in useEffect goes off either ways.
// If not, advise me on how the error should be handled.
}
}
This will work
const myApp = () => {
const [errorFetchedChecker, setErrorFetchedChecker] = useState(false);
const [isLoading,setIsLoading] = useState(true);
const [data,setData] = useState(null);
const updateState = jsonData => {
setIsloading(false);
setData(jsonData);
};
useEffect(() => {
//console.log('EXECUTING');
getJsonData().then(
data => updateState(data),
error => {
Alert.alert('DATA FETCHING ERROR !', 'Refreshing ?...');
setErrorFetchedChecker(c => !c);
//console.log('LOG__FROM_CountriesTable: Executed');
},
);
}, [errorFetchedChecker]);
return (
<View>
<Text>{state.data.title}</Text>
<Text>{data.data.completed}</Text>
</View>
);
}
import React, { useState, useRef, useEffect } from "react";
import { Text, View, TextInput } from "react-native";
const App = () => {
var ErrorFetchedChecker = false;
const [isLoading, setIsLoading] = useState(true);
const [data, setData] = useState(null);
const updateState = (jsonData) => {
setIsLoading(false);
setData(jsonData);
};
useEffect(() => {
//console.log('EXECUTING');
getJsonData()
.then((data) => {
console.log("1. Successful, just received the data from our promise");
updateState(data);
console.log("2. We set our data because we received it successfully");
return { alreadySet: true };
})
.catch((e) => {
console.log("1. We failed to gather data in our initial promise");
console.log("2. Attempting to rerun initial promise");
return getJsonData();
})
.then((data) => {
if (data.alreadySet) {
console.log(
"3. Did not attempt to retry because we are already successful"
);
} else {
console.log("3. Our second attempt succeeded");
updateState(data);
console.log("4. Set our data on our second attempt");
}
})
.catch((e) => {
console.log("3. Both attempts have failed");
});
}, []); //Shouldn't the change on this variable
//be enough to re-execute the hook ?
return (
<View>
<Text>{data ? <Text>{data.title}</Text> : null}</Text>
</View>
);
};
export async function getJsonData() {
try {
let response = await fetch("https://jsonplaceholder.typicode.com/todos/1");
let responseJson = await response.json();
return responseJson;
} catch (error) {
throw error;
// Also, is this the correct way to handle the error ?
// As the Alert in useEffect goes off either ways.
// If not, advise me on how the error should be handled.
}
}
export default App;