useState setter not updating state when called in useEffect - reactjs

Pretty much what it says on the title. When I console.log(repos) it returns an empty array. Why is the repos state not updating?
import React, { useEffect, useState } from "react";
import axios from "axios";
export default () => {
const [repos, setRepos] = useState([]);
useEffect(() => {
(async () => {
try {
let repo_lists = await axios.get(
"https://api.github.com/users/Coddielam/repos"
// { params: { sort: "created" } }
);
setRepos(repo_lists.data.slice(1).slice(-10));
console.log(repo_lists.data.slice(1).slice(-10));
console.log(repos);
} catch (err) {
setRepos([
"Something went wrong while fetching GitHub Api./nPlease use the link below to view my git repos instead ",
]);
}
})();
}, []);
return (
<div className="content">
<h2>View my recent git repos:</h2>
<ul>
...
</ul>
</div>
);
};

Answer is very simple. Your useState is updating .. believe me. The reason why you don't see it when you console.log() is because SetRespos is an asynchronous function.
Basically when you declare a function to update you useState value, react will use it as an async function
EXAMPLE
const [example, setExample] = useState('');
useEffect(() => {
setExample('Hello');
console.log('I'm coming first'); // This will be executed first
console.log(example); // This will come after this
}, [])
The output will be :
I'm coming first
// Blank Line
But still your useState will update after this. If you want to see that do this :
useEffect(() => {
console.log(respose); // This will give you the value
}, [respos])
I'm using a separate useEffect to console.log() the value. In the [] (dependency array) we pass respos which simply means that the useEffect will run every time the value of respos changes.
Read more about useStates and useEffects in react's documentation

State updates are async. You will only see them reflected on the next render. If you console log the state immediately after calling setState it will always log the current state, not the future state.
You can log the state in an effect every time it changes and you will see it changing:
useEffect(() => console.log(repos), [repos]);
This effect will be called after the state update has been applied.

Related

React useState is initially unchanged

Take this piece of code:
import React from 'react';
import { useState, useEffect } from 'react'
export function App() {
let [isAdmin, setAdmin] = useState(false)
const checkIfAdmin = async() => {
setAdmin(true)
}
useEffect(() => {
checkIfAdmin()
}, []);
console.log(isAdmin)
return (
<div className='App'>
<h1>test</h1>
</div>
);
}
When console logging isAdmin, it comes out as false initially, but when checked again (such as in an onClick() event), it comes out as true. Why does it take 2 checks to finally output the desired result? How can I make it so that in checkIfAdmin the changes immediately take place, and isAdmin comes out as true on the first time?
Passing a function to setAdmin() to toggle in between states will immediately update the state variable.
const checkIfAdmin = () => {
setAdmin(isAdmin => !isAdmin)
}
You should be getting 2 console.logs, one that returns false and another that returns true. This is due to the way useEffect, and setState work.
When useEffect is used with an empty array as the second arg, it only runs once when the component mounts. And on the first mount of your component, isAdmin is initialized to false
useEffect(() => {
checkIfAdmin()
}, []);
Once setAdmin(true) executes, it updates the dom, causing the component to render for a second time, with the isAdmin value being true
You can visualize how the dom is being updated by adding another console.log() in your useEffect.
useEffect(() => {
console.log('component is mounting...')
checkIfAdmin();
}, []);

Pull data from firestore using useEffect works on re-render only

Here is my code:
import React, { useEffect, useState } from 'react';
import { getDocs, collection } from 'firebase/firestore';
import { db } from '../firebase-config';
const Home = () => {
const [postList, setPostList] = useState([]);
const postsCollectionRef = collection(db, "data");
useEffect(() => {
const getPosts = async () => {
const data = await getDocs(postsCollectionRef);
let postListArray = []
data.forEach((doc) => {
const post = { ...doc.data() }
postListArray.push(post)
});
setPostList(postListArray)
};
getPosts();
console.log(postList);
}, []);
return (
<div>test</div>
);
};
export default Home;
On loading, the console.log returned an empty array. The spooky thing is when i changed anything , for example
return (
<div>test_epic</div>
);
The console.log shows that it is an array. Anyone has any idea as to why? Please refer to the screepcap as attached.
the first render on loading
I changed anything and components rerendered
Setting state in react is asynchronous, so the data is loaded and the state is set but the console.log statement is executed before the setting state async operation is complete
To make it a bit more clear this is how it works step by step
Component is rendered and postList is initialized with a value of []
useEffect is triggered
Data is fetched
A call to set a new value of postList is placed using setPostList (key here is a call is placed not that the data is actually updated)
You print console.log with a value from Step 1
The call from Step 4 is complete and now the data is actually updated
Here is an article that explains it with examples
And here is another answer that explains this deeply

Why useEffect does not behave like what i expect?

I was wondering Why this isn't an infinite loop?
I set the state inside of the useEffect hook and that should re-render the component and useEffect will be ran again and again and agian...
why this isn't what i expected?
import React, { useState, useEffect } from "react";
import axios from "axios";
const App = () => {
const [data, setData] = useState();
useEffect(() => {
axios.get("https://jsonplaceholder.typicode.com/comments").then((r) => {
console.log("Checking...");
setData(r.data[0]);
});
});
return (
<div>
<h1>{data}</h1>
</div>
);
};
export default App;
useEffect also accepts an array of dependencies i.e., useEffect will get executed when any values inside the array changes. So, if you want useEffect to be called whenever data changes, do this:
useEffect(() => {
axios.get("https://jsonplaceholder.typicode.com/comments").then((r) => {
console.log("Hello world");
setData(r.data[0]);
});
}, [data]);
The useEffect hook needs a second parameter, the dependency array.
When this dependency array is empty it will have the same behavior as the componentDidMount
If you add some dependencies to your array as data this useEffect will be executed every time that the data state changes.
So, if you want to fetch the data when the component loads you must do something like this.
useEffect(() => {
axios.get("https://jsonplaceholder.typicode.com/comments").then((r) => {
console.log("Hello world");
setData(r.data[0]);
});
}, []);

React useState hook not updating with axios call

I am aware this is a common question as I have spent the last two hours going through every answer and trying to get my state to update but nothing is working.
I am fetching text from a cms however on first load the state is undefined and my app crashes. However if I comment the line out, load the page and uncomment the line the correct values are displayed.
Here is some of the code I have tried.
The data i am hoping to get
[
{id:1},
{id:2},
{id:3},
{id:4},
]
import react, {useEffect, useState} from 'react'
import axios from 'axios'
const [carouselTitle, setCarouselTitle] = useState([])
useEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
await axios('api').then(
response => {
console.log(response.data)
setCarouselTitle(response.data)
console.log(carouselTitle)
})
};
return(
<h1>{carouselTitle[1].id}</h1>
)
console logging the data works fine but when i console log the state it does not work.
2ND METHOD I TRIED
useEffect(() => {
const fetchData = async () => {
const res = await axios('api');
const carouselTitleAlt = await res.data;
setCarouselTitle({ carouselTitleAlt });
console.log(carouselTitleAlt);
console.log(carouselTitle);
};
fetchData();
}, []);
Again console logging the const inside the useEffect displays the correct information but logging the state does not work.
Appreciate your responses or better ways of displaying the data.
setState is asynchronous : https://reactjs.org/docs/faq-state.html#why-doesnt-react-update-thisstate-synchronously
It means that you cannot expect to console.log the new state value the line after you called setCarouselTitle.
To log the new value, you could use another useEffect, with carouselTitle in the dependencies array, where you console.log(carouselTitle) :
useEffect(() => {
console.log(carouselTitle);
}, [carouselTitle]);
That said, your component should behave correctly, it will be refreshed when the state is updated.
In the JSX you should check that carouselTitle is not undefined (meaning that the request failed or is still pending) :
{carouselTitle && <H1>{carouselTitle[0].id}}
https://reactjs.org/docs/conditional-rendering.html#gatsby-focus-wrapper
First of all, if you pass an empty array for initial data to useState, you can't get any item in that array in here:
return(
<h1>{carouselTitle[1].id}</h1>
)
Because component returns first item of an array that has nothing. I prefer to you do it like this:
return(
<h1>{carouselTitle.length > 0 && carouselTitle[0].id}</h1>
)
And also based on this and official documentation, setState (and also setSomthing() using useState()) is asynchronous.
So state data doesn't show immediately after setting that.
You should trigger useEffect for run fetch function
useEffect(()=>{fetchData();},[carouselTitle])

