Mapping nested array inside mapped array in React JSX - javascript

I'm trying to write a dashboard sidebar which has a couple of "primary" buttons which (via Bootstrap) collapse a number of "secondary" buttons. I want to be able to easily update and style the whole thing so writing static markup is out of the picture. Here is one object out of the array:
const menuArray = [
{
primaryText: "Applications",
primaryIcon: "fa fa-rocket",
primaryURL: "/applications",
secondaries: [
{
secondaryText: "Softwares",
secondaryURL: "/softwares",
},
{
secondaryText: "Videogames",
secondaryURL: "/videogames",
},
{
secondaryText: "Tools",
secondaryURL: "/tools",
},
],
},
]
And here is the function rendering the array which i'm simply calling in the JSX markup by {renderMenuArray}
const renderMenuArray = menuArray.map((menuItem) => (
<li className="py-2">
<button
data-target={`#${menuItem.primaryText}Submenu`}
data-toggle="collapse"
aria-expanded="false"
className="btn btn-dark btn-menu btn-block pl-0 mb-1"
>
<Link to={menuItem.primaryURL}>
<span className="mr-3">
<i className={menuItem.primaryIcon}></i>
</span>
{menuItem.primaryText}
</Link>
</button>
<div
className="card-body collapse ml-5"
id={`${socialItem.primaryText}Submenu`}
>
<ul className="list-unstyled">
<li>
<Link className="small" to="/applications/softwares">
<span className="mr-3">
<i className="fa fa-chevron-right"></i>
</span>
Softwares
</Link>
</li>
</ul>
</div>
));
I can render the "primary" objects with no problem at all, but I want each "primary" object ( each iteration of the parent array) to each iterate through the count of "secondaries" array ( which is going to be different for each "primary" object).
I'm a beginner developer.

The object passed into the callback function for the map, menuItem has the included property secondaries. You can use map on this property since secondaries is an array, simply place the map inside of your JSX.
<div
className="card-body collapse ml-5"
id={`${socialItem.primaryText}Submenu`}
>
<ul className="list-unstyled">
{menuItem.secondaries.map((subItem) => {
...
})
</ul>
</div>
P.S. you forgot to close your last div tag, and you have a second </li> instead of a </ul>

This'll work (made it a bit more functional, to give you an example of what's possible with functional React components)
const menuArray = [
{
primaryText: "Applications",
primaryIcon: "fa fa-rocket",
primaryURL: "/applications",
secondaries: [
{
secondaryText: "Softwares",
secondaryURL: "/softwares"
},
{
secondaryText: "Videogames",
secondaryURL: "/videogames"
},
{
secondaryText: "Tools",
secondaryURL: "/tools"
}
]
}
];
export default function App(props) {
function renderSecondaryMenu(items) {
return items.map(secondaryItem => {
return (
<li>
<Link className="small" to={secondaryItem.secondaryURL}>
<span className="mr-3">
<i className="fa fa-chevron-right" />
</span>
{secondaryItem.secondaryText}
</Link>
</li>
);
});
}
function renderMenuArray() {
return menuArray.map(menuItem => {
return (
<li className="py-2">
<button
data-target={`#${menuItem.primaryText}Submenu`}
data-toggle="collapse"
aria-expanded="false"
className="btn btn-dark btn-menu btn-block pl-0 mb-1"
>
<Link to={menuItem.primaryURL}>
<span className="mr-3">
<i className={menuItem.primaryIcon} />
</span>
{menuItem.primaryText}
</Link>
</button>
<div
className="card-body collapse ml-5"
id={`${menuItem.primaryText}Submenu`}
>
<ul className="list-unstyled">
{renderSecondaryMenu(menuItem.secondaries)}
</ul>
</div>
</li>
);
});
}
return <div className="App">{renderMenuArray()}</div>;
}

I don't think you gain a lot from defining the sidebar as an object, unless you are using the same Sidebar component with different sets of buttons and you wanna be able to switch between them. JSX is already a markup language, so it's declarative.
You could define simply the Sidebar as
const Sidebar = () => (
<PrimaryItem text="Applications" icon="fa fa-rocket" url="/applications">
<SecondaryItem text="Softwares" url="/softwares" />
<SecondaryItem text="Videogames" url="/videogames" />
<SecondaryItem text="Tools" url="/tools" />
</PrimaryItem>
)
And then implement each component as you need to display them. I think this is cleaner and easier to maintain and modify.

Related

How to handle change of onClick on dropdown menu in react.js

