React, can't set new data after get from api - reactjs

I am not sure if I am mistaken about the asynchronous or React itself.
const fetchData = async () => {
const response = await sessionApi.get("/sessions", {
headers: {
Authorization: user.user?.token
}
})
if (response.data.success) {
setSessionData(response.data.message)
console.log(sessionData)
}
}
this is my fetchData.I try using async in function when I console.log to show data it show empty [] if I change to
console.log(response.data.message)
It shows my data from request.So that my api is not wasted.
useEffect(() => {
fetchData()
console.log(sessionData)
}, [])
Ok then I use useEffect in my React web then I console.log(sessionData) but it still empty list
const [sessionData, setSessionData] = useState([])
that is my my state. Maybe I am mistaken. Please tell me where I missed.

As #jon-parret said, you can't console log your state like this just after setting the state value.
If you want to do something when your sessionData is set or changed, you can do something like this.
useEffect(() => {
fetchData()
}, [])
useEffect(() => {
console.log(sessionData)
}, [sessionData])

The set callback from React.useState is asynchronous, so console logging your sessionData in your API call function will not log the data received.

try having a separate useEffect that listens for the sessionData to be populated
useEffect(() => {
if(sessionData) {
console.log(sessionData);
}
}, [sessionData])
the setSessionData() callback will be added to the end of the queue so will run after the console.log in your code which is already in the call stack

setSessionData is asynchronous, so the moment you set a new state, it's not guaranteed that sessionData is updated.
instead, you can check it by using an useEffect hooks:
useEffect(() => {
console.log(sessionData)
}, [sessionData])
// add `sessionData` as a dependency so when it changes, the log will show

Thanks you every, I can logging it with
useEffect(() => {
fetchData()
}, [])
useEffect(() => {
console.log(sessionData)
}, [sessionData])
I understand that using async-await will wait for that line of code to run first. And then work on the next line That makes me wonder why my console.log isn't working.

Related

Using useEffect properly when making reqs to a server

I have a handleRating function which sets some state as so:
const handleRating = (value) => {
setCompanyClone({
...companyClone,
prevRating: [...companyClone.prevRating, { user, rating: value }]
});
setTimeout(() => {
handleClickOpen();
}, 600);
};
I think also have a function which patches a server with the new companyClone values as such:
const updateServer = async () => {
const res = await axios.put(
`http://localhost:3000/companies/${companyClone.id}`,
companyClone
);
console.log("RES", res.data);
};
my updateServer function gets called in a useEffect. But I only want the function to run after the state has been updated. I am seeing my res.data console.log when I load my page. Which i dont want to be making reqs to my server until the comapanyClone.prevRating array updates.
my useEffect :
useEffect(() => {
updateServer();
}, [companyClone.prevRating]);
how can I not run this function on pageload. but only when companyClone.prevRating updates?
For preventing function call on first render, you can use useRef hook, which persists data through rerender.
Note: useEffect does not provide the leverage to check the current updated data with the previous data like didComponentMount do, so used this way
Here is the code example.
https://codesandbox.io/s/strange-matan-k5i3c?file=/src/App.js

Ho to wait with fetch until redux state is available?

I want to fetch some data from a database, and depending on the user the returned data should differ. The way i tried it, was by passing the userid as a query. The id is stored as a redux state. The problem is, that it takes some time before the redux state is available. Ive tried fixing this with if statements, and rerunning the useEffect everytime the auth state is updated. This doesn't work.
I want to fetch, when the redux state auth.user.id is available. Which it is like .1 sec after the initial load.
Here is my code:
const auth = useSelector((state) => state.auth);
useEffect(async () => {
if (auth.token.length > 0) {
const res = await getData(`calendar/?userId=${auth.user.id}`);
setLessons(res.lessons);
setEvents(res.events);
}
}, [auth, date]);
I believe useEffect is already asynchronous, so you don't need to use the async keyword in the anonymous callback. You can create the async function for that logic elsewhere and call it within the useEffect.
Similarly, you could put in self calling async function within your useEffect as such:
useEffect(() => {
(async () => {
if (auth.token.length) {
try {
const res = await getData(`calendar/?userId=${auth.user.id}`);
setLessons(res.lessons);
setEvents(res.events);
}catch (err) {console.log(err);}
}
})();
}, [auth, date]);
I think this link may be helpful:
React Hook Warnings for async function in useEffect: useEffect function must return a cleanup function or nothing
So with the basic understanding, I assume that you need to call the API whenever userId is available. try the below useEffect
useEffect(async () => {
// check user id is available here
if (auth.user && auth.user.id) {
const res = await getData(`calendar/?userId=${auth.user.id}`);
setLessons(res.lessons);
setEvents(res.events);
// some other statements
}
}, [auth, date]);

Data is not set on time on the state using hooks in React JS

