react error: Empty array after setting an const - reactjs

please why do i get an empty array when i do console.log(customers) but i get the right response which is status 200 when i do console.log(response.data);. i dont know if i am doing setCustomers(response.data.customers); in a wrong way. please help me
const [customers, setCustomers] = useState([]);
useEffect(() => {
const storedToken = JSON.parse(localStorage.getItem("token"))
const fetchCustomers = async () => {
const config = {
headers: { Authorization: `Bearer ${storedToken}` }
};
const response = await axios.get('https://exampleofalink.com/api/v1/customer', config);
console.log(response.data); // status 200
setCustomers(response.data.customers);
console.log(customers); //empty
};
fetchCustomers();
}, []);
i also tried
setCustomers(prevCustomers => [...prevCustomers, ...response.data.customers]);
but i still get an empty array

don't try to console log when react is still settting the values, you will not see the change, but the values are there!
u can place another different useEffect to see the change like this:
useEffect(() => {
console.log(customers);
}, [customers]);

When you setting a state, it doesn't immediately change the value, it will changed on re-render, so you will see the previous value which is an empty array and can't see the newest change immediately. There are two option if you want to see the values:
Put console.log outside the useEffect
Or, add console.log into another useEffect that run when customers value changed
useEffect(() => {console.log(customers)}, [customers])
See also:
The useState set method is not reflecting a change immediately

Related

usestate can change the state value after axios in useEffect

I expected to get the url with category=business,but the web automatically reset my state to the url that dosent have the category.I dont know the reason behind
let {id}=useParams()
const [newsurl,setNewsurl]=useState(()=>{
const initialstate="https://newsapi.org/v2/top-headlines?country=us&apiKey=c75d8c8ba2f1470bb24817af1ed669ee"
return initialstate;})
//console.log(id);
const [articles, setActicles] = useState([]);
useEffect( ()=>{
if(id === 2)
console.log("condition")
setNewsurl("https://newsapi.org/v2/top-headlines?country=de&category=business&apiKey=c75d8c8ba2f1470bb24817af1ed669ee")},[])
useEffect(() => {
const getArticles = async () => {
const res = await Axios.get(newsurl);
setActicles(res.data.articles);
console.log(res);
};
getArticles();
}, []);
useEffect(() => {
console.log(newsurl)
// Whatever else we want to do after the state ha
s been updated.
}, [newsurl])
//return "https://newsapi.org/v2/top-headlines?country=us&apiKey=c75d8c8ba2f1470bb24817af1ed669ee";}
return (<><Newsnavbar />{articles?.map(({title,description,url,urlToImage,publishedAt,source})=>(
<NewsItem
title={title}
desciption={description}
url={url}
urlToImage={urlToImage}
publishedAt={publishedAt}
source={source.name} />
)) } </>
)
one more things is that when i save the code the page will change to have category but when i refresh it ,it change back to the inital state.Same case when typing the url with no id.May i know how to fix this and the reason behind?
Setting the state in React acts like an async function.
Meaning that the when you set the state and put a console.log right after it, it will likely run before the state has actually finished updating.
You can instead, for example, use a useEffect hook that is dependant on the relevant state in-order to see that the state value actually gets updates as anticipated.
Example:
useEffect(() => {
console.log(newsurl)
// Whatever else we want to do after the state has been updated.
}, [newsurl])
This console.log will run only after the state has finished changing and a render has occurred.
Note: "newsurl" in the example is interchangeable with whatever other state piece you're dealing with.
Check the documentation for more info about this.
setState is an async operation so in the first render both your useEffetcs run when your url is equal to the default value you pass to the useState hook. in the next render your url is changed but the second useEffect is not running anymore because you passed an empty array as it's dependency so it runs just once.
you can rewrite your code like the snippet below to solve the problem.
const [articles, setActicles] = useState([]);
const Id = props.id;
useEffect(() => {
const getArticles = async () => {
const newsurl =
Id === 2
? "https://newsapi.org/v2/top-headlines?country=de&category=business&apiKey=c75d8c8ba2f1470bb24817af1ed669ee"
: "https://newsapi.org/v2/top-headlines?country=us&apiKey=c75d8c8ba2f1470bb24817af1ed669ee";
const res = await Axios.get(newsurl);
setActicles(res.data.articles);
console.log(res);
};
getArticles();
}, []);

after axios get my setState hooks doesn't run