I need your advice and suggest, I have here a react app that has three main component: search bar, cards, and filters. In the filters I have a drop down menu that contain at least 11 vlaues. so what I want that I create function that handle the change when I onclick one of the values and search for it.
I face two problems:
how to create on handle function for 11 elements
how to search for it after one click, because I try to test with one element and change after two clicks
The code:
//Function to create the cards with their property values
function CreateCards(doc) {
return (
<SimpleCard
key={doc.id}
theCardId={doc.id}
cardType={doc.approvetool}
cardNum={doc.num}
cardName={doc.name}
cardDate={format(new Date(doc.date), "dd/MM/yyyy")} // casting the system date to arabic date
// cardCategory={NCARMap.cardCategory}
// cardSource={NCARMap.cardSource}
cardDesc={doc.summary}
cardURL={doc.image}
/>
);
}
//create the class
export default class OfficialDocument extends Component {
//constructor elements
constructor(props) {
super(props);
//initially no data entered
this.state = {
NCARMap: [], // for print cards and changeable
NCARMapAS: [], // for print cards and unchangeable (Default value always)
search_query: "", // store the query word
search_query_drop: "", // to store the query word of drop down menu
noPlaceFound: false, // condition to print message when no result up
size_query: 0, // for set the size or number of docs to return
highlight: "",
};
this.handleChange = this.handleChange.bind(this);
this.DropDownChangeOne = this.DropDownChangeOne.bind(this);
}
componentDidMount() {
//Get NCARMap data, NCARMapAS used for filtering and sorting the cards once get the page
axios
.get(
"http://localhost:9200/ncar_index/ncar/_search?size=100&sort=date:desc"
) //size to define the total docs to get / sort: sort the data for which field you want: asc or desc (desc for reverse)
.then((resp) => {
console.log(resp);
this.setState({
NCARMap: resp.data.hits.hits,
NCARMapAS: resp.data.hits.hits,
});
});
}
//function that handle the onChange on searchbar
handleChange(event) {
console.log(event.target.value);
this.setState({
search_query: event.target.value,
highlight: event.target.value,
});
axios
.get(
`http://localhost:9200/ncar_index/ncar/_search?q=${this.state.search_query}&size=100&sort=date:desc&analyzer=whitespace`
)
.then((resp) => {
if (event?.target?.value == "") {
// if the searchbar empty set default cards
this.setState({
NCARMap: this.state.NCARMapAS,
noPlaceFound: false,
});
} else {
if (resp.data.hits.hits.length > 0 && this.state.search_query != "") {
// go for new search when there is a result and searchbar not empty
this.setState({
NCARMap: resp.data.hits.hits,
noPlaceFound: false,
});
console.log("Successful");
} else {
// if there is no result make noplacefount true to print a message and show no card
if (resp.data.hits.hits.length == 0) {
this.setState({ noPlaceFound: true });
console.log("Length is equal 0");
}
}
}
});
}
DropDownChangeOne(event) {
console.log("lol");
this.setState({
search_query_drop: "approvetool:أمر",
});
console.log(this.state.search_query_drop);
axios
.get(
`http://localhost:9200/ncar_index/ncar/_search?q=${this.state.search_query_drop}&size=100&sort=date:desc`
)
.then((resp) => {
if (this.state.search_query_drop == "") {
// if the searchbar empty set default cards
this.setState({
NCARMap: this.state.NCARMapAS,
noPlaceFound: false,
});
} else {
if (
resp.data.hits.hits.length > 0 &&
this.state.search_query_drop != ""
) {
// go for new search when there is a result and searchbar not empty
this.setState({
NCARMap: resp.data.hits.hits,
noPlaceFound: false,
});
console.log("Successful");
} else {
// if there is no result make noplacefount true to print a message and show no card
if (resp.data.hits.hits.length == 0) {
this.setState({ noPlaceFound: true });
console.log("Length is equal 0");
}
}
}
});
}
render() {
return (
//HTML FILE Converted to React
<div>
<meta charSet="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta httpEquiv="X-UA-Compatible" content="ie=edge" />
<title>منشآت</title>
<link
rel="shortcut icon"
href="/images/favicon.png"
type="image/x-icon"
/>
{/*============= ScrollToTop Section Starts Here =============*/}
<div className="overlayer" id="overlayer">
<div className="loader">
<div className="loader-inner" />
</div>
</div>
<a href="#0" className="scrollToTop">
<i className="fas fa-angle-up" />
</a>
<div className="overlay" />
{/*============= ScrollToTop Section Ends Here =============*/}
{/*============= Header Section Starts Here =============*/}
<header className="header-section">
<div className="container">
<div className="header-wrapper">
<div className="logo-area">
<div className="logo">
<a href="/">
<img src="/images/logo/logo.png" alt="logo" />
</a>
</div>
<div className="support headFont">
الصفحة الرئيسية
</div>
</div>
<ul className="menu headFont">
<li>
{/*Here we need to change the herf link*/}
الوثائق و المحفوظات
</li>
<li>
الأخبار
</li>
<li>
{/*Here we need to change the herf link*/}
التغريدات
</li>
{/*Here we need to change the herf link*/}
{/* I want to know the difference between the two below */}
<li className="d-md-none text-center">
<a href="#0" className="m-0 header-button">
تسجيل دخول
</a>
</li>
</ul>
<div className="header-bar d-lg-none">
<span style={{ backgroundColor: "#00A7CF" }} />
<span style={{ backgroundColor: "#00A7CF" }} />
<span style={{ backgroundColor: "#00A7CF" }} />
</div>
{/* <div class="header-right"> */}
{/*Here we need to change the herf link*/}
{/* تسجيل دخول
</div> */}
</div>
</div>
</header>
{/*============= Header Section Ends Here =============*/}
{/*============= Banner Section Starts Here =============*/}
<section
className="banner-2 bg_img"
data-background="/images/banner/background3.png"
>
<div className="container">
<div className="banner-content-2">
<h1 className="title cl-white">
مرحباً بك في قسم الوثائق والمحفوظات
</h1>
<p className=" cl-white">
يحتوي هذا القسم على الوثائق والمحفوظات المعتمدة من المركز الوطني
للوثائق والمحفوظات
</p>
<form className="search-form round">
<input
type="text"
style={{ textAlign: "right", color: "black" }}
onChange={this.handleChange}
placeholder="... ابحث هنا"
/>
<button type="submit">
<i className="flaticon-search" />{" "}
<span className="d-none d-sm-inline-block">ابحث</span>
</button>
</form>
</div>
</div>
</section>
{/*============= Banner Section Ends Here =============*/}
{/*============= How Search Section Starts Here =============*/}
<div className="how-search-section padding-bottom mt--93">
<div className="container">
<div className="row mb-30-none justify-content-center">
<div className="filter-rtl">
{/*begin::Body*/}
<div className="card-body filters">
{/*begin::Form*/}
<form>
{/*begin::Categories*/}
<div className="form-group mb-11">
<label className="font-size-h3 font-weight-bolder text-dark mb-7">
التنصيفات
</label>
{/* start dropdown menue */}
<div className="dropdown">
<button
className="btn btn-secondary dropdown-toggle"
type="button"
id="dropdownMenuButton"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
أداة الاعتماد
</button>
<div
className="dropdown-menu"
aria-labelledby="dropdownMenuButton"
>
<a
className="dropdown-item"
onClick={this.DropDownChangeOne}
>
أمر ملكي
</a>
<a className="dropdown-item" href="#">
مرسوم ملكي
</a>
<a className="dropdown-item" href="#">
قرار مجلس الوزراء
</a>
<a className="dropdown-item" href="#">
أمر سامي
</a>
<a className="dropdown-item" href="#">
قرار وزاري
</a>
<a className="dropdown-item" href="#">
قرار مجالس وهيئات
</a>
<a className="dropdown-item" href="#">
قرار إداري
</a>
<a className="dropdown-item" href="#">
توجيه سامي
</a>
</div>
</div>
<div className="dropdown">
<button
className="btn btn-secondary dropdown-toggle"
type="button"
id="dropdownMenuButton"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
فئة الوثيقة
</button>
<div
className="dropdown-menu"
aria-labelledby="dropdownMenuButton"
>
<a className="dropdown-item" href="#">
الاتفاقيات و المعادات الدولية العامة
</a>
<a className="dropdown-item" href="#">
الاتفاقيات الدولية الثنائية
</a>
<a className="dropdown-item" href="#">
الاتفاقيات الدولية متعددة الأطراف
</a>
</div>
</div>
</div>
{/* end dropdown menue */}
{/* Start: : DateRangePickerComponent */}
<DateRangePickerComponent></DateRangePickerComponent>
{/* End: : DateRangePickerComponent */}
{/*end::Categories*/}
<button
type="submit"
className="btn btn-primary font-weight-bolder mr-2 px-8"
>
إعادة ضبط
</button>
<button
type="reset"
className="btn-submit btn btn-clear font-weight-bolder text-muted px-8"
>
بحث
</button>
</form>
{/*end::Form*/}
</div>
{/*end::Body*/}
</div>
<div className="general-card">
{this.state.noPlaceFound ? (
<h3 className="noPlaceFound">
<i className="fas fa-exclamation-circle fa-sm WarnIcon"></i>
لا يوجد نتائج
</h3>
) : (
this.state.NCARMap.map((v) => CreateCards(v._source))
)}
{console.log(this.state.NCARMap)}
</div>
</div>
</div>
</div>
{/*============= How Search Section Ends Here =============*/}
{/*============= Footer Section Starts Here =============*/}
<footer className="footer-section pt-70-145">
<div className="dot-slider bg_img" />
<div className="container">
<div className="row mb--50 justify-content-between">
<div className="col-sm-8 col-lg-4">
<div className="footer-widget widget-about"></div>
</div>
</div>
</div>
<div className="footer-bottom cl-white">
<p>جميع الحقوق محفوظة © 2021</p>
</div>
</footer>
{/*============= Footer Section Ends Here =============*/}
</div>
);
}
}
Case 1: Using map in this case:
{
["أمر ملكي", ...].map((text, i) => {
return (
<a key="i" href="" className="dropdown-item" onClick={() => this.DropDownChangeOne("approvetool:أمر")}> // Your query for each element
{text}
</a>
);
});
}
Case 2: Pass query to DropDownChangeOne
DropDownChangeOne(query) {
this.setState({
search_query_drop: query,
});
axios
.get(
`http://localhost:9200/ncar_index/ncar/_search?q=${query}&size=100&sort=date:desc`
)
.then((resp) => {
...
});
}
Firstly change the onclick function
<a className="dropdown-item" onClick={(e) => this.DropDownChangeOne(e, 'approvetool:أمر')} >
Then in function:
DropDownChangeOne(event, value) {
console.log("lol");
this.setState({
search_query_drop: value,
});
console.log(value);
axios
.get(
`http://localhost:9200/ncar_index/ncar/_search?q=${value}&size=100&sort=date:desc`
)
.then((resp) => {
....
....
....
})
}

