How to map an array that is inside another in react - reactjs

I am having trouble with array mappings. There is an array with data I would like to bind to my component. It looks like the following:
export var arrayObjects: IObjects[] = [
{
name: 'First Object',
icon: 'bi bi-capsule',
subMenu: [
{
name: 'First Sub Object',
path: ''
},
{
name: 'Second Sub Object',
path: ''
}
]
}
]
The array's type is an interface IObjects which contais name, icon, path and subMenu: IObjects. I need to access the subMenu items in order to create a dynamic menu. Althought, everytime I try to map and call the objects there is no return.
Below you'll be able to see how the mapping I made goes:
<ul>
{ arrayNavCadastros.map((item:any, subMenu: any) =>(
<li className="nav-item">
<i className={item.icon}></i>
{item.name}
<ul>
{ subMenu.map((name:any) =>(
{name}
))}
</ul>
</li>
)) }
</ul>
I also tried doing something like {item.subMenu.name} but there is also no return.
I'm new to react but I'm used to do these kind of bindings in Angular, which seems way easier since you should only do a loop inside the other subMenu from the arrayObjects... I would appreaciate any help!
Note: when I map the first properties of the arrayObjects (which, from the example, returns 'First Object') it works as well as all the titles for the sub menus.

so you have array in subMenu and in each iterate you have object of element
so you need to change this :
{ subMenu.map((name:any) =>(
{name}
))}
to this :
{ subMenu.map((sub:any) =>(
{sub.name}
))}

Few pointers
provide a key for items in the array
<ul>
{ arrayNavCadastros.map((item:any, subMenu: any) =>(
<li
key={item.id} // key for React to know which item to udpate, when array updated
className="nav-item"
>
<i className={item.icon}></i>
{item.name}
<ul>
{ subMenu.map((item:any, idx: number) =>(
<li key={idx}> // key and might need li
<a href={item.path}>{item.name}</a>
</li>
))}
</ul>
</li>
)) }
</ul>
or you can create a child component
<ul>
{ arrayNavCadastros.map((item:any, subMenu: any) =>(
<li
key={item.id} // key for React to know which item to udpate, when array updated
className="nav-item"
>
<i className={item.icon}></i>
{item.name}
<SubMenu items={subMenu} />
</li>
)) }
</ul>
//child component
const SubMenu = ({ items = []}) => {
return (
<ul>
{ subMenu.map((item:any, idx: number) =>(
<li key={idx}> // key and might need li
<a href={item.path}>{item.name}</a>
</li>
))}
</ul>
)
}
New React documentation has helpful information on working with array
Hope it helps

Related

React, Each child in a list should have a unique "key" prop

I've encountered "Each child in a list should have a unique "key" prop." error.
My user data is not dynamic so I use static id for unique key prop.
const App = () => {
const meals = [
{
name: "Kimchi Fried Rice",
info: "Delicious kimch and fresh veggies with rice",
price: 10.99,
id: 1,
},
{
name: "Bibimbap",
info: "Fresh veggies with spicy sauce ",
price: 15.99,
id: 2,
},
{
name: "Ddeok bokki",
info: "Chewy rice cake stirred with spicy sauce",
price: 10,
id: 3,
},
{
name: "Son tofu",
info: "The softest tofu soup ever ",
price: 11.99,
id: 4,
},
];
// console.log(meals.map((a) => a.name));
return (
<div>
<Layout food={meals} />
</div>
);
};
const Layout = (props) => {
return (
<Card>
<div className={classes.frame}>
{props.food.map((menu) => (
<ul>
<li className={classes.name} key={menu.id}>
{menu.name}
</li>
<li className={classes.info}>{menu.info}</li>
<li className={classes.price}>{menu.price}$</li>
<Button>+Add</Button>
</ul>
))}
</div>
</Card>
);
};
I initially made this code
<li className={classes.name} key={menu.id}>
{menu.name}
</li>
<li className={classes.info} key={menu.id}>
{menu.info}
</li>
<li className={classes.price} key={menu.id}>
{menu.price}$
</li>
But this error -> Encountered two children with the same key, 1. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.
I tried to use Math.random.().toString() for a key but it just threw a same error.
Could anyone tell me what I am doing wrong?
The list in the error message refers to a list of items rendered by Array.map(). In your case, each menu item is a ul element, so the error message's "child in the list" refers to the ul. Move the key to the ul:
{props.food.map((menu) => (
<ul key={menu.id}>
<li className={classes.name}>
{menu.name}
</li>
<li className={classes.info}>{menu.info}</li>
<li className={classes.price}>{menu.price}$</li>
<Button>+Add</Button>
</ul>
))}

onClick triggering all sub menus instead of only the clicked one

