How can I acces a nested object from my API - arrays

I made an API with spring-boot that is able to create Cook objects with Recipes objects in it. The idea is that one cook has a relationship with (multiple) recipe(s). When I perform a get request to my server, I am able to show the entire (Cook) object with nested (Recipe) object(s). I am also able to render Cook object properties to the browser, e.g name, age etc. However, I am not able to render Recipe properties to the browser from within the Cook object. It always gives me an undefined.
My code for the API request that shows the name from a cook correctly :
const [posts, setPosts] = useState([]);
useEffect(()=> {
axios.get(" http://localhost:9090/api/cooks")
.then(response => {
console.log(response.data) //response.data.recipe.x doesn't seem to work.
setPosts(response.data);
})
.catch(error => {
console.log(error)
})
},[])
return (
<div>
{posts.map(post => <li key={post.id}>{post.name}</li>)}; //post.recipe.x also doesn't work
</div>
)
}
const Index = () => {
return (
<div>
<Alt />
</div>
)
}
The raw JSON data from the server looks like this:
{
"id": 1,
"name": "bart",
"surName": "Janssen",
"gender": "male",
"age": 3,
"recipes": [
{
"id": 1,
"recipeName": "Hamburger",
"portion": 1,
"meat": "Ground meat",
"vegetable": "Tomato, Pickles, other greens",
"other": "salt, pepper, mustard, ketchup",
"instructions": "bla bla"
},
{
"id": 2,
"recipeName": "tomato soup",
"portion": 2,
"meat": null,
"vegetable": "tomato",
"other": "salt, stock",
"instructions": "bla bla"
}
]
}
]
However, I really need to access the Recipe properties too.
I tried accessing recipes through response.data[0], response.data[0][0], response.data[0][1], response.data[[0][0]] However, they all seem to give me an undefined back or an error.
I also tried to use JSON.Stringify(response.data), but this didn't give me any successes too.
I really could use some help. Thanks in advance :)

try to use useEffect with async/await
useEffect(async () => {
const result = await axios(
'http://localhost:9090/api/cooks',
);
setData(result.data.recipes);
});
and another moment, you initialize
const [posts, setPosts] = useState([]);
as an array - useState([ ]), but trying to write a data as object -
{
"id": 1,
"name": "bart",
"surName": "Janssen",
....
}
that's why you need to straight extract recipes
setData(result.data.recipes);

Related

Nested array with React and Firestore - How to display?