React, onClick only works after second click, Axios, Async/Await

I must be missing something very straight forward.
I am trying to make an axios GET request and then setState in an async/await function, I have also tried fetch().then(). The console.log() returns undefined unless I click getData() twice or put it in a separate onClick() event. I think the state is being set correctly but the console.log() doesn't perform as I expect it. Is there a way to console.log() in the same function as the axios request? As always, any help is greatly appreciated. Thanks!
import React, { useState } from "react";
import axios from "axios";
const SalesGraph = () => {
const [retrievedData, setretrievedData] = useState();
const getData = async () => {
try {
const salesData = await axios.get("http://localhost:5000/sales");
await setretrievedData(salesData);
console.log(retrievedData);
} catch (err) {
console.log(err);
}
};
const showState = () => {
console.log(retrievedData);
};
return (
<div>
<button onClick={getData}>Graph</button>
<button onClick={showState}>showState</button>
</div>
);
};
export default SalesGraph;
setretrievedData is the asynchronous method and you can't get the updated value of retrievedData immediately after setretrievedData.
You need to get it in the useEffect by adding a dependency.
useEffect(() => {
console.log(retrievedData);
}, [ retrievedData ]);
All setters for react useState are async so even if you await your setretrievedData it will go on to the next line even before it is done.
To run any code only after the setter is done you need to add a useEffect and pass the state to watch as the 2nd arg

Resources