How to synchronously get data using Axios? - reactjs

I'm trying to get data from Axios but while the data is being fetch the return statement gets executed, I think.
import axios from 'axios'
import React, { useState, useEffect} from 'react'
export default function Overall() {
const [post, setPost] = useState({
date:null,
total:null
});
useEffect(() => {
async function fetchData(){
await axios.get(`https://data.covid19india.org/v4/min/data.min.json`).then((response) => {
console.log("Inside Then block");
setPost({'total':Object.entries(response.data).map(([k,v])=>
// console.log(k,v.meta.last_updated,v.total.confirmed)
v.total.confirmed
).reduce((prev, curr)=>prev+curr,0)
})
setPost({'date':Object.entries(response.data)[0][1].meta.last_updated})
});
}
fetchData()
console.log("Data Fetched");
}, []);
console.log("Post: ",post);
return (
<div className='overall-container'>
<div>
<h2>India</h2>
</div>
<div>
<h2>Total: {post.total}</h2>
<h2>Date: {post.date}</h2>
</div>
</div>
)
}
The problem is that in return only {post.date} is working while {post.curr} is not. I don't know why? I'm new to react so I don't know much. I would appreciate if someone could explain this.

I'm assuming that you meant {post.total} instead of {post.curr}
You could change from:
setPost({'total':Object.entries(response.data).map(([k,v])=>
// console.log(k,v.meta.last_updated,v.total.confirmed)
v.total.confirmed
).reduce((prev, curr)=>prev+curr,0)
})
setPost({'date':Object.entries(response.data)[0][1].meta.last_updated})
To:
setPost({
total: Object.entries(response.data).map(([k,v])=>
// console.log(k,v.meta.last_updated,v.total.confirmed)
v.total.confirmed
).reduce((prev, curr)=>prev+curr,0),
date: Object.entries(response.data)[0][1].meta.last_updated
});
And it should work. However, maybe it'd be better to seperate date and total into two different states?

This is a bad practice to do async stuff in useEffect. You should create a separate function and call it from useEffect.
Your error is caused because you call setPost to update total and then once again to update date.
When you call setPost it will erase previous value for post. If you need to keep previous value you can do:
setPost((prev) => ({
...prev,
date: new Date()
}));
In your code you should call setPost only once
import React, { useState, useEffect} from 'react'
import axios from 'axios'
export default function Overall() {
const [post, setPost] = useState({});
const fetchData = async () => {
try {
const response = await axios.get(`https://data.covid19india.org/v4/min/data.min.json`)
// tranform your response and then set it to state at once
const total = 1 // replace with your total
const date = new Date() // replace with your date
setPost({ total, date })
} catch(err) {
console.log(err);
}
}
useEffect(() => {
fetchData()
}, []);
console.log("Post: ",post);
return (
<div className='overall-container'>
<div>
<h2>India</h2>
</div>
<div>
<h2>Total: {post?.total}</h2>
<h2>Date: {post?.date}</h2>
</div>
</div>
)
}

Related

React hook "useMemo" with array as dependency

