Update array using useState - arrays

I'm calling an API inside the useEffect hook and trying to update my state covidData but my array remains empty even after calling the setData function:
const [covidData, setData] = useState([]);
useEffect(() => {
async function getGlobalData() {
let response = await fetch('https://api.covid19api.com/summary');
let jsonResponse = await response.json();
const globalData = jsonResponse.Global;
//setting covidData
setData([...covidData, {recovered: globalData.TotalRecovered}])
console.log(covidData); //covidData is empty
}
getGlobalData()
}, [])
what am I doing wrong?

Your code is correct, the state is updated asynchronously, it's normal your console.log will not display your state after your setState.

It is how react works, when you change state of something, react creates new instance of virtual dom.
So when you change state of covidData the value you set will be in new instance and the console.log is still in that old instance so it logs old value which is empty as you set when using useState.
Try logging with button onClick event and you will see your data or you can check with React Dev Tools
Also you can refactor your code as
useEffect(async () => {
let response = await fetch('https://api.covid19api.com/summary');
let jsonResponse = await response.json();
const globalData = jsonResponse.Global;
//setting covidData
setData([...covidData, {recovered: globalData.TotalRecovered}])
}, [])
More on virtual dom
React Docs
What is virtual DOM

Related

usestate not setting the data

i am using usestate for transfer data. but ufotunately it not quite work.
here is my code:
const [totCons, settotCons] = useState(null)
useEffect(() => {
// declare the async data fetching function
const fetchData = async () => {
// get the data from the api
const data = await fetch('https://piscons2.vercel.app/ConsPiscTotCons');
// convert the data to json
const json = await data.json();
// set state with the result
settotCons(json);
console.log(json)
console.log(totCons)
}
// call the function
fetchData()
// make sure to catch any error
.catch(console.error);;
}, [])
as you can see on image the json return data but the totCons return null.
i did set it settotCons(json)
Updated state will not be available to the state value immedieately.
The react setState is asynchronous, but thats not the only reason for this behaviour. The reason is a closure scope around an immutable const value.
Both props and state are assumed to be unchanging during 1 render.
Treat this.state as if it were immutable.
You can use useEffect to create the sideeffects for totCons
useEffect(() => {
// action on update of totCons
}, [totCons]);
try doing console.log(totCons) outside useEffect.
you will not get the updated value in next line.
you will get the updated value in next render

setState not working inside useEffect hook

I fetched some data from my firebase realtime database which is returned as an object inside my useEffect hook function. I wanted to map these data into different rows in my UI. But the problem is when I try to setState in the component by passing the fetched data in the setState() function, it returns an empty object. I tried to convert the fetched data object to convert to an array and then set the state, still, the console.log(state) shows an empty array. When I use the state as a dependency (2nd argument in the useEffect) it returns an infinite loop of the state. The code looks like this-
const ToDoList = () => {
const [toDo, setToDo] = useState([])
useEffect(() => {
const fetchData = async () => {
try{
toDoListRef.on('value', snapshot => {
const fetchedPostObject = snapshot.val()
console.log(fetchedPostObject) // shows fetched data in the object form
var fetchedPostArr = []
fetchedPostArr = Object.entries(fetchedPostObject)
setToDo(fetchedPostArr)
console.log(toDo) // []
})
}catch(err){
console.log(err)
}
}
fetchData()
}, [])
}
if I don't convert the fetched object to an array then it returns an empty object if again I use the dependency, it returns an infinite state.
You will not get the updated value just after the setState as it is async. Instead, you can try something like this
useEffect (() => {console.log(toDo)},[toDo])
This hook listens to any updated value of toDo and will execute when the value of toDo gets updated.

Not awaiting for data in useEffect