I am working on a Instagram clone project. I am doing the profile page part and got some problems.
I am trying to use hooks (useState) to set my response from the server with the user document. My problem is that when I use the useEffect (only once with []), the username state is not set.
I let useEffect run infinitely, and I found out that the state is set after some little time.
const getUser = async () => {
await axios.get(`/user/getbyusername/${props.match.params.username}`)
.then(res => {
console.log(res.data) // PRINTS DATA CORRECTLY
setProfileUser({
_id: res.data._id,
username: res.data.username,
fullName: res.data.fullName,
followersCount: res.data.followersCount,
followingCount: res.data.followingCount,
profileImage_PublicId: res.data.profileImage_PublicId,
bio: res.data.bio
})
console.log(profileUser)
})
}
// DOES NOT SET ANY DATA
useEffect(() => {
getUser(); // async function and awaits for the server response
// HERE, I call getUserPosts using profileUser._id, and it profileUser._id is not set yet
getPosts(); // async function as well
}, [])
Checking if setProfileUser(...) is correct, and it is because it sets the data, but after some time even though I could console.log(res.data) in the correct first time using [].
// WORKS, sets the user state after two runs. NOT GOOD PRACTICE
useEffect(() => {
getUser();
})
Add another useEffect hook that runs when profileUser changes, so that it runs after setProfileUser finishes:
useEffect(getUser, []);
useEffect(() => {
if (profileUser) {
getPosts();
}
},
[profileUser],
);

useEffect not respecting promise and returns null

I'm trying to useEffect when my component first loads. UseEffect has a call to a firebase function but it's returning null and I can't figure out why.
const [emails, setEmails] = useState({
reports: [],
savedReports: [],
})
useEffect(() => {
props.firebase.getEmailReportsTo(props.orgId)
.once('value')
.then(returnedEmails => {
console.log(returnedEmails.val());
setEmails(prevEmails => ({
...prevEmails,
savedReports: [returnedEmails.val()]
}))
})
}, []);
Here I make a call to a firebase method which returns the values. I've confirmed there's values in that endpoint. The above returns null. It appears as if the console log is logging BEFORE the call finishes executing. However I'm not sure why since console.log is in then. Console.log isn't the important part but then it creates null under my state also.
When I do this:
useEffect(() => {
props.firebase.getEmailReportsTo(props.orgId)
.once('value')
.then(returnedEmails => {
console.log(returnedEmails.val());
setEmails(prevEmails => ({
...prevEmails,
savedReports: [returnedEmails.val()]
}))
})
}, [emails.savedReports]);
I can view the console.log with the data but this kicks into an infinite loop.
Changing [emails.savedReports] to [emails] also returns null
This can be broken down into two question but whatever helps me achieve the result of setting the state to the firebase call works for me. One would be why is the useEffect not waiting for then to complete? The other would be if [emails.savedReports is updated then shouldn't it stop executing useEffect? However I can resolve this is cool with me.
You are making request when you are getting your props.orgId
useEffect(() => {
if(props.orgId){ // if you receive your orgId then an then your request will be call
props.firebase.getEmailReportsTo(props.orgId)
.once('value')
.then(returnedEmails => {
console.log(returnedEmails.val());
setEmails(prevEmails => ({
...prevEmails,
savedReports: [returnedEmails.val()]
}))
})
}
}, [props.orgId]);

I cannot collect data from API using Axios + React

I'm beginner with React. I have 2 different cases where I'm using React Hooks which I cannot receive the data from my local API properly.
Case 1:
export const RegisterT = () => {
const [test, setTest] = useState()
const addrState = {}
axios.get('http://127.0.0.1:3333/states', { addrState })
.then(res => {
setTest(res.data)
console.log(test)
})
...
}
It works with the state test displaying correctly the content from the API but I don't know why/how the Axios continues calling the API infinity - endless. (Ps: the very first call it returns undefined, then the next ones it works) What am I doing wrong?
To fix this I've tried to use useEffect like this (Case 2):
export const RegisterT = () => {
const [test, setTest] = useState()
const addrState = {}
useEffect(() => {
axios.get('http://127.0.0.1:3333/states', { addrState })
.then(res => {
setTest(res.data)
console.log(test);
})
}, [])
...
}
Now the Axios works only once but no data is coming from the API. Maybe I should use async/wait for this case but I cannot make it work. Does anyone know how to fix that (Case 1 or/and Case 2)?
Thanks.
Updating the state is an asynchronous operation. So the state is not really updated until the next time the component gets rendered. If you want to capture the correct state, you can either console.log(res.data) or wrap that inside the useEffect hook with test as dependency.
export const RegisterT = () => {
const [test, setTest] = useState()
const addrState = {}
// effect only runs when component is mounted
useEffect(() => {
axios.get('http://127.0.0.1:3333/states', { addrState })
.then(res => {
setTest(res.data);
});
}, []);
// effect runs whenever value of test changes
useEffect(() => {
console.log(test);
}, [test]);
}
That way it is guaranteed that the console.log runs when the value of test is updated.
Also the reason the API request is invoked once is you have not mentioned anything in the dependency array. [] empty dependency array runs the effect when the component is mounted for the first time.
async/await is just a wrapper around Promise object. So they would behave similarly.
The solution with useEffect is good. If you don't use it each render will call the request. This is the same if you put there console.log with any information. The reason why you don't see the data in the useEffect is that the value of the state is not updated in current render but in the next which is called by setter of the state. Move the console.log(test); after useEffect to see the data. On init it will be undefined but in the next render, it should contain the data from the request.

Resources