Refresh data received from an API every minute React, Javascript - javascript

I am working on a little project that involves getting the weather at different locations and also, dynamically getting the time at those locations afterwards. All these worked fine but then i tried to take it a step further by attempting to refresh the data gotten from the API every minute(without resubmitting/pressing the enter key).
I've tried various ways of implementing the setinterval function into the code but none seem to work. Here is what the code looks like:
function App() {
const [query, setQuery] = useState("");
const [weather, setWeather] = useState({});
const [timeZone, setTimeZone] = useState({});
const handleChange = (e) => {
setQuery(e.target.value);
};
const apiCall = () => {
Axios({
method: "get",
url: `${api.timeBase}apiKey=${api.timeKey}&location=${query}`,
timeout: 10000,
})
.then((res) => {
console.log(res.data);
setWeather(res.data);
//setQuery("");
})
.catch((err) => {
console.log(err);
});
};
const handleSubmit = (e) => {
e.preventDefault();
apiCall();
};
useEffect(() => {
setInterval(apiCall, 60000);
}, []);
useEffect(() => {
if (weather.main !== undefined) {
Axios.get(
`${api.timeBase}apiKey=${api.timeKey}&location=${weather.name}, ${weather.sys.country}`
)
.then((res) => {
console.log(res.data);
setTimeZone(res.data);
})
.catch((err) => {
console.log(err);
});
}
}, [weather]);
Basically, the issue i get the most is that somewhere within the setinterval function, the queryparameter is cleared and the API continues to retrieve a different location till gets back to normal. i tried preventing the query parameter from clearing after it updates the state, that didnt help.
PS: i had to breakdown the handlesubmit because attaching the setinterval to the original block of code throws an error cause e.preventDefault isn't defined. Also, I think calling the setInterval without the useEffect makes things a lot worse too.

Clear out the interval after the interval is finished. The setInterval function returns a timerId that should be cleared out otherwise, it remains in the timer pool and can execute even after the instance of the callback has been run.
React.useEffect(() => {
const id = setInterval(apiCall, 60000);
return () => clearInterval(id);
}, []);

Related

useEffect fetch request is pulling data twice

What I Want:
I'm pulling data from an api, then setting the data to state. I've done this inside a useEffect hook, but when I console.log the data afterwards, it's displaying the data twice, sometimes 4 times. I'm at a loss as to why this is happening.
What I've Tried:
console.log within useEffect to see data from source
disabling react developer tools within chrome.
My Code:
// make api call, assign response to data state
const [apiData, setApiData] = useState();
useEffect(() => {
async function fetchData() {
try {
await fetch('https://restcountries.com/v3.1/all')
.then(response => response.json())
.then(data => setApiData(data));
} catch (e) {
console.error('Error fetching api data', e);
};
};
fetchData();
}, []);
console.log(apiData);
Result of console.log:
As was mentioned in the other comment this is due to effects being "double invoked" in strict-mode.
A common solution and one I believe has been suggested by the React team (although am struggling to find where I read this) is to use useRef.
// make api call, assign response to data state
const [apiData, setApiData] = useState();
const hasFetchedData = useRef(false);
useEffect(() => {
async function fetchData() {
try {
await fetch('https://restcountries.com/v3.1/all')
.then(response => response.json())
.then(data => setApiData(data));
} catch (e) {
console.error('Error fetching api data', e);
};
};
if (hasFetchedData.current === false) {
fetchData();
hasFetchedData.current = true;
}
}, []);
If you are using React version 18+, StrictMode has a behavior change. Before it wasn't running effects twice, now it does to check for bugs. ( Actually, it does mount-remount, which causes initial render effects to fire twice)
https://reactjs.org/blog/2022/03/29/react-v18.html#new-strict-mode-behaviors

Async function in useEffect doesn't execute only when i'm rereshing the page

I'm new in react! and I tried to search for a solution but unfortunately, I didn't have an answer.
I'm trying to get profile data from an async function in useEffect, the first call of the component always doesn't show the data (it shows only the interface without the data ) and also doesn't show "loaded" in the console which I use it for testing, as the code shows. however, when I refresh the page all the data is loaded successfully
here's the code :
const [profile, setProfile] = useState({});
useEffect(() => {
async function fetchData() {
try {
/*some code to get the instance and account used is the function getProfile */
const response = await instance.methods.getProfile(account[0]).call();
setProfile(response.profile);
} catch (error) {
alert(
`Failed`,
);
console.error(error);
}
}
fetchData().then(()=>{ console.log('Loaded!') });
});
what should I do ! if there is no solution !
how can refresh the component one it loaded ? or something like that
first, you need to useEffect dependencylist to only run after page render and every rerender you don't give dependencylist it will execute useEffect everytime when state update, the problem is when you first load page there is no data to show what why it's showing nothing because API need time to get data so you can show loader when API is making a call
const [profile, setProfile] = useState({});
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchData() {
try {
setLoading(true)
/*some code to get the instance and account used is the function getProfile */
const response = await instance.methods.getProfile(account[0]).call();
setProfile(response.profile);
} catch (error) {
console.error(error);
} finally {
setLoading(false)
}
}
fetchData();
},[]);
return loading ? <div>Loading....</div> : now show your component here

How to send a GET request with SetTimeout and get data if it is not ready yet?

