Iterate nested object to access the key and values in react js - javascript

I have a nested object which is getting from api response, need to iterate that nested object based keys and values and the structure like this,
Have tried but not getting the expected output.
Code: Api response
{
"dashboard": "Dashboard",
"users": "Users",
"page_builder": "Page Builder",
"filemanager": {
"brand": "Brand Images",
"manufacturer": "Manufacturer Images"
},
"catalog": {
"catalog_product": "Product"
},
"coupon": "Coupon",
"egift": "E-gifting",
"paymentconfig": {
"configuration": "Gateway Config",
},
"app": {
"app_general": "General Config",
"forceupdate_config": "Force Update Config",
},
"apppayment": "Kapture Category",
"kapturecrm": "Vertical Master",
"phpinfo": "PHP Info"
}
When i tried from my end, am getting the output like this,
Tried sample code:
{Object.keys(roletest).map((key, idx) => (
<CFormCheck
id="validationrole_access"
key={idx}
name="role_access"
label={roletest[key]}
value={roletest[key]}
onChange={handleChange}
aria-describedby="inputGroupPrepend"
/>
))}
My Expected output:
Dashboard
Users
Page Builder
filemanager
Brand Images
Manufacturer Images
catalog
Product
Coupon
E-gifting
paymentconfig
Gateway Config
app
General Config
Force Update Config
Kapture Category
Vertical Master
PHP Info
My output:
Dashboard
Users
Page Builder
Coupon
E-gifting
Kapture Category
Vertical Master
PHP Info
Please do my needs

It sounds like you want to create a nested list. Which can actually be done quite easily. You were on the right track using an object method to iterate over the properties but Object.entries might be a little easier.
So, the trick is to make sure you use separate components for the list, and the list items (List/ListItem). In List, as you iterate over the items check if an item is an object. If it is create a new list with the List component, otherwise return a list item.
const data={dashboard:"Dashboard",users:"Users",page_builder:"Page Builder",filemanager:{brand:"Brand Images",manufacturer:"Manufacturer Images"},catalog:{catalog_product:"Product"},coupon:"Coupon",egift:"E-gifting",paymentconfig:{configuration:"Gateway Config"},app:{app_general:"General Config",forceupdate_config:"Force Update Config"},apppayment:"Kapture Category",kapturecrm:"Vertical Master",phpinfo:"PHP Info"};
function Example({ data }) {
return <List list={data} />;
}
function List({ list }) {
return (
<ul>
{Object.entries(list).map((item, key) => {
return <ListItem key={key} item={item} />;
})}
</ul>
);
}
function ListItem({ item }) {
const [label, value] = item;
if (typeof value === 'object') {
return (
<li>
{label}
<List list={value} />
</li>
);
}
return <li>{value}</li>;
}
ReactDOM.render(
<Example data={data} />,
document.getElementById('react')
);
<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>

Related

Loop through JSON in JSX