document.querySelector is not working in my react component

function Slider() {
const track=document.querySelector('.slide__track')//To access the div with class slide track
console.log(track);
return (
<div className="slider">
<i className="fas fa-chevron-left"></i>
<div className="head">
<h1 className="title">Based on your last search</h1>
<h6>View more</h6>
</div>
<div className="slider_container">
<ul className="slider__track">
<li className="slider__items">
<Card />
</li>
</ul>
</div>
<i className="fas fa-chevron-right"></i>
</div>
);
}
i cannot access the div with class slide__track.
What is the problem here?
Or how can i access that element?
Try and use this code in useEffect()
useEffect(() => {
const track = document.querySelector('.slide__track')
// have access to it
}, []);
I think it's because it is running before the DOM has been rendered. Move the track code into useEffect.
import {useEffect} from "react";
function Slider() {
useEffect(() => {
const track=document.querySelector('.slide__track')//To access the div with class slide track
console.log(track);
});
return (
<div className="slider">
<i className="fas fa-chevron-left"></i>
<div className="head">
<h1 className="title">Based on your last search</h1>
<h6>View more</h6>
</div>
<div className="slider_container">
<ul className="slider__track">
<li className="slider__items">
<Card />
</li>
</ul>
</div>
<i className="fas fa-chevron-right"></i>
</div>
);
}
You should be probably be using ref's to access dom elements inside react. see docs: https://reactjs.org/docs/refs-and-the-dom.html - reason being, if you have the below in a loop, or have multiple instances on the page, you'll need to be more careful with the selector approach.
import React, { useRef, useEffect } from 'react';
function Slider() {
const slideTrackRef = useRef(null);
useEffect(() => {
if (slideTrackRef && slideTrackRef.current) {
console.log(slideTrackRef.current);
}
}, [slideTrackRef]);
return (
<div className="slider">
<i className="fas fa-chevron-left" />
<div className="head">
<h1 className="title">Based on your last search</h1>
<h6>View more</h6>
</div>
<div className="slider_container">
<ul className="slider__track" ref={slideTrackRef}>
<li className="slider__items">
<p>tst</p>
</li>
</ul>
</div>
<i className="fas fa-chevron-right" />
</div>
);
}
export default Slider;

