I am building a React-App and I hae a problem working with json/array
an the .map()-function
.
My serverside json object looks like :
let jsonlist = [
{
Title: "first title",
Listofitems: ["Item 01", "Item 02", "Item 03"]
},
{
Title: "second title",
Listofitems: ["Item 04", "Item 05", "Item 06"]
}
]
And a sample of my code clientside looks like this:
const [lists, setmylist] = useState([]);
const fetchFact = () => {
fetch("http://myapi:4000/getmyList")
.then((response) => response.json())
.then((data) => setmylist(data));
}
useEffect(() => {
fetchFact()
}, []);
return (
<div className='container'>
{lists.map(singleobject => {
var test = Object.entries(singleobject);
{ console.log(test) }
{ console.log(test[0][0]) }
<p>{test[0][0]}</p>
})}
</div>
);
If I run this I get as { console.log(test)}
:
[
[
"Title",
"first title"
],
[
"Listofitems",
[
"Item 01",
"Item 02",
"Item 03"
]
]
]
But the <p>{test[0][0]}</p>
not gets displayed. If I change
{lists.map(singleobject => {})}
to something like
{projects.map(singleproject => (
<p key={hereineedauniqueid}>Just checkin</p>
))}
Just checkin
gets displayed 2 times just like I want but i don´t know how to acess my values from the json/array
. Do i need to change the structure of the json/array
or do i need to change my code?
I think I need to use the {lists.map(singleobject => {})}
function because i want to create a Table and a react-bootstrap/Modal
for every elment in the json/array
and want to display the values from the json/array
in every specific modal
Thanks for your help
CodePudding user response:
You are mixing up ordinary JavaScript syntax with JSX syntax in this callback here:
.map(singleobject => {
var test = Object.entries(singleobject);
{ console.log(test) }
{ console.log(test[0][0]) }
<p>{test[0][0]}</p>
})}
The syntax of { expression }
is useful inside JSX when interpolating an expression into a React component (or when debugging, for side-effects). But you're not in the context of JSX here - you're in a plain JavaScript function. Your {}
s are denoting plain blocks.
{ console.log(test) }
is like doing
if (true) {
console.log(test)
}
Then, if you were in JSX, putting
<p>{test[0][0]}</p>
right next to the interpolated logs would work - but again, you're not in JSX. You'd need to return the <p>
to see the result. But instead of doing that, if you want to show what the value contains and iterate over each entry, consider returning a .map
over the entries of the object.
const App = () => {
const lists = [
{
Title: "first title",
Listofitems: ["Item 01", "Item 02", "Item 03"]
},
{
Title: "second title",
Listofitems: ["Item 04", "Item 05", "Item 06"]
}
];
return (
<div className='container'>
{lists.map(singleobject => Object.entries(singleobject)
.map(([key, value]) => (
<div>
<div>Key: {key}</div>
<div>Value is a: {typeof value}</div>
</div>
))
)}
</div>
);
};
ReactDOM.createRoot(document.querySelector('.react')).render(<App />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div class='react'></div>
But because the keys are the same for each object, looping over entries seems superfluous. It'd be easier to reference the .Title
and Listofitems
directly.
const App = () => {
const lists = [
{
Title: "first title",
Listofitems: ["Item 01", "Item 02", "Item 03"]
},
{
Title: "second title",
Listofitems: ["Item 04", "Item 05", "Item 06"]
}
];
return (
<div className='container'>
{lists.map(singleobject => (
<div>
<div>{singleobject.Title}</div>
<div>{singleobject.Listofitems.join(', ')}</div>
</div>
))}
</div>
);
};
ReactDOM.createRoot(document.querySelector('.react')).render(<App />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div class='react'></div>
CodePudding user response:
I don't see a need to use Object.entries
on each object. I would leave them as objects and work with their key/value
pairs to render things to the DOM.
For instance, with your jsonlist
:
let jsonlist = [
{
Title: "first title",
Listofitems: ["Item 01", "Item 02", "Item 03"]
},
{
Title: "second title",
Listofitems: ["Item 04", "Item 05", "Item 06"]
}
]
You can simply render the first object like this:
return (
<div className='container'>
{lists.map(singleobject => {
return (
<div>
<h3>{singleobject.Title}</h3>
<ol>
{singleobject.Listofitems.map(item => {
return <li>{item}</li>
})
</ol>
</div>
)
})}
</div>
If you are going to build a component for each of the objects in your list, you can pass the information as props to the component within your lists.map(singleobject => {})
function.
CodePudding user response:
Just do it like this
return (<p>{test[0][0]}</p>);
you just need to know the basics of arrow function
CodePudding user response:
It may be easier to think about the problem if you break down the interface into smaller components. You know that data
is an array of objects that you'll have to map
over, but your listofitems
is also an array so it makes sense that you would also map
over those instead.
So within your container map
over the data you get from the endpoint to produce a new list for each object (with a title and a further list of items). You can use a separate List
component for this into which you pass the current iterated object. (You mentioned in your question that you'd like a table, but since I don't know the format of the table you want, a list is easier to produce - but the principles still stand. Replace List
and ListItem
with a table row component, and a table cell component).
And in your List
component you can create an element for the title, and a list element and then - within that - map over the object's list of items using a ListItem
component.
Note, by doing this you'll need to add a key
attribute to the elements that you're iterating over (or React will give you a warning). So with this in mind this example refactors the data to give each object an id
, and also creates objects for each item each with their own id too.
For example:
[{ id: 1, title: 'first title', items: [{ id: 1-1, value: 'Item 01' }...]
const data=[{title:"first title",items:["Item 01","Item 02","Item 03"]},{title:"second title",items:["Item 04","Item 05","Item 06"]}];
// Create an updated dataset with ids
const updated = data.map((obj, id) => {
obj.id = id 1;
obj.items = obj.items.map((item, id) => {
return { id: `${obj.id}-${id 1}`, value: item };
});
return obj;
});
// For this example passing in the data to
// the component rather than fetching it
// `map` over the array and, for each obj create
// a list with the List component
function Example({ data }) {
return (
<div className="container">
{data.map(obj => {
return <List key={obj.id} obj={obj} />
})}
</div>
);
}
// The List component accepts an object, and
// we can destructure the title, and items, from
// the object. We create a title, and a list element
// and then `map` again over the items array to create
// a set of list items with the ListItem component
function List({ obj }) {
const { title, items } = obj;
return (
<div className="list">
<div className="title">{title}</div>
<ul>
{items.map(item => {
const { id, value } = item;
return <ListItem key={item.id} item={value} />
})}
</ul>
</div>
);
}
// Accepts an item, and returns
// a list item element
function ListItem({ item }) {
return <li>{item}</li>;
}
ReactDOM.render(
<Example data={updated} />,
document.getElementById('react')
);
.title { text-transform: uppercase; }
ul { list-style: none; margin-left: 0px; padding: 0px; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>