I recently used hooks with React to fetch data from server but i'm facing a problem with hooks. The code seems correct but it look like the useEffect isn't called at first time but 3 seconds after with the setInterval. I have blank table for 3 seconds before it appear. I want to directly show the data and call it 3 seconds later.
What is the correct way to use it ?
const [datas, setDatas] = useState([] as any);
useEffect(() => {
const id = setInterval(() => {
const fetchData = async () => {
try {
const res = await fetch(URL);
const json = await res.json();
setDatas(jsonData(json));
} catch (error) {
console.log(error);
}
};
fetchData();
}, TIME)
return () => clearInterval(id);
}, [])
You need to invoke fetchData once initially outside the interval. Define fetchData outside the interval.
useEffect(() => {
// (1) define within effect callback scope
const fetchData = async () => {
try {
const res = await fetch(URL);
const json = await res.json();
setDatas(jsonData(json));
} catch (error) {
console.log(error);
}
};
const id = setInterval(() => {
fetchData(); // <-- (3) invoke in interval callback
}, TIME);
fetchData(); // <-- (2) invoke on mount
return () => clearInterval(id);
}, [])
With React Hooks:
const [seconds, setSeconds] = useState(0)
const interval = useRef(null)
useEffect(() => { if (seconds === 60) stopCounter() }, [seconds])
const startCounter = () => interval.current = setInterval(() => {
setSeconds(prevState => prevState + 1)
}, 1000)
const stopCounter = () => clearInterval(interval.current)
Related
I have to functions/const to get data from API:
const [isLoadingRoom, setLoadingRoom] = useState(true);
const [isLoadingLobby, setLoadingLobby] = useState(true);
const [rooms, setRooms] = useState([]);
const [lobbies, setLobbies] = useState([]);
const getRooms = async () => {
let isMounted = true;
async function fetchData() {
const response = await fetch(link);
const json = await response.json();
// 👇️ only update state if component is mounted
if (isMounted) {
setRooms(json);
setLoadingRoom(false);
}
}
fetchData();
return () => {
isMounted = false;
}
}
const getLobbies = async () => {
let isMounted = true;
async function fetchData() {
const response = await fetch(link);
const json = await response.json();
// 👇️ only update state if component is mounted
if (isMounted) {
setLobbies(json);
setLoadingLobby(false);
}
}
fetchData();
return () => {
isMounted = false;
}
}
useEffect(() => {
const roomInterval = setInterval(() => {
getRooms();
getLobbies();
}, 5000);
return () => clearInterval(roomInterval);
}, []);
The API gets data every 5 second, but after a while I get this message:
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
I have tried different approaches to fetch the API with const, functions, async etc. but I get this error message anyway.. Any tips?
useRef rather than normal variable:
const isMountedRef = useRef(true);
useEffect(() => {
const roomInterval = setInterval(() => {
getRooms();
getLobbies();
}, 5000);
return () => {
clearInterval(roomInterval);
isMountedRef.current = false;
};
}, []);
and change check conditions to
if(isMountedRef.current){
// execute setState
}
Hope it helps. feel free for doubts
I have received the boolean value and set to setNomStatus, but how can I check if that is true to show setShowCalender(true) ?
const [nomStatus, setNomStatus] = useState(false);
useEffect(() => {
const fetchData = async () => {
const email = localStorage.getItem("loginEmail");
try {
const res = await Axios.get(
"http://localhost:8000/service/activeStatus", {email}
);
setNomStatus(res.data[0].status);
console.log("Get status data :" + res.data[0].status);
if(nomStatus == true){
setShowCalender(true);
}
} catch (e) {
console.log(e);
}
};
fetchData();
}, []);
You can add another useEffect which watches this change, useEffect takes a second argument which is dependency array and the effect gets called if any of the dependency array value changes .
In this case since you need to make a decision based on the nomStatus, you can add it as a dependency to your useEffect
useEffect(() => {
if (nomStatus) {
setShowCalender(true);
}
}, [nomStatus]);
You can't since React state updates are asynchronously processed, the nomStatus state update won't be available until the next render cycle. Use the res.data[0].status value to set the showCalendar state.
const [nomStatus, setNomStatus] = useState(false);
useEffect(() => {
const fetchData = async () => {
const email = localStorage.getItem("loginEmail");
try {
const res = await Axios.get(
"http://localhost:8000/service/activeStatus",
{email}
);
setNomStatus(res.data[0].status);
console.log("Get status data :" + res.data[0].status);
if (res.data[0].status){
setShowCalender(true);
}
} catch (e) {
console.log(e);
}
};
fetchData();
}, []);
Or you can use a second useEffect hook with a dependency on nomStatus state update to set the showCalendar state.
useEffect(() => {
const fetchData = async () => {
const email = localStorage.getItem("loginEmail");
try {
const res = await Axios.get(
"http://localhost:8000/service/activeStatus",
{email}
);
setNomStatus(res.data[0].status);
console.log("Get status data :" + res.data[0].status);
} catch (e) {
console.log(e);
}
};
fetchData();
}, []);
useEffect(() => {
if (nomStatus){
setShowCalender(true);
}
}, [nomStatus]);
I'm new to react and I'm learning how to use useEffect. I encountered this warning in my react app. I tried out some solutions on SO but the warning still remains. Both fetchUser and fetchPosts trigger this warning. Can anyone enlighten me what is the problem and what does the warning mean?
App.js
useEffect(() => {
setLoading(true)
const getUser = async () => {
const userFromServer = await fetchUser()
if (userFromServer) {
setUser(userFromServer)
setLoading(false)
} else {
console.log("error")
}
}
getUser()
}, [userId])
useEffect(() => {
const getPosts = async () => {
const postsFromServer = await fetchPosts()
setPosts(postsFromServer)
}
getPosts()
}, [userId])
useEffect(() => {
const getUserList = async () => {
const userListFromServer = await fetchUserList()
setUserList(userListFromServer)
}
getUserList()
}, [])
// Fetch user
const fetchUser = async () => {
const res = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`)
const data = await res.json()
return data
}
// Fetch posts
const fetchPosts = async () => {
const res = await fetch(`https://jsonplaceholder.typicode.com/posts?userId=${userId}`)
const data = await res.json()
return data
}
// Fetch list of users
const fetchUserList = async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/users/')
const data = await res.json()
return data
}
If you are using any function or state which has been declared outside the useEffect then you need to pass it in the dependency array like this:
const someFunctionA = () => {
....
}
const someFunctionB = () => {
....
}
useEffect(() => {
....
}, [someFunctionA, someFunctionB])
You can read more about it here in case you want to know how it will be rendered: React useEffect - passing a function in the dependency array
I am trying to combine two json apis based on the id value. Is there a way I could achieve that?
Thanks. Below is my section of the code I have attempted so far:
const [data, setdata] = useState([])
const [runs, setruns] = useState([])
//get data from the first api
useEffect(() => {
const fetchData = async () => {
try {
const res = await axios.get('http://localhost:8000/tasks?format=json');
setdata(res.data['results']);
} catch (e) {
console.log(e)
}
}
fetchData();
}, []);
//map the rows of data from the api above to obtain values based on id value
useEffect(() => {
data.map(row => {
console.log(row.id)
const fetchRuns = async () => {
const res2 = await axios.get(`http://localhost:8000/task/${row.id}/runs`);
setruns(res2.data)
}
fetchRuns();
row.rundata = runs
console.log('row:', row)
})
}, []);
You can make the second request in the first useEffect as well and then store everything together
useEffect(() => {
const fetchData = async () => {
try {
const res = await axios.get('http://localhost:8000/tasks?format=json');
const arr = [];
res.data.result.map(row => {
arr.push(axios.get(`http://localhost:8000/task/${row.id}/runs`));
}
const res2 = await axios.all(arr);
setdata(); // here you will need to join both results, but to help you better we will need the structure of both
} catch (e) {
console.log(e)
}
}
fetchData();
}, []);
So if I understand correctly, you have first an API call that will provide you with a list of IDs and you need to populate (get the data) from those IDS based on a second API call.
You need to pass "data" in the dependencies of your second useEffect. This tells React "whenever 'data' changes, please do the following".
Also, you should set the data at the end of your loop or you'll end up changing it every iteration with 1 value!
Anyway, you should probably use the "for await" syntax as async logic is not easily compatible with .map.
const [data, setdata] = useState([])
const [runs, setruns] = useState([])
useEffect(() => {
const fetchData = async () => {
try {
const res = await axios.get('http://localhost:8000/tasks?format=json');
setdata(res.data['results']);
} catch (e) {
console.log(e)
}
}
fetchData();
}, []);
async function populate(data){
let populatedData = []
for await (let row of rows){
const res2 = await axios.get(`http://localhost:8000/task/${row.id}/runs`)
populatedData.push(res2.data)
}
setruns(populatedData)
}
useEffect(() => {
if (data.length === 0) return
populate(data)
},[data])
Let me know if it works!
i'm building a simple react app that fetches the data from the OpenWeather Api. i want to refresh the data received every minute to reflect the changes(if there are any changes) to the app. I tried using setInterval when i call the fetchApi function that i created, but according to the console log it doesn't sound very precise or realiable. This is my part of the code:
useEffect(() => {
const currentData = async () => {
const currentWeatherData = await fetchCurrentData();
setCurrentWeather(currentWeatherData);
};
const futureData = async () => {
setFutureWeather(await fetchFutureData())
console.log(futureWeather);
};
currentData();
futureData();
setInterval(() => {
currentData();
futureData();
console.log("reloaded!");
}, 60000);
}, []);
How can i improve this code to make it effectively work?
Thanks guys
I guess that you will need to clear the interval in the cleanup of the useEffect function.
useEffect(() => {
const currentData = async () => {
const currentWeatherData = await fetchCurrentData();
setCurrentWeather(currentWeatherData);
};
const futureData = async () => {
const futureWeatherData = await fetchFutureData();
setFutureWeather(futureWeatherData);
};
currentData();
futureData();
const intervalId = setInterval(() => {
currentData();
futureData();
}, 60000);
return () => {
clearInterval(intervalId);
};
}, []);