problem with usestate in firebase to save data - reactjs

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

Related

react error: Empty array after setting an const

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

useEffect does not store state for Firebase data on initial load and only shows up after fast refresh

UPDATE
#Chris's answer below helped me with my issue. Installed the React Firebase Hooks package and set up my code as seen below. Everything is working flawlessly now! :)
const [value] = useCollection(collection(db, `${sessionUserId}`), {
snapshotListenOptions: { includeMetadataChanges: true },
})
useEffect(() => {
if (value) {
const allData = JSON.parse(
JSON.stringify(
value?.docs.map((doc) => ({
id: doc.id as string,
resultData: doc.data() as DocumentData,
}))
)
)
setAllLogs(allData)
}
}, [value])
What am I using in my project? NextAuthjs, Firebase, Recoiljs
I have a useRecoilState hook to save logs that users have created.
const [allLogs, setAllLogs] = useRecoilState(modalAllLogs)
The modalAllLogs looks like this:
export const modalAllLogs = atom<Array<DocumentData>>({
key: 'allLogs',
default: [],
})
Here is the issue. I have a useEffect hook that grabs the data the user has from the database and assigns setAllLogs to that data. Note: I * do not * have the state updating anywhere else.
useEffect(() => {
const docQuery = query(
collection(db, `${sessionUserId}`),
orderBy('timestamp', 'desc')
)
const unsubscribe = onSnapshot(
docQuery,
(snapshot: QuerySnapshot<DocumentData>) => {
const data = JSON.parse(
JSON.stringify(
snapshot.docs.map((doc: QueryDocumentSnapshot<DocumentData>) => ({
id: doc.id as string,
resultData: doc.data() as DocumentData,
}))
)
)
setAllLogs(data)
}
)
return unsubscribe
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [db])
What happens is, on initial render, allLogs returns an empty array, which makes sense, but stays empty until I make any edit within the same file the useEffect hook is in, save it, and NextJS triggers it's fast render, only then does allLogs suddenly display the data properly. I did a console.log(snapshot) and it is empty on every initial render as well.
Whenever I add data using the inputs I have created, Firebase stores all the data properly and correctly. The only issue is when the data is not loaded in on initial render. Other than that, I have no other issues.
I am not sure what I am doing wrong here. Maybe it is the RecoilState that I am utilizing wrong?
I have tried many things from other forums - changing the dependency to allLogs (which gave me an infinite loop), tried using getDocs() instead of onSnapshot(), and etc.
The problem here is, that the useEffect isn't triggered because the "db" constant doesn't change.
Have a look at these custom hooks for Firebase/firestore https://github.com/CSFrequency/react-firebase-hooks/tree/master/firestore
or lift the onSnapshot up to get real-time results. But in the last case the renders won't be triggered so I think it's the best to make use of custom hooks.

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();
}, []);

React useState without undefined values

I'm novice with React and the state concept. So I fetch data from my database with axios. I put everything in a state (accomodation) and then I need to catch a field (country) into datas and use it into other function.
I succeed to catch everything but when I try to test it with a 'console.log' it appears that the two first result returns empty/undefined before having a result. Because the fact the two first attempts are empty/undefined, the other function doesn't work.
Anyone could help me please :)
Here are my code :
const { id } = useParams()
const [accomodation, setAccomodation] = useState('')
const getAcc = async () => {
const data = await axios.get(
`${process.env.REACT_APP_API_URL}api/v1/accomodations/${id}`
)
setAccomodation(data.data.data.accomodation)
}
useEffect(() => {
getAcc()
}, [])
const country = accomodation.country
console.log(country)
To go over what is happening here:
const [accomodation, setAccomodation] = useState('') the accomodation object is set to empty string.
useEffect(() => { getAcc() }, []) getAcc() is called upon render.
const country = accomodation.country; console.log(country) we are still waiting for getAcc() to finish, meanwhile, country is set to the 'country' property of an empty string which is underfined. undefined is printed.
setAccomodation(data.data.data.accomodation) getAcc() finally finishes, and accommodation is hopefully set to the object you intended.
What happens now?.. Nothing. Because you have not done anything to act on a state update. So one of the answers already posted here is on the right track. You need to act on the update of accommodation state.
This is how you trigger an effect on state update:
useEffect(() => {
if (accommodation.country) {
console.log(accomodation.country);
}
}, [accomodation])
Note that this useEffect() will be invoked whenever accommodation changes from its previous state. If the desired effect is not achieved it may be that accommodation is not the object you intended.

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