I am new to react (that I use with typeScript) and I am facing an issue with the use of the useMemo hook.
Here is my fetching service:
export default class FetchingService {
datas: Data[] = [];
constructor() {
this.fetch();
}
async fetch(): Promise<Data[]> {
const d = // await an async array from an api, using Array.flat()
this.datas = d;
console.log(this.datas);
return d;
}
}
In a component, I try to watch for change of the datas attribute of my service:
import fetchingService from '../services/fetchingService.ts';
const Home: React.FC = () => {
const ds: Data[];
const [datas, setDatas] = useState(ds);
const fetchDatas = useMemo(() => {
console.log('Render datas', fetchingService.datas?.length)
setDatas(fetchingService.datas);
return fetchingService.datas;
}, [fetchingService.datas]);
return (
<ul>{datas.map(d => {
return (
<li key={d.id}>{d.id}</li>
);
</ul>
);
}
The problem I am facing is that the useMemo hook is not recompouted when the datas attribute changes within my fetchService. I am pretty sure that my FetchingService.fetch() function works because the console.log within the fetch function always display the fetched datas.
The observed behavior is that sometimes datas are well rendered (when fetch ends before rendering ...), but sometimes it isn't.
The expected one is that datas are rendered every time and only on refresh, exept when datas are modified
I also tried to put the length of the data array as a dependency in useMemo, but in both cases it doesn't work and I have a warning in my IDE, telling me it is an unnecessary dependency.
I don't really understand if it is a typescript or a specific react behavior issue. I think the reference of the datas attribute should change at the end of the fetch (or at least its length attribute ...), but tell me if I am wrong.
I do appreciate every help !
in fetchingService, when datas change, probably the dependency cannot be accepted. You can use a custom hook in stead of it.
You can use this source about useMemo: useMemo with an array dependency?
import { useState, useLayoutEffect, useCallback } from "react";
export const useFetchingService = () => {
const [fetchedData, setFetchedData] = useState([]);
const fetch = useCallback(async () => {
const d = await new Promise((res, rej) => {
setTimeout(() => {
res([1, 2, 3]);
}, 5000);
}); // await an async array from an api, using Array.flat()
setFetchedData(d);
}, []);
useLayoutEffect(() => {
fetch();
}, []);
return [fetchedData];
};
useLayoutEffect runs before rendering
using:
const [fetchData] = useFetchingService();
const fetchDatas = useMemo(async () => {
console.log("Render datas", fetchData.length);
setDatas(fetchData);
return fetchData;
}, [fetchData]);
You can also use this directly without 'datas' state.
I hope that this will be solution for you.
So I put together a codesandbox project that uses a context to store the value:
App.tsx
import React, { useState, useEffect, createContext } from "react";
import Home from "./Home";
export const DataContext = createContext({});
export default function App(props) {
const [data, setData] = useState([]);
useEffect(() => {
const get = async () => {
const d = await fetch("https://dummyjson.com/products");
const json = await d.json();
const products = json.products;
console.log(data.slice(0, 3));
setData(products);
return products;
};
get();
}, []);
return (
<div>
Some stuff here
<DataContext.Provider value={{ data, setData }}>
<Home />
</DataContext.Provider>
</div>
);
}
Home.tsx
import React, { FC, useMemo, useState, useEffect, useContext } from "react";
import { DataContext } from "./App";
import { Data, ContextDataType } from "./types";
const Home: FC = () => {
const { data, setData }: ContextDataType = useContext(DataContext);
return (
<>
<ul>
{data.map((d) => {
return (
<li key={d.id}>
{d.title}
<img
src={d.images[0]}
width="100"
height="100"
alt={d.description}
/>
</li>
);
})}
</ul>
</>
);
};
export default Home;
This was my first time using both codesandbox and typescript so I apologize for any mistakes

Repeat Fetching with UseEffect and SetTimeOut on React.js

I want to make Component that Fetching Data from REST API with repeatedly.
I noticed There is some problem with setInterval and React.js.
so I coded recursive way with setTimeout and useEffect.
It seems work well though, I am curious about necessity of clearTimeout.
Is there no performance Issue cause of no clearTimeout or else?
ps: Sorry for my poor English.
import { useEffect, useState } from "react";
import axios from "axios";
function RepeatFetch() {
const [data, setData] = useState(null);
const repeatFetchData = async () => {
try {
const response = await axios.get(
"https://api.foo.com/"
);
setData(response.data);
} catch (e) {
}
setTimeout(repeatFetchData, 3000);
};
useEffect(() => {
responseFetchData();
// return () => clearTimeout(timer); // Do I need clearTimeout? then, how?
}, []);
return (
<div>
{data}
</div>
);
}
export default RepeatFetch;

useEffect and Axios getting a null json

I built a react code using useEffect()and Axios to get a json from an URL, however I'm getting an empty array, probably because it is not async function. Here is my code:
import axios from "axios";
import { useEffect, useState } from "react";
export function UseVacation (){
const[vacations, setVacations] = useState([]);
useEffect(() =>{
axios.get('APILINK').then( async (res) =>{
setVacations(res.data);
console.log("vactest: "+ JSON.stringify(vacations))
}).catch((err)=>{
alert("Error extracting the data from API: "+err);
})
}, [])
return (
<div>
{vacations.map( (vac) => {
<h1>{vac._id}</h1>
})}
</div>
)
}
Any idea on this?
Why dont you console res.data instead of vacations and see what it has ? Also check the network tab, and see what actual request has in the response
I've changed my return and it fixed issue. Why? I don't know, but is working:
return (
<div>
{vacations.map( (vac) => {
return(
<h1>{vac?._id}</h1>
)
})}
</div>
)
Have you tried the following?
return (
<div>
{vacations && vacations.map( (vac) => {
<h1>{vac._id}</h1>
})}
</div>
)
I have good suggestion for you:
const[vacations, setVacations] = useState([]);
useEffect(() => {
const getApi = async () => {
try {
const res = await axios.get('APILINK')
setVacations(res.data);
} catch(error) {
console.log(error)
}
}
getApi()
}, [])
console.log("vactest: "+ vacations)
I cannot explain but if you set state in useEffect and direct log state in useEffect, it only log previous state

Cannot read property 'map' of undefined on useState value

I'm new to react, I'm getting this error constantly and after google some I can't find the reason why the useState value can't be read as array :( ... this the error I'm getting: 'TypeError: team.map is not a function'
import React, { useEffect, useState } from "react";
const SportTeams = () => {
const [team, setTeam] = useState([]);
useEffect(() => {
const getSports = async () => {
const response = await fetch("https://www.thesportsdb.com/api/v1/json/1/all_sports.php");
const data = await response.json();
setTeam(data);
console.log(data);
}
getSports();
}, []);
return (
<div className="myClass">
<ul>
{team.map((sport, index) => {
return <li key={`${sport.strSport}-${index}`}>{sport.strSport}</li>
})}
</ul>
</div>
);
};
export default SportTeams;
Just update setTeam like following, your error will be resolved.
setTeam(data.sports);
It is because you are setting the team state with the data without checking if its undefined. If the data is undefined your state team become undefined as well. So make sure to check the data.
import React, { useEffect, useState } from "react";
const SportTeams = () => {
const [team, setTeam] = useState([]);
useEffect(() => {
const getSports = async () => {
const response = await fetch("https://www.thesportsdb.com/api/v1/json/1/all_sports.php");
if (response) {
const data = await response.json();
if (data) {
setTeam(data);
}
}
console.log(data);
}
getSports();
}, []);
return (
<div className="myClass">
<ul>
{team.map((sport, index) => {
return <li key={`${sport.strSport}-${index}`}>{sport.strSport}</li>
})}
</ul>
</div>
);
};
export default SportTeams;
There might also be the chance that your response is not what you expected and the actual data might be inside your response. In that case you need check what your response first then proceed to set the data.
As I said in my comment. the value you are setting to teams isn't an array.
const data = await response.json();
setTeam(data.sports);

useState not updating for some reason?

when I try to get some data from my backend API using axios, and set the state after I've gotten the result for some reason the state is not updated and when I try to use the state it will only show me an empty array. but what's so interesting is that when I console.log(res.data) it will show me my array of lists with no problem, so I guess the problem is with the setCategories() state function. What am I doing wrong?
const Home = (props) => {
const [categories, setCategories] = useState([]);
useEffect(() => {
getCats();
}, []);
const getCats = async () => {
const data = await axios.get(`${myUrl}/allItems`, {
withCredentials: true,
});
const cats = await data.data;
console.log(cats); //this one works perfectly
setCategories(cats);
console.log(categories) //this one doesn'nt work which means the setState didn't work
};
return (
<>
<div className="card-div mt-5">
{categories.map((cat) => {
<li>{cat.name}</li>;
})}
</div>
</>
);
};
the state is set asynchronously, so the data is not updated instantly. that's why you are not getting the output on console.log(categories) right after setCategories(cats);
here is a small example of asynchronous behaviour of useState state update:
Link to working example: stackblitz
import React, { useEffect, useState } from "react";
import "./style.css";
import axios from "axios";
const url = "https://jsonplaceholder.typicode.com/users";
export default function App() {
const [users, setUsers] = useState([]);
useEffect(() => {
axios.get(url).then(result => {
console.log("1. when data is fetched sucessfully: ", result.data);
setUsers(result.data);
console.log("2. Just after setting state: ", users);
});
}, []);
// secons useEffect for logging out upadated todos useState
useEffect(() => {
console.log("todos upadated: ", users);
}, [users]);
return (
<div>
<h1>Hello StackBlitz!</h1>
<p>Start editing to see some magic happen :)</p>
{users.map(user => (
<p>{user.name}</p>
))}
</div>
);
}
Here is what is happening in the above example:
You can see the flow of data fetching and async update of state.
The useState function is asynchronous, so you will never get the new state in the same function, the best way is to use it in another function or useEffect.
Example:
useEffect(() => {
console.log(categories);
}, [categories]);

Resources