I have a useEffect in my component that is waiting for data from the context so that it can set it in state. But its not waiting for the state and is moving on to the next line of code to set isLoading to false.
I'd like it to wait for the data so that I can render the loading.
I tried setting the isFetchingData in the context but I had run into problems where if another component calls it first it would set the isFetchingData state to false.
First call to ReactContext is setting the isLoading sate to false
It is fine for results to come back with no records. The component would render 'No records found'. Therefore, I cannot check the length on state to say if length is zero then keep loading.
Following is my code:
Context
const [activeEmployees, setActiveEmployees] = useState([]);
const [terminatedEmployees, setTerminatedEmployees] = useState([]);
useEffect(() => {
getEmployees()
.then(response => {
/// some code...
setActiveEmployees(response.activeEmployees)
setTerminatedEmployees(response.terminatedEmployees)
});
});
Component
const EmployeesTab = () => {
const { activeEmployees, terminatedEmployees } = useContext(BlipContext);
//Component states
const [isFetchingData, setIsFetchingData] = useState(true);
const [newEmployees, setNewEmployees] = useState([]);
const [oldEmployees, setOldEmployees] = useState([]);
useEffect(() => {
async function getData() {
await setNewEmployees(activeEmployees);
await setOldEmployees(terminatedEmployees);
setIsFetchingData(false);
}
getData();
}, [activeEmployees, terminatedEmployees, isFetchingData]);
if(isFetchingData) {
return <p>'Loading'</p>;
}
return (
// if data is loaded render this
);
};
export default EmployeesTab;
Since you have useState inside your useContext, I don't see the point of storing yet again the activeEmployees in another state.
If you want to have a local loading variable it could something like:
const loading = !(activeEmployees.length && terminatedEmployees.length);
This loading will update whenever getEmployees changes.
And to answer you question, the reason await is not having an effect is because setState is synchronous.

Howcome my state is not updating using react hooks and use Effect

My useEffect function is trying to fetch data from an API endpoint. The results resultAllTestTypes are currently logging fine.
However, I can't find out why the allTestTypes are coming back as undefined as I thought I had already set it in a state variable it should be able to log it to the console. But when I log the allTestTypes data it gives me this.
Code:
const [allTestTypes, setAllTestTypes] = useState([])
useEffect(() => {
async function onLoadCreateUnitTests() {
const results = await get('get_tables_autocomplete/b', user.user)
const resultsAllTestTypes = await get('get_all_test_types', user.user)
autoComplete.setTablesAutoComplete(results)
setAllTestTypes(resultsAllTestTypes)
console.log('resultAllTestTypes data ',resultsAllTestTypes.data);
console.log('allTestTypes data ',allTestTypes.data);
}
onLoadCreateUnitTests()
It's setting the state, you just have a console.log in a spot that's not particularly useful.
allTestTypes is a local const. It will never change, and that's not what setAllTestTypes is trying to do. When you set state, this tells react to render the component again. When that render occurs, you'll make a new call to useState, which will return the new value and assign it to a new local const. That new variable can be interacted with by code in the new render, but code from the previous render (such as your console.log) will never see the new value.
If you'd like to verify that the component is rerendering with a new value, move your console.log into the body of the component:
const [allTestTypes, setAllTestTypes] = useState([])
console.log('Rendering with', allTestTypes);
useEffect(() => {
async function onLoadCreateUnitTests() {
const results = await get('get_tables_autocomplete/b', user.user)
const resultsAllTestTypes = await get('get_all_test_types', user.user)
autoComplete.setTablesAutoComplete(results)
setAllTestTypes(resultsAllTestTypes)
}
onLoadCreateUnitTests()
});
cuz setAllTestTypes is async, so u can't get it immediately.
if u want to use it ,use the local variable resultsAllTestTypes instead

UseEffect hook executes multiple times on updating state

I am updating state of parent component from child component's useEffect hook. Follwing is the piece of code from child component. Here useEffect is getting called twice. Not sure how can I avoid it.
useEffect = () => { const flag = someApi; setStateOfParent(flag),[]}
This below code will fix the issue:
useEffect(() => {
// executed only once
}, [])
You should definitely avoid setting state inside useEffect because it will trigger a new render. Also, if you specify your effect dependencies you will have better control over it and assure that it will only get executed when one of those dependencies change.
You do not set state in useEffect. Make a function to get data from API, call it in useEffect and setState in that function. You can check a an example code below.
useEffect(() => {
getImage();
}, []); // Calling getImage function to fetch data
const [items, set] = useState([]);
async function getImage() {
try {
const response = await axios.get("https://picsum.photos/v2/list");
let image = response.data;
for (var i in image) {
let x = "https://picsum.photos/id/";
let y = image[i].id;
let v = image[i].width;
image[i].author = `${x}${y}/${v}/${image[i].height}`;
image[i].height = Math.floor(Math.random() * 650) + 300;
let z = image[i].height;
image[i].download_url = `url(${x}${y}/${v}/${z})`;
}
set(image); // setting state after fetching data
} catch (error) {
console.error(error);
}
}

Resources