When I click on an item it should expend some sub items. This is working but if I have two, three or four etc. list items then when I click on one it expands ALL of the sub items for all the list items which is obviously not what I want. How can I fix this code to make it only open expand the one I actually clicked on?
const [sideActive, setSideActive] = useState(false);
const toggleSideActive = () => {
setSideActive(!sideActive);
};
html:
<li>
<div
onClick={toggleSideActive}
className={
sideActive
? `${styles.navList__subheading} row ${styles.row__align_v_center} ${styles.navList__subheading__open}`
: `${styles.navList__subheading} row ${styles.row__align_v_center}`
}
>
<span className={styles.navList__subheading_icon}>
<FaBriefcaseMedical />
</span>
<span className={styles.navList__subheading_title}>
insurance
</span>
</div>
<ul
className={
sideActive
? `${styles.subList}`
: `${styles.subList} ${styles.subList__hidden}`
}
>
<li className={styles.subList__item}>medical</li>
<li className={styles.subList__item}>medical</li>
<li className={styles.subList__item}>medical</li>
</ul>
</li>
You can create a local state for tracking the selected id and show the content based on the state. Also, update the selected Id on click of the tab like below.
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [selected, setSelected] = useState("");
const data = [
{
id: 1001,
name: "Tab - 1",
content: ["test1", "test2"]
},
{
id: 1002,
name: "Tab - 2",
content: ["test21", "test22"]
}
];
return (
<div className="App">
<ul class="parent">
{data.map((v) => (
<li onClick={() => setSelected(selected !== v.id ? v.id : "")}>
{v.name}
{selected === v.id && (
<ul class="content">
{v.content.map((val) => (
<li>{val}</li>
))}
</ul>
)}
</li>
))}
</ul>
</div>
);
}
For the below example, click on the tab to see the content.
Working code - https://codesandbox.io/s/long-leaf-cl4cy?file=/src/App.js:0-759
Let me know if you are facing any issues.

Implement Reactjs CRUD Application without unique ID for items

I am creating a CRUD application using Reactjs but i'm having a hard time implementing the DELETE feature. I used the key={item + 1} to generate a unique ID for every stored item in the array but I don't really know how to assess it. Here's my code...
the LIST
<ul>
{this.state.items.map(item => (
<li key={item + 1}>
{item}
<button>edit</button>
<button onClick={this.handleDelete}>delete</button>
</li>
))}
</ul>;
delete function
handleDelete(id) {
let items = this.state.items;
this.setState({
items: this.state.items.filter(item => items.id !== id),
});
}
state
this.state = {
input: '',
items: [],
};
you can pas some info to handleDelete
handleDelete(id){
let items = this.state.items.filter(item => items.id !== id);
this.setState({
items: items
});
}
<ul>
{
this.state.items.map(item => (
<li key={item.id}>
{item}
<button>edit</button>
{/* here I send id of current item */}
<button onClick={()=>this.handleDelete(item.id)}>delete</button>
</li>
))}
</ul>
Update. Ignoring id you can use key => index
handleDelete(key){
let items = this.state.items.filter((item, mapKey) => key !== mapKey);
this.setState({
items: items
});
}
<ul>
{
this.state.items.map((item, key) => (
<li key={key}>
{item}
<button>edit</button>
{/* here I send id of current item */}
<button onClick={()=>this.handleDelete(key)}>delete</button>
</li>
))}
</ul>

One component in every view but with different className

So, I have a progress bar for my application that I have to put it in every other component with different class name to change the color and show the progress of the process.
<ol>
<li className="complete" data-step="1">
</li>
<li className="active" data-step="2">
</li>
<li data-step="3">
</li>
</ol>
Is there a more proper way to prevent copying the bar in every component and just change the class names based on the component it is in?
You can simply make conditional rendering
Example:
<ol>
<li className={condition ? "active" : "complete"} data-step="1">
</li>
<li className={condition ? "active" : "complete"} data-step="2">
</li>
<li data-step="3">
</li>
</ol>
I would do something like this:
const completedStep = 2; // 1, 2, 3 (0 == nothing is completed)
const steps = ['Step 1', 'Step 2', 'Step 3']; // can be whole component, not just a string
return (
<ol>
{steps.map((step, index) => {
return (
let className;
if (index < completedStep) {
className = 'completed';
} else if (index === completedStep) {
className = 'active';
} else {
className = '';
}
<li
key={index}
data-step={index + 1}
className={className}
>
{step}
</li>
);
})}
</ol>
);
The parameters above can be passed down as props to the component.

react dom traverself or parent div targeting?

I have the following in React (using redux):
....
deleteBase(title) {
this.props.dispatch(removeBase(title));
}
render() {
const baseList = this.props.bases.map(base => {
return (
<li key={base.title} className="base">
<Link to={base.title}>{base.title}</Link>
<i
className="fas fa-times"
onClick={title => this.deleteBase(title)}
/>
</li>
);
});
}
In the dispatch function of removeBase I will need to have access to the single base.title from the clicked element. No matter how I try to get the right title transferred over to the function I can't do it.
return (
<ul className="baseWrapper">
{baseList}
<li className="add-list-wrapper">
<AddBase type="base" onAdd={title => this.addBase(title)} />
</li>
</ul>
);

Resources