How to render an array of JSX fragments? - reactjs

I'm relatively new to React and trying to render an array of JSX fragments that all look like this.
<>
<div className="tourCard" key={address.name}>
<h3 className="tourCard__header">{address.name}</h3>
<div className="tourCard__capacity">Capacity: {foundUser.capacity}</div>
{foundUserAddress}
{foundUserAddress2}
<section className="tourCard__cityStateZip">
<div className="tourCard__city">{foundUser.city}</div>
<div className="tourCard__state">{foundUser.state}</div>
{foundUserZip}
</section>
<h5 className="tourCard__blurbHeader">About Us</h5>
{foundUserBlurb}
{socialMediaButtonClicked ? (
<>
{foundUserSocialMedia}
</>
) : (
<>
<button onClick={() => {
socialMediaButtonClicked = true
}}>Social media</button>
</>
)}
</div>
</>
I'm pushing them into an array exactly as above, and I'm struggling with the right format in the return statement below to get them to render.
I've tried
return (
<>
<section className="tourSection">
{tourCards}
</section>
</>
)
and
return (
<>
<section className="tourSection">
{tourcards.map(tourCard => {
return(
{tourCard}
)
}
</section>
</>
)
But neither were successful. Where am I going wrong?
Complete page code below:
import React, {useContext, useState} from "react"
import { withGoogleMap, GoogleMap, Marker, InfoWindow } from 'react-google-maps'
import { AddressContext } from "../addresses/AddressProvider"
import { UserContext } from "../user/UserProvider"
import { render } from '#testing-library/react'
export default (props) => {
const { addresses } = useContext(AddressContext)
const { users } = useContext(UserContext)
let tourCards = []
const PlanMap = withGoogleMap(props => (
<GoogleMap google={window.google} defaultCenter = { { lat: 39.5, lng: -98.35 } }
defaultZoom = { 4 }>
{
addresses.map(address =>
<>
<Marker
key={address.id}
position={{
lat: address.address.lat,
lng: address.address.lng
}}
label={{
text: "Hello World!",
fontFamily: "Arial",
fontSize: "14px",
}}
>
<InfoWindow
key={address.id}>
<>
<span>{address.name}</span>
<div>
<button onClick={() => {
let foundUser = users.find(user => user.name === address.name)
let foundUserAddress = ""
if (foundUser.hasOwnProperty("address") && foundUser.hasOwnProperty("addressPublic") && foundUser.addressPublic === true) {
foundUserAddress = <>
<div className="tourCard__address">{foundUser.address}</div>
</>
}
let foundUserAddress2 = ""
if (foundUser.hasOwnProperty("address2") && foundUser.hasOwnProperty("address2Public") && foundUser.address2Public === true) {
foundUserAddress2 = <>
<div className="tourCard__address2">{foundUser.address2}</div>
</>
}
let foundUserZip = ""
if (foundUser.hasOwnProperty("zip") && foundUser.hasOwnProperty("zipPublic") && foundUser.zipPublic === true) {
foundUserZip = <>
<div className="tourCard__zip">{foundUser.zip}</div>
</>
}
let foundUserBlurb = ""
if (foundUser.hasOwnProperty("blurb") && foundUser.hasOwnProperty("blurbPublic") && foundUser.blurbPublic === true) {
foundUserBlurb = <>
<div className="tourCard__blurb">{foundUser.blurb}</div>
</>
}
let socialMediaButtonClicked = false
let foundUserWebsite = ""
let foundUserFacebook = ""
let foundUserInstagram = ""
let foundUserTwitter = ""
let foundUserSocialMedia = <>
<section className>
{foundUserWebsite}
{foundUserFacebook}
{foundUserInstagram}
{foundUserTwitter}
</section>
</>
if (foundUser.webPublic === true) {
if (foundUser.hasOwnProperty("website")) {
foundUserWebsite = <>
<div className="tourCard__website">{foundUser.website}</div>
</>
}
if (foundUser.hasOwnProperty("facebook")) {
foundUserFacebook = <>
<div className="tourCard__facebook">{foundUser.facebook}</div>
</>
}
if (foundUser.hasOwnProperty("instagram")) {
foundUserInstagram = <>
<div className="tourCard__instagram">{foundUser.instagram}</div>
</>
}
if (foundUser.hasOwnProperty("twitter")) {
foundUserInstagram = <>
<div className="tourCard__twitter">{foundUser.twitter}</div>
</>
}
}
tourCards.push(
<div className="tourCard" key={address.name}>
<h3 className="tourCard__header">{address.name}</h3>
<div className="tourCard__capacity">Capacity: {foundUser.capacity}</div>
{foundUserAddress}
{foundUserAddress2}
<section className="tourCard__cityStateZip">
<div className="tourCard__city">{foundUser.city}</div>
<div className="tourCard__state">{foundUser.state}</div>
{foundUserZip}
</section>
<h5 className="tourCard__blurbHeader">About Us</h5>
{foundUserBlurb}
{socialMediaButtonClicked ? (
<>
{foundUserSocialMedia}
</>
) : (
<>
<button onClick={() => {
socialMediaButtonClicked = true
}}>Social media</button>
</>
)}
</div>
)
console.log(tourCards)
debugger
}}
>
Add to tour
</button>
</div>
</>
</InfoWindow>
</Marker>
</>
)
}
</GoogleMap>
));
return (
<>
<div>
<PlanMap
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `400px`, width: `400px` }} />}
mapElement={<div style={{ height: `100%` }} />}
/>
</div>
<section className="tourSection">
{tourCards}
</section>
</>
)
}

