How to assign data to a variable from axios get() response - reactjs

I am trying to use Express (axios) to build a React app.
I was able to get an array of objects from MongoDB using get() method. Currently the list is printed out to the console. How can I assign it to a variable so that I could use that array for further actions?
useEffect(() => {
const expensesListResp = async () => {
await axios.get('http://localhost:4000/app/expenseslist')
.then(
response => console.log(response.data))
}
expensesListResp();
}, []);
Many thanks!

You can assign it in the following way, let say you have an array posts:
const [posts, setPosts] = useState([]);
useEffect(() => {
axios.get('url')
.then(res => setPosts(res.data))
.catch(err => console.log(err));
}, [])
In your code, you can do it in this way:
const [resultArray, setResultArray] = useState([]);
useEffect(() => {
const expensesListResp = async () => {
await axios.get('http://localhost:4000/app/expenseslist')
.then(
response => setResultArray(response.data))
}
expensesListResp();
}, []);

I am assuming that you have data printed on the console.log(response.data) and you want it to be assigned to a variable so that you can use it right?
if that's the case you are already using async function just name it with whatever variable name you want it to be before await.
for example:
const expensesListResp = async () => {
const "your variable name" = await axios.get('http://localhost:4000/app/expenseslist')
}
you can also save that variable in your state, if you want to use that variable data throughout your application.

Related

how to map multiple nested arrays json react rails api

I have multiple nested arrays, how can I map them ? I could map only one nested with the help of #Apostolos
`
function RequestDetail({match}) {
const [request, setRequests] = useState({ user: {} });
const [fulfillment, setFulfillments] = useState({});
const [text, setText] = useState([]);
useEffect(() => {
fetchRequest();
}, []);
const fetchRequest = () => {
axios
.get(
`${baseUrl}/${match.params.id}`
)
.then((res) => {
setRequests(res.data);
console.log(res.data);
})
.catch((err) => console.log(err));
};
const handleSubmit = async (e) => {
//e.preventDefault();
const newfulfillment = {text}
try{
const response = await axios.post(`${baseUrl}/${match.params.id}/fulfillments`, newfulfillment);
setAuthHeaders();
const allFullfilments = [...fulfillment, response.data];
setFulfillments(allFullfilments);
setText('');
} catch (err) {
console.log(`Error: ${err.message}`);
}
}
`
The array I need is fulfillments
Your response is an array which contains another array fullfilments and you
u want an array with all fullfilments right?
To do so, you need to map your response, and retrieve your fullfilments, you'll have an "array of array" which you can .flat()
Something like
response.data.map(({fullfilments}) => fullfilments).flat()
Since I was using rails API, with the help of Serializers I could I get the needed data in a different way.

useEffect for chained fetch calls

I'm chaining two fetch calls to retrieve data. The first call gets a token and then second call uses that token to get the data. Here's an example:
fetch("[GET THE TOKEN]")
.then((response) => response.json())
.then(token => {
fetch("[GET DATA]?token="+token)
.then((response) => response.json())
.then((data) => {
return data;
});
})
The issue is that I need to make lots of different calls sometimes within the same component and writing that chained call over and over again can get tedious and if I need to make changes it's a lot of code to edit.
I came up with a functional solution but I haven't stress tested it yet. I'm still a react noob so feedback would be helpful
context.jsx
const [token,setToken] = useState('')
const fetchToken = async () => {
fetch("[GET THE TOKEN]")
.then(response => response.json())
.then(data => {
setToken(data);
});
}
component.jsx
const {token, fetchToken } = useContext(context)
//fetch data function
const [initFetch,setInitFetch] = useState(false);
const fetchData = () => {
fetch("[GET DATA]?token="+token)
.then((response) => response.json())
.then((data) => {
return data;
});
}
//action that inits fetch data
const submitForm = () => {
setInitFetch(true)
}
//useEffect that will trigger the function on initFetch and token const changing values
useEffect(() => {
if (token && initFetch === true) {
fetchData();
}
},[token,initFetch]);
I see that you want to call this fetch function when you submit the form, Therefore, you should not do that with an useEffect, simply because you don't need to.
Use the useCallback hook and create an async function inside of it. Call it almost wherever you want.
See:
const MyComponent = () => {
const {token, fetchToken } = useContext(context)
const [initFetch, setInitFetch] = useState(false);
const fetchData = useCallback(async () => {
const response1 = await fetch(`[GET DATA]?token=${token}`);
const jsonFromResponse1 = await response1.json();
return jsonFromResponse1;
}, [token])
const randomFunc = async () => {
const data = await fetchData()
}
return (
<button onClick={fetchData}>Button</button>
)
}
The dependency array of useCallback is crucial, if you don't insert it, when the token changes, the hook will never update the function with its new value.
You can continue to use then. But I strongly recommend you to try await/async. It will make your code way easier and readable.
What I get from you question, that you are looking for some way to not repeat your fetch calls.
If that's so, I believe you can go with a custom hook that you can call every time you need.
something like that
const useFetchFn=(arg)=>{
fetch(arg)
.then((response) => response.json())
.then((data) => {
return data;
});
}
Then in your component
const [token,setToken] = useState('')
const fetchToken =useFetchFn("[GET THE TOKEN]")