JSX conditional modification of outer element

I'm trying to do something like this:
render() {
let a = <a className="nav-link" href={this.props.href} />
if (this.props.hasCollapse) {
a = <a className="nav-link"
href={this.props.href}
data-toggle="collapse"
data-target={this.props.collapseId} />
}
return (
{a}
<i className="fas fa-fw fa-cog"></i>
<span>{this.props.title}</span>
{a}
)
}
where the {a} in the return is replaced by the appropriate version as determined by the hasCollapse property. How might I achieve this?
You should change the properties for the link. For example
function AwesomeLink({ collapseId, hasCollapse, href, title }) {
let hrefProps = {};
if (hasCollapse) {
hrefProps = {
'data-toggle': 'collapse',
'data-target': collapseId,
};
}
return (
<a className='nav-link' href={href} {...hrefProps}>
<i className='fas fa-fw fa-cog' />
<span>{title}</span>
</a>
);
}
I would do this with simple conditionals:
const Link = (props) => {
return (<a className="nav-link"
href={props.href}
data-toggle={props.hasCollapse ? 'collapse' : null}
data-target={props.hasCollapse ? props.collapseId : null}
>
<i className="fas fa-fw fa-cog"/>
<span>{props.title}</span>
</a>
)
}
Also, it looks like you're setting data properties for usage with Bootstrap or a similar library. You may want to look into React Bootstrap and make use of their components designed to be used in React without dealing with messy DOM manipulation.