<> ... </> is not an array. Depending on how and where your tourCards are created you could do something like:
var tourCards = [];
tourCards.push(<div className="tourCard"> ... </div>);
// ... more tourCards.push()
return (
<>
<section className="tourSection">
{tourCards}
</section>
</>
);

As I said in the comments, I think your problem is just that your component doesn't re-render after you update tourCards. And even more, since tourCards is redefined as an empty array each time your component re-renders, it won't contain whatever you try to put into it. So you should use useState to keep track of it.
When working with arrays using react hooks it's a good idea to treat them as immutable and create a new array each time you set the state using array spreading
const [arr, setArr] = useState([])
// somewhere in an effect or callback
setArr(prevArr => [...prevArr, newValue]) // this is the same as arr.push
setArr(prevArr => [newValue, ...prevArr]) // this is the same as arr.unshift
setArr(prevArr => [...newArr, ...prevArr]) // this is arr.concat
Here's a working example you should be able to run right here on SO:
const {useState} = React
function App() {
const [tourcards, setTourcards] = useState([])
const addTourCard = function() {
// Do whatever you need to do here
const card = <div key={`card-${tourcards.length}`}>Card</div>
setTourcards(cards => [...cards, card])
}
return (
<div>
<button onClick={addTourCard}>Click Me</button>
<div>{tourcards}</div>
</div>
)
}
ReactDOM.render(<App />, document.querySelector("#root"));
<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="root"></div>

Related

<button> that changes "navigation titles" with "content field" / passing data from child to parent [duplicate]