I have a lot of functions like this, using the same approach, and they all run fine.
But this one is going me crazy.
My code is:
const [match,setMatch] = useState(null);
axios.get(path)
.then((res) => {
status = res.data;
setMatch(res.data)});
const test = match;
If I debug the program and break at setMatch(status), the content of res.data is correct:
res.data is an array, and each element contains a mix of values and objects as show in the screenshot below.
But when I execute the setMatch(status) the result is that the match state variable doesn't change: it remains null as set the first time.
Can someone provide some suggestion?
From your question, I dont know what could likely affect your code from running. But this is the common pattern when handling asynchronous request.
It is expected that it should be executed in the useEffect hook right after it is painted
const Component = () => {
const [match,setMatch] = useState([]);
useEffect(() => {
axios.get(path)
.then((res) => {
status = res.data;
setMatch([match, ...res.data])})
.catch((err) => handleError(err));
}, [path]) // assuming that the path changes otherwise if you are running this once, leave the array empty
return (<div> {(match.length > 0)? 'Loading': {renderYourLogicHere} </div>)
}

React useEffect causing infinite re-render despite passing argument to dependency array

The 'myPosts' has an object with multiple posts inside it.. I wanted the user profile to immediately show the post after it is uploaded so I passed 'myposts' in the dependency array.
But the problem is that the component is re-rendering infinitely. How can I make it so that it re-renders once, only when a new post is uploaded? I can't understand why passing 'myposts' in the array is causing infinite renders instead of only once.
const [myposts, setPosts] = useState([]);
useEffect(() => {
fetch('/mypost', {
headers: {
cookie: 'access_key',
},
})
.then((res) => res.json())
.then((data) => {
// console.log(data);
setPosts(data.myposts);
});
}, [myposts]);
When fetch resolves, it modifies myposts, which triggers a fetch because it is listed as dependency of useEffect, which modifies myposts, and so it continues...
It seems that myposts depends on the result of the fetch, not the other way around. So I would suggest removing myposts from the dependency list.
The useEffect hook is called when myposts gets updated. In the final .then of your fetch, you're updating it via setPosts. The best way to fix this is by making the dependency array an empty array.
But this won't solve the issue of updating posts from the server, but this can also be done in a periodic function with setInterval. This would result in something like the code below.
const [myposts, setPosts] = useState([]);
const update = fetch('/mypost', {
headers: {
cookie: 'access_key',
},
})
.then((res) => res.json())
.then((data) => {
// console.log(data);
setPosts(data.myposts);
});
useEffect(() => {
update()
const interval = setInterval(update, 30000)
return () => clearInterval(interval)
}, []);

problem with usestate in firebase to save data

I have a problem that I can not solve try a thousand ways and nothing
I happen to show
I generate a state:
const [totalComidas, setTotalComidas] = useState({})
After that I do a useEffect to bring me the data from firebase and I want to save it in the state but it saves it empty
useEffect(() => {
const resultadoComidas = []
db.collection('menu semanal')
.get()
.then((snap) => {
snap.forEach((doc) => {
const comida = doc.data()
// comida.id = doc.id
resultadoComidas.push(comida)
console.log(resultadoComidas[0])
})
setTotalComidas(resultadoComidas)
console.log(totalComidas)
})
}, [])
And these are my results in console
enter image description here
The first result in the console is before adding it to the state and the second is the new state, which seems strange to me because it brings the data correctly but when I assign it to the state it assigns it to me empty.
It's normal for the totalComidas to be empty when you log it inside useEffect because the result of your setTotalComidas won't be showed in that particular useEffect. If you want to see the change of your totalComidas you need to have another useEffect like so
useEffect(() => {
console.log(totalComidas)
})
If you wonder why this is the case then read this. It explains the behavior of useEffect

React useEffect infinite loop fetching data from an api

Hi I'm trying to make a twitter clone app. I am using React on the client side and Express on the server side and PostgreSQL as my database. So here's the problem, I'm trying to use the useEffect like this:
const [tweets, setTweets] = useState([]);
const getTweets = async () => {
const res = await api.get("/posts", {
headers: { token: localStorage.token },
});
setTweets(res.data);
};
useEffect(() => {
getTweets();
}, [tweets]);
I have no idea why it's looping infinite times, am I using it correctly though? I want the tweets to be updated every time I post a tweet. It's working fine but it's running infinite times. I just want it to re-render if a tweet got posted.
Here's my server code for getting all the posts:
async all(request: Request, response: Response, next: NextFunction) {
return this.postRepository.find({
relations: ["user"],
order: {
createdAt: "DESC",
},
});
}
The problem is every time you change the tweets it executes useEffect and changes the tweets and so long and so forth, so it's natural that it loops infinitely, the solution is to add a trigger that you set to true when a tweet gets posted, so the solution would be like this
const [tweets, setTweets] = useState([]);
const [isFetching, setIsFetching] = useState(false);
const getTweets = async () => {
const res = await api.get("/posts", {
headers: { token: localStorage.token },
});
setTweets(res.data);
};
useEffect(() => {
getTweets();
setIsFetching(false);
}, [isFetching]);
and set some logic to use setIsFetching(true) in order to execute the useEffect
PS: if you use an empty array in useEffect, it would execute only when the component is mounted (at the start)
useEffect(() => {
getTweets();
}, [tweets]); // [tweets means that hook works every time 'tweets' state changes]
so your getTweets function set tweets => as tweets are changed hook works again => call getTweets => ... = infinite loop
if you want to download tweets, use empty array instead - hook will work once then
Pass empty array as a second arg for calling it once otherwise for changing it on every tweet change it will re-trigger, so whenever state will change only then it will be re-rendered like Tarukami explained. One thing you can do is check the length like mentioned below so not to compare the whole object but just the length
useEffect(() => {
getTweets();
}, [tweets.length]);
This might raise an error react-hooks/exhaustive-deps lint error (that's a bypass you can use it).
But if you want more tighter check you can compare the ids on each re-render (create a hash/key/id from all element in the array and compare them on each render) like so [tweet id here]) // Only re-subscribe if id changes

Resources