Get an empty array when use array of objects in filter function React

I am new to react and try to get data from the database and view data in frontend. This is the code I tried.
function ViewPost() {
const { postId } = useParams();
console.log(postId);
const [post, setPost] = useState({});
useEffect(()=>{
getOnePost();
}, []);
useEffect(()=>{
if (post && post.location) {
console.log(post.location);
console.log(post.location.longitude);
console.log(post.location.latitude);
}
}, [post]);
const getOnePost = async () => {
try {
const response = await axios.get(`/buyerGetOnePost/${postId}`)
console.log(response);
const allPost=response.data.onePost;
setPost(allPost);
} catch (error) {
console.error(`Error: ${error}`)
}
}
console.log(post);
console.log(post.wasteItemList);
const [offers, setOffers] = useState([]);
useEffect(()=>{
getAllOffers();
}, []);
const getAllOffers = async () => {
await axios.get(`/viewPendingSellerOffers`)
.then ((response)=>{
const allNotes=response.data.existingOffers;
setOffers(allNotes);
})
.catch(error=>console.error(`Error: ${error}`));
}
console.log(offers);
const wasteItem = offers?.filter(wasteItems => wasteItems.status==='accepted' && wasteItems.wasteItemsListId===post?.wasteItemList?._id);
console.log(wasteItem);
}
When I call the first API I get these results. This is an image of results.
In the above image, there is a length 2 array of objects called as wasteItemList. Then I call the second API and get these results.
This image shows length 8 array of objects. Then I try to filter the data of these two arrays using this const wasteItem = offers?.filter(wasteItems => wasteItems.status === 'accepted' && wasteItems.wasteItemsListId === post?.wasteItemList?._id); code. But I get a length 0 empty array as the results of this filter function. But when I try an ID of a wasteItemList array
6112679258125b0418844368 instead of using this post?.wasteItemList?._id code I get the correct result. What is the problem here? How do I solve this problem?
Edited code:
function ViewPost() {
const { postId } = useParams();
const [post, setPost] = useState(undefined);
const [offers, setOffers] = useState(undefined);
useEffect(() => {
setPost(undefined);
axios
.get(`/buyerGetOnePost/${postId}`)
.then((resp) => setPost(resp.data.onePost))
.catch((err) => console.error(err));
}, [postId]);
useEffect(() => {
axios
.get(`/viewPendingSellerOffers`)
.then((response) => setOffers(response.data.existingOffers))
.catch((err) => console.error(err));
}, []);
useEffect(()=>{
if (post && post.location) {
console.log(post.location);
console.log(post.location.longitude);
console.log(post.location.latitude);
}
}, [post]);
console.log(post);
console.log(post?.wasteItemList);
console.log(offers);
const wasteItem = offers?.filter(wasteItems => wasteItems.status==='accepted' && wasteItems.wasteItemsListId===post?.wasteItemList?._id);
console.log(wasteItem);
}
useEffect runs asynchronously so your post will not be available
on your getAllOffers function which is located in your second
useEffect.
You will need to make your getOnePost() and getAllOffers() to
run synchronously within a single useEffect.
Or the problem is in your condition checks as I can't tell much only
by your given array picture.