I have a nested array within the individual items in a collection.
{
"id": "RpFRcKLIgELlBLgIOJM4",
"Category": "",
"Method": "",
"User": "rWFZhAKk9eOSIIFoP0DqqvrC6WJ3",
"Foods": [
{
"Weight": 1.065,
"label": "Milk - Long Life (1 Litre) (1.065)",
"value": "Milk-LongLife(1Litre)"
},
{
"label": "Blueberries (0.125)",
"value": "Blueberries",
"Weight": 0.125
}
],
"Name": "456",
"Serves": ""
}
{
"id": "WQ6KBLevFsCdV73j4KU4",
"Category": "",
"Name": "123",
"Foods": [
{
"value": "Wine-White",
"label": "Wine - White"
},
{
"value": "Milk-LongLife(1Litre)",
"label": "Milk - Long Life (1 Litre)"
}
],
"Serves": "",
"User": "rWFZhAKk9eOSIIFoP0DqqvrC6WJ3",
"Method": ""
}
const useItemsMeals = () => {
const user = firebase.auth().currentUser;
const user1 = user.uid;
const [items, setItems] = useState([]); //useState() hook, sets initial state to an empty array
useEffect(() => {
const unsubscribe = firebase
.firestore()
.collection("meals")
.where("User", "==", user1)
.orderBy("Category", "asc")
.onSnapshot(snapshot => {
const listItemsMeals = snapshot.docs.map(doc => ({
id: doc.id,
...doc.data()
}));
setItems(listItemsMeals);
console.log(listItemsMeals);
});
return () => unsubscribe();
}, []);
return items;
};
I am having a tough time trying to display items from the 'Foods' array, am currently using for my return:
const listItemMeals = useItemsMeals();
{listItemMeals.map(item => (
<TableRow hover key={item.id} id={item.id}>
<TableCell>{item.Category}</TableCell>
<TableCell>{item.Name}</TableCell>
<TableCell>{item.Foods}</TableCell>
When doing this it tells me:
Error: Objects are not valid as a React child (found: object with keys {label, value}). If you meant to render a collection of children, use an array instead.
I think I need to map this nested array again somehow - but for the life of me - cannot figure it out!
You're almost there.
Your useItemsMeals functions loads the data from Firestore, and sets it correctly into the items variable in the state to render it. But then you use const listItemMeals = useItemsMeals() in your rendering code, which messes things up.
You should not try to return any value from useItemsMeals, and instead solely rely on the items variable from the state to pass the information between the database and the rendered.
So:
// return items; 👈 remove this
---
// const listItemMeals = useItemsMeals(); 👈 remove this
---
{items.map(item => ( // 👈 read items from the state instead
You need to loop over the Foods array again. Like this
const listItemMeals = useItemsMeals();
{listItemMeals.map(item => (
<TableRow hover key={item.id} id={item.id}>
<TableCell>{item.Category}</TableCell>
<TableCell>{item.Name}</TableCell>
{
item.Foods.map(food=>(
<div> //this can div or a new row tag
<TableCell>{food.weight}</TableCell>
<TableCell>{food.label}</TableCell>
<TableCell>{food.value}</TableCell>
</div>))
}

filter array in props

i keep having a problem with filtering an array of objects in props in a nextjs page. i m using below json and coding.
[
{
"id": "1",
"name": "name1",
"category": ["food"]
},
{
"id": "2",
"name": "name2",
"category": ["food", "beverages"]
}]
import React from "react";
const test2 = ({ prods }) => {
return (
<div>
<div>
{prods
.filter((product) => product.category.includes("eve"))
.map((filterarray) => (
<li>
{filterarray.id}
{filterarray.name}
{filterarray.category}
</li>
))}
</div>
</div>
);
};
export async function getStaticProps() {
const prods = (await import("./product.json")).default;
return {
props: {
prods,
},
};
}
export default test2;
listing the full array works. (data comes as 1 string eg 'foodbeverage' but that should still be ok i think)
Filtering on id works fine. But when i try to use include it no longer displays any result.
if anyone could point me to what i m doing wrong. or if i better follow a different approach, any help would be much appreciated.
You need to replace include with some on the category array:
let prods = [
{
id: "1",
name: "name1",
category: ["food"]
},
{
id: "2",
name: "name2",
category: ["food", "beverages"]
}
];
let result = prods.filter((product) =>
product.category.some((pro) => pro.includes("eve"))
);
console.log(result);
Array#includes() does not do partial matches. You need to use String#includes() on each element in the array.
You could do this by using Array#some() in the filter.
prods.filter((product) => product.category.some(cat=>cat.includes("eve")))

Mapping API response to JSX

I have a JSON response in the form:
[
{
"author": 2,
"title": "how to draw",
"slug": "how-to-draw",
"content": "second attempt",
"status": 0,
"date_created": "2020-11-28T20:25:41.650172Z",
"date_posted": "2020-11-28T20:25:41.650172Z",
"tags": "none"
},
{
"author": 1,
"title": "Admin test post",
"slug": "admin-test-post",
"content": "This is just a test of the content field!\r\n\r\nParagraph 2",
"status": 4,
"date_created": "2020-11-16T21:02:02.521705Z",
"date_posted": "2020-11-16T21:37:40.477318Z",
"tags": "a b c"
}
]
And I am struggling to map the response correctly to a series of divs in ReactJS. The idea would be to have a div for each post, each with the author, title, etc. of the post. I can use post[0].title to take the title of the first element of the array. Am I missing something obvious?
import React, {useState} from 'react';
import axios from 'axios';
const Django = () => {
const [posts, setPosts] = useState([]);
// proxy set to http://127.0.0.1:8000/
const apiLink = "/api/post/";
const fetchData = async () => {
const res = await axios.get(`${apiLink}`, { headers: { Accept: "application/json"} });
console.log(res.data);
setPosts([res.data]);
}
return (
<div>
<h1>Posts:</h1>
<button onClick={fetchData}>Load jokes</button>
{posts.map((post, key) => {
//console.log(joke);
return (
<div className="data" key={key}>
{post.title}
{key}
</div>
)})}
</div>
);
}
export default Django;
It is not exactly clear what your problem is, but I suppose you're confused about why you aren't getting any data by directly calling {post.title} in the map body. That's because you're putting the result from your fetch, which is already an array into another array: setPosts([res.data]);. Just change the state setter to setPosts(res.data); and everything in the map should work fine.

how do I Mock API and following the same approach as my static array?

I have a React hooks component, which uses an HTML div-alike table to render data, and the data is being fetched from the server. I need to test the component to see if the table has data by mocking the API call. Below is the current code. I want to remove the use of arr completely and make avoid getting {users} rendered as text/plain as you can you in the below image
const arr = [
{
"demo": [
{
"_id": "T0810",
"title": "Historian",
"tags": [
"demo"
],
"queries": [],
},
{
"_id": "T0817",
"title": "book",
"tags": [
"demo"
],
"queries": [],
},
],
"demo_2": [
{
"_id": "T0875",
"title": "Program",
"tags": [
"demo_2",
"Control"
],
"queries": [],
},
{
"_id": "T0807",
"title": "Interface",
"tags": [
"demo_2"
],
"queries": [],
}
]
}];
const keys = Object.keys(arr[0]);
export default function Demo () {
const [isModalOpen, setModalIsOpen] = useState(false);
const [users, setUsers] = useState([]);
const handleOnClick = async () => {
try {
const { data } = await axios.get('https://run.mocky.io/v3/0d7aa6e3-fc01-4a47-893d-7e1cc3013d4e');
setUsers(data);
// Now that the data has been fetched, open the modal
setModalIsOpen(true);
} catch (err) {
console.error("failed", err);
}
};
return (
<div className="container">
<>
{keys.map((key) => (
<div className="col" key={key}>
<div className="row">{key}</div>
{arr[0][key].map((item) => (
<div className="row" key={item.technique_id} onClick={() => handleOnClick(item)}>{item.technique}</div>
))}
</div>
))}
</>
{isModalOpen && <Modal onRequestClose={() => setModalIsOpen(false)} data={users}/>}
</div>
);
}
Second attempt...Assuming I understand the ask then a package like Nock can do the trick (https://github.com/nock/nock).
The approach is to allow Nock to mock the API and return the result locally without calling the server. In your case it will be something like:
const nock = require('nock');
const arr = [...]; // your original definition of arr
const scope = nock('https://run.mocky.io')
.get('/v3/0d7aa6e3-fc01-4a47-893d-7e1cc3013d4e')
.reply(200, arr);
// The rest of your code "as is"
...
This setup of Nock will intercept every HTTP call to https://run.mocky.io and will specifically return the content of arr in response to a GET /v3/0d7aa6e3-fc01-4a47-893d-7e1cc3013d4e.
Typically, I would not put this code in the main code.
Instead you can use it as part of a testing script that runs without dependency on an active server. E.g. testing during build.

How to access data returned from props

I new to nextjs and react. I am using the nextjs to fetch data from api and display it in view. this is my code.
const Index = props => (
<Layout>
<h1>Events</h1>
<ul>
{props.eventList.map(({ venue }) => (
<li key={venue.name}>
<a>{venue.name}</a>
</li>
))}
</ul>
</Layout>
);
Index.getInitialProps = async function() {
const res = await fetch("api link here");
let data = await res.json();
console.log(`Show data fetched. Count: ${data.events.length}`);
return {
eventList: data.events
};
};
Now my api return data like this
[
{
"id": 4303920,
"name": "Test Event",
"venue": {
"id": 1610,
"name": "Eiffel Tower Viewing Deck at Paris Las Vegas",
}
},
{
"id": 4323,
"name": "Test Event 2",
"venue": {
"id": 1610,
"name": "Eiffel Tower Viewing Deck at Paris Las Vegas",
}
}
]
I am able to access the venue details by props.eventList.map function but i am not able to access the id and name.
In your map instead of ({venue}) => just do (item) => your issue is you are getting each element and only extracting venue from it instead of the entire element. Once you have item you can say item.id, item.venue etc.
You should not put {venue} inside your map function but pass the event like this (event) then you will have even.name and event.is and you will have event.venue.id and event.venue.name

Resources