On Submit click I send a POST request with some data to the server and in response I get an object with id and timeout. Then I need set timeout and when time comes send a GET request for the data. The problem is that data is not ready yet and I get undefined and my React app crashes.
I was told that timeout should be from the first request only (I mean I can't mannualy increase it or do something like this: timeout * 2, I need to use timeout from the first request only). How can I do that? I think it can be done somehow with While loop ...but I'm not smart enough to write this code. Please help
const [someData, setSomeData] = useState({}) // here comes undefined and app crashes because this object renders in DOM
const getData = async (id) => {
const response = await fetch(`$BASE_URL/${id}`)
setSomeData(response)
}
const onSubmit = async (data) => {
const { id, timeout } = await fetch(url, data)
setTimeOut(() => {
getData(id) // data is not ready and I get undefined
}, timeout) // its around 1000ms and I can't change it mannually
}
If I do this then everything works fine
const getData = async (id) => {
const response = await fetch(`$BASE_URL/${id}`)
setSomeData(response)
}
const onSubmit = async (data) => {
const { id, timeout } = await fetch(url, data)
setTimeOut(() => {
getData(id)
}, 6000) // if I manually specify timeout to 6000
}
fetch will return a promises, you could use then to getData.
const onSubmit = async (data) => {
fetch(url, data)
.then(res => return res.json())
.then(res => getData(res.id))
}

my useEffect is reaching callstack and after text change, i don't know why?

i want to fire a callback after a text change, basically this is for search. My code:
const fetchMovies = useCallback(async () => {
console.log('fetchMovies api ');
const {Search} = await fetch(
`http://www.omdbapi.com/?apikey=${apiKey}&s=${text}&page=${page}`,
).then(data => data.json());
console.log('movies', Search);
return Search;
}, [page, text]);
useEffect(() => {
console.log('useEffect!');
if (timeout) {
clearTimeout(timeout);
}
if (text) {
const newTimeout = setTimeout(async () => {
dispatch(fetchMoviesRequest('fetch'));
console.log('fetch!1');
try {
const moviesResult = await fetchMovies();
console.log('fetch!2', moviesResult);
dispatch(fetchMoviesSuccess(moviesResult));
} catch (fetchError) {
console.log('fetch!3e', fetchError);
dispatch(fetchMoviesFailure(fetchError));
}
}, 2000);
dispatch(onSetTimeout(newTimeout));
}
return () => {
clearTimeout(timeout);
};
}, [fetchMovies, text, timeout, page]);
somehow i cannot figure out what causes it to rerender so much when it's supposed to rerender only after text change? i can only type 1 letter and app crashes with error of max call stack?
I'm not sure what the values of your other variables are in your useEffect dependency array, but what looks suspicious to me is that you have timeout as one of the dependencies.
I'm going on a hunch and say that this line onSetTimeout(newTimeout) will change the value of the timeout variable which will re-trigger this useEffect. This will create an infinite loop because the effect will run every time timeout changes.
Have you tried changing your useEffect's dependency list to only [text]?
I'm not too sure, but I think that'll only call the function if text changes.

React- why useEffect called many times (even with condition)?

I work on a simple chat application with react hooks. In the second useEffect I need to append the new message to the rest of of them so I will be able to display all of them in the chat. Now i able to append the message only after the useEffect is being called x2 from the array's length. For example: in the forth message the UseEffect will be execute 8 times before the array will be complete.
notice: in the useEffect I setAllMessages twice but only one is being execute which is fine depends if it is a reciever of sender (so I dont think this the problem)
function Chat() {
const [message, setMessage] = useState("");
const [userName] = useState(
JSON.parse(atob(localStorage.getItem("token").split(".")[1])).name
);
const [userTyping, setUserTyping] = useState(null);
const [allMessages, setAllMessages] = useState([]);
useEffect(() => {
socket.emit("join", userName);
socket.on("chat-message", data => {
toast(`Hello ${data}`);
});
socket.on("user-joined", data => {
toast(`${data} joined the chat`);
});
}, [userName]);
useEffect(() => { // the problem is here
function handleAllMessages(data) {
setAllMessages([...allMessages, data]);
console.log(allMessages);
}
socket.on("broadcast-message", data => {
handleAllMessages(data);
});
socket.on("my-message", data => {
data["userName"] = "You";
handleAllMessages(data);
});
}, [allMessages]);
useEffect(() => {
socket.on("who-typing", data => {
setUserTyping(data);
setTimeout(() => {
setUserTyping(null);
}, 2500);
});
}, [userTyping]);
useEffect(() => {
socket.on("user-disconnected", data => {
toast(`${data} left the chat`);
});
}, []);
function handleChat(e) {
e.preventDefault();
if (message.trim() === "") return toast.warn("not valid message");
socket.emit("user-message", message, userName);
setMessage("");
}
function handleChange(e) {
setMessage(e.target.value);
socket.emit("typing", userName);
}
return (...
The reason while the second useEffect will be executed multiple times -in face I suppose infinitely-, is because you are telling it to execute each time allMessages change, since it's in the Array -the second argument for useEffect.
Passing that array with allMessages in it will cause the useEffect to re-run every time allMessages change, which is actually what you are doing inside the useEffect when you are calling the handleAllMessages function.
Read about this.
In hooks the right way to setAllMessages (in this case) is to pass the oldArray like this...
setAllMessages(allMessages => [...allMessages, data]);
and inside the use effect pass empty array as a second argument instead [allMessages]
this fix the problem

Categories