why can't I use array methods on an array returned from the backend

I'm using a get request to get info about a post from the backend, I receive the data fine, it's just an object with some of the values being arrays. For example the imageRoutes property has an array value. I've saved the response object into my state, but when I try to access the array information using dot or bracket notation or try to use array methods like '.length', it just comes up as undefined? Why is this?
const [post, setPost] = useState({})
const [error, setError] = useState('')
useEffect(() => {
fetchData()
}, [])
console.log(post)
console.log(post.imageRoutes)
console.log(post.imageRoutes.length)
console.log(post.imageRoutes[0])
const fetchData = async () => {
await axios.post('/forums-post', {post_id: match.params.id})
.then(res => {
res.data.error ? setError(res.data.error) : setPost(res.data)
})
}
You're blending two different styles of asynchronous code: the traditional promise style (.then) and the more modern async/await style.
As a result, when you do:
const fetchData = async () => {
await axios.post('/forums-post', {post_id: match.params.id})
.then(res => {
res.data.error ? setError(res.data.error) : setPost(res.data)
})
What you're really doing, if we translate it to 100% async/await style, is ...
const fetchData = async () => {
const res = await axios.post('/forums-post', {post_id: match.params.id})
res.data.error ? setError(res.data.error) : setPost(res.data)
});
But you know what I don't see in that function? return! You're not returning anything, which explains why:
but when I try to access the array information using dot or bracket
notation or try to use array methods like '.length', it just comes up
as undefined?
If you simply add a return keyword (and keep the code all in a single style) ...
const fetchData = async () => {
const res = await axios.post('/forums-post', {post_id: match.params.id});
return res.data.error ? setError(res.data.error) : setPost(res.data);
});
... everything should work, and you should actually return a (not undefined) value ... or at least, you will if setPost/setError actually return something. If they don't, you might instead want to do:
const fetchData = async () => {
const res = await axios.post('/forums-post', {post_id: match.params.id});
res.data.error ? setError(res.data.error) : setPost(res.data);
return res.data;
});

Which is the best use of useEffect to fetch multiple data?

I'm some confused about some different ways to use the useEffect hook to fetch API data. I want to know if there is a "best way" to do this, performance related, or if it really doesn't matter how to do it.
Consider the following ways:
Mutiple function calls to fetch API data inside a single useEffect:
useEffect(() => {
const fetchStudents = async () => {
const students = await studentService.getAll()
setStudents(students)
}
const fetchCourses = async () => {
const courses = await courseService.getAll()
setCourses(courses)
}
const fetchSchedules = async () => {
const schedules = await scheduleService.getAll()
setSchedules(schedules)
}
fetchStudents()
fetchCourses()
fetchSchedules()
}, [])
A single function call to fetch all the api data inside a single useEffect:
useEffect(() => {
const fetchAllData = async () => {
const students = await studentService.getAll()
const courses = await courseService.getAll()
const schedules= await scheduleService.getAll()
setStudents(students)
setCourses(courses)
setSchedules(schedules)
}
fetchAllData()
}, [])
Maybe, multiple useEffects for each api call:
useEffect(() => {
const fetchStudents= async () => {
const students = await studentService.getAll()
setStudents(students)
}
fetchStudents()
}, [])
useEffect(() => {
const fetchCourses = async () => {
const courses = await courseService.getAll()
setCourses(courses)
}
fetchCourses()
}, [])
useEffect(() => {
const fetchSchedules = async () => {
const schedules= await scheduleService.getAll()
setSchedules(schedules)
}
fetchSchedules()
}, [])
Is there another way to consider? Let it be known.
In your second example you wait for each promise to resolve before executing the next one, this will hurt performance, the other examples are all running in parallel.
I would go with Promise.all inside a single useEffect because i think its more readable then 3 useEffect or 3 functions, and this will also make sure all of our promises are executing in parallel.
Note that if one of the promises inside Promise.all reject, the function is going to throw and you won't have any access to the resolved promises
useEffect(() => {
Promise.all([
studentService.getAll().then(setStudents),
courseService.getAll().then(setCourses),
scheduleService.getAll().then(schedules)
])
}, [])

Resources