Build dropdown menu with array passed from parent component [duplicate]

This question already has answers here:
map function not working in React
(3 answers)
Index inside map() function
(4 answers)
Closed 25 days ago.
I am trying to build a dropdown-menu and add data from my array to the dropdown-item. My current code isn't returning anything into my const Users. How can I use the array to add data into the dropdown-item?
UserDisplay component
const UserDisplay = ({ users }) => {
const Users = users.map(user => {
let i = 0;
<a className="dropdown-item" href="#">
{user[i]}
</a>;
i++;
});
return (
<div className="dropdown-menu" id="users">
<a className="dropdown-item" href="#">
Online Users
</a>
<div className="dropdown-divider" />
{Users}
</div>
);
};
Parent Component ChatLayout
return (
<div className="chat navbar fixed-bottom">
<div className="btn-group dropup">
<button
type="button"
className="btn btn-secondary dropdown-toggle"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
Chat
</button>
<UserDisplay users={[this.state.users]} />
</div>
<ul id="messages">
<div />
</ul>
<form onSubmit={this.onSubmit}>
<textarea
name="message"
placeholder="Enter your message here"
autoComplete="off"
type="submit"
onKeyDown={this.onEnterPress}
value={this.state.message}
onChange={this.onChange}
/>
<input type="submit" className="btn btn-info btn-block mt-4" />
</form>
</div>
);
You don't need to define and iterate i.. the .map already keeps track of the array index. Assuming users has data it should work like this...
UserDisplay(users) {
const Users = users.map((user,i) => {
return (
<a className="dropdown-item" key={i} href="#">
{user}
</a>)
});
return (
<div className="dropdown-menu" id="users">
<a className="dropdown-item" href="#">
Online Users
</a>
<div className="dropdown-divider" />
{Users}
</div>
);
};
Working Codeply: https://www.codeply.com/go/DnqpGhozra
You are destructing props object and get users out of it so its fine.
basically map returns a list of array so you dont have return anything.
You need to iterate to the first element of users like
const Users = users[0].map((user,i) => {
console.log(user);
return (
<a className="dropdown-item" key={i} href="#">
{user}
</a>)
});
OR just pass users directly
<UserDisplay users={this.state.users} />

When selecting item from an array renders all items react js

I have the following problem, when selecting an item through a checkbox and pressing a button so that that selected item is added to my new array, all the items are added.
I use the function map and return the component with their respective key and item:
renderFiltersInlcuye() {
if(this.props.incluye){
return this.props.incluye.map((item, index) =>{
return <BusquedaPaquetesFiltrosAplicadosItems
key = {item.value}
item= {item}
onClickClose={this.removeItemIncluye.bind(this,index)}
/>
})
}
}
And in my other component I render the following:
render () {
return(
<ul>
<li>
<span className="badge badge-info " >
<button onClick={this.props.onClickClose} type="button" className="close" ><i className="fas fa-times-circle"></i> </button>
{this.props.item.texto}
</span>
</li>
</ul>
);
}

Categories

Resources