In <Nav/> component, "click event" on chevron <button>, triggers nextTitle(length) function in useNextTitle.js custom hook. This function sets the value of val, which is being returned by useNextTitle.js. How to pass that new val to App.js ?
Bigger picture: I want to change the display between <Dod /> and <Analogia /> (in App.js), after "click event" in <Nav /> (figured out val would be helpful for that, as a parameter in Conditional Statement).
Functionality I`m trying to achieve is visualized on the website I done with vanilla Java Script : link (the blue navigation, changes "main pages" with "titles" when chevron clicked)
App.js
import Nav from "./components/Nav";
import Dod from "./components/Dod";
import Analogia from "./components/Analogia";
function App() {
return (
<div className="App">
<Nav />
<Dod />
<Analogia />
</div>
);
}
export default App
Nav.js
import useNextTitle from './useNextTitle';
import './Nav.css';
const Nav = () => {
const navData = [
{id: 0, text: "DOD"},
{id: 1, text: "analogia"}
]
const length = navData.length;
const { val, nextTitle } = useNextTitle();
return (
<nav>
<div>
{/* titles */}
<ul>
<li key="li1">
{navData.map((title, index) => {
return (
<div
className={index === val ? "active" : "display-none"} key={title.id}>
{title.text}
</div>
)
})}
</li>
</ul>
{/* chevron button */}
<div>
<button onClick={() => nextTitle(length)}>
<span className="material-icons">
chevron_right
</span>
</button>
</div>
</div>
</nav>
)
}
export default Nav
useNextTitle.js
import { useState } from 'react';
const useNextTitle = () => {
const [val, setVal] = useState(0);
const nextTitle = (length) => {
setVal(val === length -1 ? 0 : val + 1 )
console.log("hook vav = " + val)
}
return { val, nextTitle }
}
export default useNextTitle;
Move the useNextTitle hook/state up to App and pass val and nextTitle down to Nav to toggle/update the state. Use val to conditionally render Dod and Analogia.
Example:
function App() {
const { val, nextTitle } = useNextTitle();
return (
<div className="App">
<Nav {...{ val, nextTitle }} />
{val === 0 && <Dod />}
{val === 1 && <Analogia />}
</div>
);
}
...
const Nav = ({ val, nextTitle }) => {
const navData = [
{ id: 0, text: "DOD" },
{ id: 1, text: "analogia" }
];
const length = navData.length;
return (
<nav>
<div>
{/* titles */}
<ul>
<li key="li1">
{navData.map((title, index) => {
return (
<div
className={index === val ? "active" : "display-none"}
key={title.id}
>
{title.text}
</div>
);
})}
</li>
</ul>
{/* chevron button */}
<div>
<button onClick={() => nextTitle(length)}>
<span className="material-icons">chevron_right</span>
</button>
</div>
</div>
</nav>
);
};

routing on click and pass a data also though routing : react js

i am tryin to route to localhost/detiled on click on the <li>
when i type in url localhost/detiled my <InnerDetail /> is loading i want the same when i click on the <li> tag
and also how can i access suggestion.id in <InnerDetail />
search.js
<Link to={{ pathname:"/detiled" }}
>
<li
style={styles.listyle}
// onMouseOver={{ background: "yellow" }}
key={index}
className={classname}
onClick={finddoctor(suggestion.id)}
>
{suggestion.firstname}
</li>
</Link>
in my path.js i have this
path.js
<Route path="/detiled">
<InnerDetail />
</Route>
import PersonPhoto from "../img/doctor.png";
import { useLocation } from "react-router-dom";
import axios from "axios";
import React, { useState, useEffect } from "react";
export default function Detail(props) {
const location = useLocation();
const [detail, setDetail] = useState(false);
//const data3 = location.state.data;
//const doctor_id = data3.id;
const inipath = window.location.pathname;
const path = inipath.split("/cdoctor/");
const [data3, setdata3] = useState([]);
console.log(path[1]);
useEffect(() => {
const config = {
headers: {
Authorization: `token ` + localStorage.getItem("token"),
},
};
//remove this date after setting up the admin pannel
axios
.get(
"doctor-filter/?id=" + path[1],
config
// config
)
.then((res) => {
console.log(res.data);
// setdata3(res.data);
});
});
return (
<>
{/* {detail ? (
<InnerDetail />
) : (
<> */}
<h4 style={{ textAlign: "left", marginLeft: "10px" }}>
Top Specialities <i className="fa fa-angle-right"></i> /doctor/Detail
</h4>
<hr
style={{
margin: "30px 10px",
background: "#fff",
height: "1px",
border: "none",
}}
/>
<div style={styles.wrapper}>
{/* begin header */}
<div style={styles.pheader}>
<div>
<div style={styles.pname}>
<strong style={styles.namealigner}>Dr {data3.firstname}</strong>
<br />
<strong style={styles.namealigner}> {data3.lastname}</strong>
</div>
<hr style={styles.hr} />
<div style={{ textAlign: "left", fontSize: "12px" }}>
<span>
{" "}
{data3.speciality} | {data3.experience} years of Experience
</span>
</div>
<hr style={styles.hr} />
</div>
<div>
<img style={{ height: "100px" }} src={PersonPhoto} alt="" />
</div>
</div>
{/* end header */}
{/* begin detail */}
<div style={styles.iflex}>
<div style={styles.innerflex}>
<i className="fa fa-graduation-cap"></i>
<strong> Education</strong>
<br />
<small> {data3.qualification}</small>
</div>
<div style={styles.innerflex}>
<i className="fa fa-map-marker"></i>
<strong> Location</strong>
<br />
<small>{data3.location}</small>
</div>
</div>
<div style={styles.iflex}>
<div style={styles.innerflex}>
<i className="fa fa-user"></i>
<strong> Registeration Number</strong>
<br />
<small>{data3.councilRegNo}</small>
</div>
<div style={styles.innerflex}>
<i className="fa fa-globe"></i>
<strong> Language</strong>
<br />
<small>English Malayalam</small>
</div>
</div>
{/* end detail */}
</div>
</>
// )}
// </>
);
}
this is the dashbord here serch is common in all pages
<Search />
<Specialities />
import React from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faSearch } from "#fortawesome/free-solid-svg-icons";
import { useState, useEffect } from "react";
import axios from "axios";
import { Link } from "react-router-dom";
import { useHistory } from "react-router-dom";
const initialState = {
idaddProducts: "",
};
const Searchclients = () => {
const history = useHistory();
const [showResults, setShowResults] = React.useState(true);
const [poName, pnName] = React.useState(initialState);
const [showSerch, setShowSerch] = React.useState([]);
const [detail, setDetail] = useState(false);
const [inputValue, setInputValue] = React.useState("");
const [filteredSuggestions, setFilteredSuggestions] = React.useState([]);
const [selectedSuggestion, setSelectedSuggestion] = React.useState(0);
const [displaySuggestions, setDisplaySuggestions] = React.useState(false);
function finddoctor(e) {
console.log(e);
setDetail(true);
}
const suggestions = [];
showSerch.forEach(function (data) {
suggestions.push(data);
});
const onChange = (event) => {
const value = event.target.value;
setInputValue(value);
setShowResults(false);
//console.log(strung.substring(1, strung.length - 1));
// console.log(JSON.stringify(suggestions));
// var suggestions = suggestions.substring(1, suggestions.length - 1);
// newObj = suggestions;
//console.log(suggestions);
//setFilteredSuggestions({ ...poName, idAddProducts: idAddProducts });
const filteredSuggestions = suggestions.filter(
(suggestion) =>
suggestion.firstname
.toString()
.toLowerCase()
.includes(value.toLowerCase()) ||
suggestion.id.toString().toLowerCase().includes(value.toLowerCase())
);
// const filteredSuggestions = suggestions.filter((suggestion) =>
// suggestion.toString().toLowerCase().includes(value.toLowerCase())
// );
setFilteredSuggestions(filteredSuggestions);
setDisplaySuggestions(true);
};
const onSelectSuggestion = (index) => {
setSelectedSuggestion(index);
setInputValue(filteredSuggestions[index]);
setFilteredSuggestions([]);
setDisplaySuggestions(false);
};
const SuggestionsList = (props) => {
// console.log(props);
const {
suggestions,
inputValue,
onSelectSuggestion,
displaySuggestions,
selectedSuggestion,
} = props;
if (inputValue && displaySuggestions) {
if (suggestions.length > 0) {
return (
<ul className="suggestions-list" style={styles.ulstyle}>
{suggestions.map((suggestion, index) => {
// console.log(suggestions);
const isSelected = selectedSuggestion === index;
const classname = `suggestion ${isSelected ? "selected" : ""}`;
return (
<Link to={`/detiled/${suggestion.id}`}> //this isthe link
<li
style={styles.listyle}
// onMouseOver={{ background: "yellow" }}
key={index}
className={classname}
>
{suggestion.firstname}
</li>
</Link>
);
})}
</ul>
);
} else {
return <div>No suggestions available...</div>;
}
}
return <></>;
};
useEffect(() => {
axios
.get("all-doctors-list/")
.then((res) => {
const data = res.data;
// pnName(data.data);
// var stringdata = data;
setShowSerch(data);
//console.log(stringdata);
});
// setShowSerch(data);
}, []);
return (
<>
<div className="note-container" style={styles.card}>
<div style={styles.inner}>
<p style={{ textAlign: "left" }}>Search Doctors</p>
<form className="search-form" style={{}}>
{showResults ? (
<FontAwesomeIcon
style={{ marginRight: "-23px" }}
icon={faSearch}
/>
) : null}
<input
onChange={onChange}
value={inputValue}
style={styles.input}
type="Search"
/>
<SuggestionsList
onClick={() => this.nextPath("/detiled")}
inputValue={inputValue}
selectedSuggestion={selectedSuggestion}
onSelectSuggestion={onSelectSuggestion}
displaySuggestions={displaySuggestions}
suggestions={filteredSuggestions}
/>
</form>
</div>
</div>
</>
);
};
export default Searchclients;
You can add a route for detail view of the suggestion.
<Switch>
<Route path="/detailed" exact>
list suggestions component
</Route>
<Route path="/detailed/:id" exact}>
DetailComponent
</Route>
</Switch>
Then the link will become:
<Link to={`/detailed/${suggestion.id}`}>
<li
style={styles.listyle}
// onMouseOver={{ background: "yellow" }}
key={index}
className={classname}
>
{suggestion.firstname}
</li>
</Link>
And in DetailComponent you can get the id from route params.
import { useParams } from "react-router-dom";
...
const { id } = useParams(); // the id passed in url

Too many re-renders. React limits (Next JS)

I have an error with the code below
( Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.)
The goal is to add a span on each item excepted the last one.
What is the best way to do that?
const Section = () => {
const [lastItem, setlastItem] = React.useState(false);
// rendu des Sections
const sectionLentgh = Data.sections.length;
const sectionList = Data.sections.map((item, i) => {
// AJout du séparateur
if (sectionLentgh === i + 1) {
setlastItem(false);
} else {
setlastItem(true);
}
console.log(i);
return (
<div>
<h2>{item.title}</h2>
<img src={`/images/${item.image}`}></img>
<span style={{ backgroundImage:`url(/images/${item.image})` }}></span>
<p dangerouslySetInnerHTML={{ __html: item.description }} />
<span className={`${ lastItem ? styles.separator : '' }`}></span>
</div>
);
})
return (
<>
<div className={styles.sections}>
{sectionList}
</div>
</>
);
};
export default Section;
Just use the length of the array and compare it to the index of the iteration:
const Section = () => {
const sectionLength = Data.sections.length;
const sectionList = Data.sections.map((item, i) => {
const lastItem = i === (sectionLength - 1);
return (
<div>
<h2>{item.title}</h2>
<img src={`/images/${item.image}`}></img>
<span style={{ backgroundImage: `url(/images/${item.image})` }}></span>
<p dangerouslySetInnerHTML={{ __html: item.description }} />
<span className={`${lastItem ? styles.separator : ""}`}></span>
</div>
);
});
return (
<>
<div className={styles.sections}>{sectionList}</div>
</>
);
};
export default Section;
You enter an infinite loop because you call setlastItem in the map function, which in turn reruns on every render. Since setState triggers a new render, this causes the infinite loop.
What you want to is to put the generation of the sectionList in a useEffect, that reruns only every time the Data.sections changes.
Like this:
const Section = () => {
const [sectionList, setSectionList] = useState([]);
useEffect(() => {
if(!Data.sections|| Data.sections.length < 2){
setSectionList([]);
} else {
setSectionList(Data.sections.splice(-1, 1));
}
}, [Data.sections]);
return (
<div className={styles.sections}>
{sectionList.map(item => (
<div>
<h2>{item.title}</h2>
<img src={`/images/${item.image}`}></img>
<span style={{ backgroundImage: `url(/images/${item.image})`}
}></span>
<p dangerouslySetInnerHTML={{ __html: item.description }} />
<span className={`${lastItem ? styles.separator : ""}`}></span>
</div>
)}
</div>
);
};
As you see, I separated the generation of the data from the jsx, which makes the code more easy to understand and rebuild, I find.
const Section = () => {
return (
<>
<div className={styles.sections}>
{Data.sections.map((item, id) => (
<div key={id}>
<h2>{item.title}</h2>
<img src={`/images/${item.image}`}></img>
<span style={{ backgroundImage: `url(/images/${item.image})` }
}></span>
<p dangerouslySetInnerHTML={{ __html: item.description }} />
<span className={`${id < (Data.sections.length - 1) ? styles.separator : ""}`}></span>
</div>
))
}
</div>
</>
);
};
or
const Section = () => {
return (
<>
{Data.sections.map((item, id) => (
<div key={id}>
<div className={styles.sections} key={id}>
<h2>{item.title}</h2>
<img src={`/images/${item.image}`}></img>
<span style={{ backgroundImage: `url(/images/${item.image})` }
}></span>
<p dangerouslySetInnerHTML={{ __html: item.description }} />
</div>
{ id < (Data.sections.length - 1) &&
<span className={styles.separator}></span>
}
</div>
))
}
</>
);
};

Nuka Carousel not displaying in react slideHeight props 0

I am using nuka carousel in react TypeScript a saleor pwa react ts app
Nuka carousel not showing items cause nuka is passing slideHeight 0 to slider -frame
Code Example:
render() {
const { title } = this.props;
const { products } = this.state;
const productsList = products.map((product: any) => (
<Link to={'/product/' + product.id} key={product.id}>
<ProductListItem product={product} />
</Link>
))
return (
<div className="products">
<div className="container">
<h3>{title}</h3>
<Carousel>
{productsList}
</Carousel>
</div>
</div >
)
}
I solve it by just add if (products.length)
Solution:
render() {
const { title } = this.props;
const { products } = this.state;
if (products.length) {
const productsList = products.map((product: any) => (
<Link
to={'/product/' + product.id} key={product.id}
>
<ProductListItem product={product} />
</Link>
))
return (
<div className="products">
<div className="container">
<h3>{title}</h3>
<Carousel>
{productsList}
</Carousel>
</div>
</div >
)
}
return null;
}
There is no need to override css this is proper way
Here is solution Via Override css. this is for those who is interested in css override

DRY-er way of displaying items from an array with subcategories

I'm creating a menu page using React and Redux. I currently have the entire menu of over 100 items on an array of objects, which includes a 'category' property for the type of food (appetizers, burgers, desserts, etc.)
I originally just mapped through all of the items and rendered them all like this:
render(){
let foodList = this.props.foodMenu.map((food) => (
<Food className="food-menu" key={food.id} food={food} addToCart={this.addToCart}/>
))
return (
<div >
<h2>Food Menu</h2>
{foodList}
</div>
)
However, I want to be able to separate the menu by categories, which led me to this:
render(){
let appetizers = this.props.foodMenu.filter(food => food.category === 'appetizers').map((food) => (
<Food className="food-menu" key={food.id} food={food} addToCart={this.addToCart}/>
))
let soupsalad = this.props.foodMenu.filter(food => food.category === 'soupsalad').map((food) => (
<Food className="food-menu" key={food.id} food={food} addToCart={this.addToCart}/>
))
let steaks = this.props.foodMenu.filter(food => food.category === 'steaks').map((food) => (
<Food className="food-menu" key={food.id} food={food} addToCart={this.addToCart}/>
))
return (
<div>
<h2>Food Menu</h2>
<h3>Appetizers</h3>
<div className='container'>
{appetizers}
</div>
<h3>Soup or Salad</h3>
<div className='container'>
{soupsalad}
</div>
<h3>Steak</h3>
<div className='container'>
{steaks}
</div>
</div>
Except instead of 3 categories, I have 12. As you can see, this starts to become extremely repetitive and not "DRY". I was wondering if there was a cleaner way to go about doing this?
Based on previous answer I decided to make a real working solution of that. I also added another way of doing this with the forEach method. Please do run the snippet to see the results.
Cheers!
const Food = props => {
//Do something that make sense with this :)
const { food, addToCart, className } = props;
return (
<React.Fragment>
Food: {food.id} <br />
</React.Fragment>
);
};
const App = props => {
const categories = {
Appetizers: "appetizers",
Soupsalad: "soupsalad",
Steaks: "steaks"
};
var menus1 = [];
Object.keys(categories).forEach(categorie => {
var subMenus = props.foodMenu
.filter(food => food.category === categories[categorie])
.map((food,i) => (
<div key={i}>
<h3>{categorie}</h3>
<div className="container">
<Food
className="food-menu"
key={food.id}
food={food}
addToCart={""}
/>
</div>
</div>
));
menus1 = [...menus1, subMenus];
});
const menus2 = Object.entries(categories).map(e => {
return props.foodMenu
.filter(food => food.category === e[1])
.map((food,i) => (
<div key={i}>
<h3>{e[0]}</h3>
<div className="container">
<Food
className="food-menu"
key={food.id}
food={food}
addToCart={""}
/>
</div>
</div>
));
});
return (
<React.Fragment>
<h2>Food Menu1 with foreach</h2>
{menus1}
<h2>Food Menu2 with map of map</h2>
{menus2}
</React.Fragment>
);
};
var foodMenu = [
{ id: "food1", category: "appetizers" },
{ id: "food2", category: "soupsalad" },
{ id: "food3", category: "steaks" }
];
const rootElement = document.getElementById("root");
ReactDOM.render(<App foodMenu={foodMenu} />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"/>
#geostack posted a great answer. Here is also something I found worked for my application.
const sortByCategory = (dataArray) => {
let sortedByCategory = {}
dataArray.forEach(item => {
if (sortedByCategory[item.category]) {
sortedByCategory[item.category].push(item)
} else {
sortedByCategory[item.category] = []
sortedByCategory[item.category].push(item)
}
})
return sortedByCategory
}
render(){
let sortedData = sortByCategory(this.props.foodMenu)
let foodMenu = []
for(let key in sortedData) {
foodMenu.push(
<div >
<h3>{key.toUpperCase()}</h3>
<br />
<div>
{
sortedData[key].map(food => {
return (
<div key={food.id}>
<Food key={food.id} food={food} addToCart={this.addToCart}/>
<hr />
</div>
)
})
}
</div>
</div>
)
}
return (
<div>
{foodMenu}
</div>
)
}

Resources