I have a tree like JSON structure and it can be n levels deep. Here is an example:
"plot": {
"population": "All",
"gates": [
{
"name": "population1",
"plot": {
"population": "population1",
"gates": [
{
"name": "population3",
"plot": {
"population": "population3",
}
}
]
}
},
{
"name": "population2",
"plot": {
"population": "population4",
}
}
]
};
It starts with plot. The is the top level. A plot can have many gates. These are essentially branches. Each gate has another plot, which can have multiple gates etc.
I want to output the plot.population within JSX wrapped in a div. Here is my attempt (MultiStainState is a JSON file with the above JSON):
function Plot(props) {
...
const renderPlots = (plotObject) => {
console.log("plotObject is ", plotObject);
if (plotObject) {
return (
<>
<div>{plotObject.population}</div>
</>
);
}
{
plotObject.gates.map((gate, gateIndex) => {
plotObject(gate.plot);
});
}
};
return (
<div
style={{
height: "200px",
}}
>
Render Plots:
{renderPlots(MultiStainState)}
</div>
);
}
This output Render Plots:All and none of the child plot populations.
This is presumably because of the return within renderPlots(). I feel like I need to use recursion here (as I have attempted to do). But I cant figure out how....
The main issue with your renderPlots function is that if the given plotObject is non-null, the function just returns plotObject.population and never gets to the recursive step. There are some other issues with that function, but I'm going to offer a rewrite that will address those.
I'm not sure of your exact desired output format, but I'm going to use nesting <div> elements so that the DOM hierarchy matches the JSON structure.
You'll want to return a single JSX object with the recursive step within (recursion looks a bit weird in React/JSX compared to usual programming). I've also split the renderPlots function into a separate component, but that's more of a stylistic choice (I'll leave you to find a better name for the component).
Here's a simple example:
function PlotRender({ plotObject }) {
if (!plotObject) {
return null; // just in case
}
return (
<div>
{plotObject.population}
{plotObject.gates?.map(e => (
<PlotRender key={e.name} plotObject={e.plot}/>
))}
</div>
);
}
Which will render as (for the given sample data):
<div>
All
<div>
population1
<div>
population3
</div>
</div>
<div>
population4
</div>
</div>
Note also that in the outer <Plot> component, you'll likely need to pass MultiStainState.plot as the plotObject prop to <PlotRender> rather than just MultiStainState.
Here is a simple rendering of a recursive component based on this article. It checks if it has a gates array of length > 0 and if so will recursively render the component
sandbox
const plot = {
"population": "All",
"gates": [
{
"name": "population1",
"plot": {
"population": "population1",
"gates": [
{
"name": "population3",
"plot": {
"population": "population3",
}
}
]
}
},
{
"name": "population2",
"plot": {
"population": "population4",
}
}
]
}
const RecursiveComponent = ({ population, gates }) => {
const hasGates = gates && gates.length
return (
<React.Fragment>
<div>
{population}
</div>
{hasGates && gates.map(({name,plot}) => <RecursiveComponent key={name} population={plot.population} gates={plot.gates} />)}
</React.Fragment>
);
};
const App = props => {
return (
<RecursiveComponent population={plot.population} gates={plot.gates} /> //starting point
);
};
ReactDOM.render(<App />, document.getElementById("app"));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="app"></div>

React use map to acces nested objects when object key is unknown

I am working on a NextJs project with a Firebase Store.
I access a collection and get an array with objects with a random key and as value the data:
const data = [
{
randomdocid67233: {
name: "ABC",
latinName: "DEF"
}
},
{
randomdocid6d7dddd233: {
name: "GHI",
latinName: "JKI"
}
}
];
Beause I dont know the randomdocid's I cant figure out how to display the name and latinName. I have made a codesandbox with the problem to illustrate it: https://codesandbox.io/s/spring-fast-w0tt4?file=/src/App.js:56-268
Im sure it's actually easy to do but I don't seem to be able to figure it out. Hopefully someone knows!
Thanks,
Santi.
You can use Object.keys() as solution here
{data.map((item, index)=> {
let key=Object.keys(item)[0]
return <div key={key}> // better to use unique key value then index
{item[key].latinName}
</div>
)}
You need to first get the key inside every object and return the value of that key in the map. Update the code based on your need to render the data after you fetch it. You can do it like this
export default function App() {
const data = [
{
randomdocid67233: {
name: "ABC",
latinName: "DEF"
}
},
{
randomdocid67233: {
name: "GHI",
latinName: "JKI"
}
}
];
const newData = data.map(item => {
const key = Object.keys(item)[0];
return item[key]
})
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
{newData.map((item, index) => (
<div key={index}>
{item.name} {item.latinName}
</div>
))}
</div>
);
}

How to exclude certain item in a map with a certain condition?

I'm developing a menu page, where users can see the menu's items based on their roles.
Currently, I have 3 pages: Bulletin Board, Info Hub and Settings
So naturally, I have 3 roles: Admin Role (can access all 3 of the pages), Bulletin Board (can only access Bulletin Board), Info Hub (can only access Info Hub)
So users can have a different roles, for example, if they have Bulletin Board and Info Hub, then they can access both of them, but not the Settings page (only "Admin Role" can see Settings), so I want to hide the Settings in this menu that I've already developed and rendered using map.
Or if the user has all 3 roles including Admin Role, then they can see everything as well.
I'm taking the loginList prop from an API and passing it into the AllAppsCentre.js to determine which menu items to show, but I just can't figure out the logic to do a filter or indexOf at the map.
In the codesandbox that I've created, the user has all 3 roles.
AllAppsCentre.js(map function to display the menu items)
useEffect(() => {
const loginListFromParent = loginList;
const showAll = loginListFromParent.some(
(item) =>
item.permissionName.includes("Admin Role") &&
item.permissionType.includes("view")
);
const showBulletin = loginListFromParent.some(
(item) =>
item.permissionName.includes("Bulletin Board") &&
item.permissionType.includes("view")
);
const showInfoHub = loginListFromParent.some(
(item) =>
item.permissionName.includes("Info Hub") &&
item.permissionType.includes("view")
);
if (loginListFromParent) {
setShowAll(showAll);
setShowBulletin(showBulletin);
setShowInfoHub(showInfoHub);
}
}, [loginList]);
return (
{AllAppsCentreData
.filter((item) => {
.map((item, index) => {
return (
<Col key={index} xs={6} md={3}>
<div className={item.className}>
<Link to={item.path}>
{item.icon}
<Row>
<span className='apps-title'>
{item.title}
</span>
</Row>
</Link>
</div>
</Col>
)
})}
)
AllAppsCentreData.js
import * as IoIcons from 'react-icons/io'
import * as MdIcons from 'react-icons/md'
export const AllAppsCentreData = [
{
title: 'Bulletin Board',
path: '/bulletinboard',
icon: <IoIcons.IoIosPaper size={80} />,
className: 'row text-center apps-centre-icon'
},
{
title: 'Info Hub',
path: '/infohub',
icon: <MdIcons.MdDeviceHub size={80} />,
className: 'row text-center apps-centre-icon'
},
{
title: 'Settings',
path: '/settings',
icon: <IoIcons.IoMdSettings size={80} />,
className: 'row text-center apps-centre-icon'
},
]
I've been trying to figure out how to deal with this but I just couldn't think of a solution, if all else fails, I might just remove the map method and just copy and paste my AllAppsCentreData's items and move it directly into the AllAppsCentre page instead so I can do ternary operations at the menu items.
If there is any better way to do this menu with the role-based display, feel free to let me know as I also want to learn the optimal way to do something like this.
To start off, there is no variable in your code that is responsible for holding current user's permissions (or I can't see one). So I made it up:
const currentUsersPermissions = useGetPermissions() // get users permissions
// the above can be "admin", "bulletin" or "info-hub"
Then, you need to first filter your array and then map it (currently, you are using map inside of filter). To do it easier and correctly, you could add a property called requiredPermission (or something simillar) to your AllAppsCentreData objects:
{
title: 'Bulletin Board',
path: '/bulletinboard',
icon: <IoIcons.IoIosPaper size={80} />,
className: 'row text-center apps-centre-icon',
requiredPermission: "bulletin" // and info-hub for Info Hub
},
Then, while rendering and mapping, you can do it like this:
return (
{AllAppsCentreData
.filter(
// below, you return true if permission is admin because he can see everything
// and true or false depending if permissions are other than that but
// the same as required by AllAppsCentreData array objects
(item) => currentUsersPermissions === "admin" ? true : currentUsersPermissions === item.requiredPermission
).map((item, index) => {
return (
<Col key={index} xs={6} md={3}>
<div className={item.className}>
<Link to={item.path}>
{item.icon}
<Row>
<span className='apps-title'>
{item.title}
</span>
</Row>
</Link>
</div>
</Col>
)
)
So, to sum up:
I have created a variable to hold current users' permission
I have added a property to each AllAppsCentreData's objects, which contains info about permission required to show it
I have filtered the array right before mapping it and rendering, so it contains only the data I want to render
Also, I think you are lost in using filter method, so perhaps read this and try doing some easier excercises to get a feeling how it works: MDN on Array.filter method. The thing you need to pay attention is that you pass a function into filter, and this function should return true or false depending on whether you want your item included in the output array.

Looping over an array as a value in a JSON (javascript/react)

I have a JSON of team members:
[
{
"name": "Allie Armstrong",
"title": "Head of Finance",
"teams": ["Finance", "Europe"]
},
....]
I map over it, it's fine.
{teamMembersList.map(teamMember => {
return(
<TeamMember
teamMembers={teamMember}
/>
)
})}
But the teams part comes together.
I would like to basically get teammember.teams[0], then teammember.teams[1] etc, so they can be in their own span. Instead of having them as FinanceEurope
The number of elements in this array varies.
TRY :
{ if(teamMembersList.teams && teamMembersList.teams.length) {
teamMembersList.teams.map(teamMember => {
return( <TeamMember teamMembers={teamMember} />)
})
} else {
return('')
}
}
Also you need to check if teamMembersList.teams have some value because if it returns undefined / null OR string then it will not work and will give you error.
In the end I moved my JSON data into the js file as a const.
Then:
{teamMembersList.map(x => {
return x.teams.map(teams => (
<li key={x.id}>
{teams}
</li>
))
})}
It works fine :)

Each child in an array or iterator should have a unique "key" prop in reactjs

I totally confused, because I have mistake in my console and I read reactjs documentation and all tips on stackoverflow, but I can't unterstand what problem is.
I see list of book's titles ({item.volumeInfo.title}), but console has error.
Here is my code:
import React, { Component } from 'react';
import { connect } from 'react-redux';
class BookList extends Component {
renderBook(mainData) {
return(
<ul>
{mainData.items.map((item, i) => {
return <li key={i} item={item}>{item.volumeInfo.title}</li>
})}
</ul>
)
}
render(){
return (
<div className="book-row">
<div className="book-info">
{this.props.book.map(this.renderBook)}
</div>
</div>
);
}
}
function mapStateToProps({book}) {
return {book};
}
export default connect(mapStateToProps)(BookList);
It is part of API response:
{ "kind": "books#volumes",
"totalItems": 288,
"items": [
{
"kind": "books#volume",
"id": "yXlOAQAAQBAJ",
"etag": "CG7f2mQ+7Nk",
"selfLink": "https://www.googleapis.com/books/v1/volumes/yXlOAQAAQBAJ",
"volumeInfo": {
"title": "Nineteenth Century Home Architecture of Iowa City",
"subtitle": "A Silver Anniversary Edition"
I tried to do the next keys:
key={item.etag}, key={i}, key={item.volumeInfo.title}
but error is still here.
Please help.
Thank you so much.
Since you are mapping over book:
{this.props.book.map(this.renderBook)}
the ul also needs a key prop:
renderBook(mainData, bookIdx) {
return(
<ul key={bookIdx}>
{mainData.items.map((item, i) => {
return <li key={i} item={item}>{item.volumeInfo.title}</li>
})}
</ul>
)
}
This is because there will be many ul siblings and React needs to tell the difference (same as with li).
However, it is better (if possible) to use a key that is not the index of the array. So, if book and item have a unique identifier, it would be best to use that.
So, it looks like you have another array outside of the sample data you provided:
[
{ "kind": "books#volumes",
"totalItems": 288,
"items": [
{
Reason is you are using two map, and assigning the key in only one, assign the key to <ul> also, because of that it is giving you the error, Try this:
renderBook(mainData, index) {
return(
<ul key={index}>
{mainData.items.map((item, i) => {
return <li key={i} item={item}>{item.volumeInfo.title}</li>
})}
</ul>
)
}

Categories

Resources