Mapping array causing performance issues React - reactjs

I have a mySQL database with about 100 rows, containing the data of products I'm listing in an eCommerce app.
I have them displayed on the front-end by using Axios to get the rows from my back-end SELECT * statement. They are then held as an array state const [rows, setRows] = useState([]);.
These rows are then mapped for each row found in the table, so around 100 maps.
I have noticed with this many results performance has started to become an issue and I've heard similar issues from mapping arrays.
I've benchmarked and tinkered to find that this component is the one causing the performance issues.
I would like any solutions to improve my component's performance, without altering the structure too much.
Heres the component
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import Button from '#mui/material/Button';
export default function ProductCard(props) {
const [rows, setRows] = useState([]);
const isntDesktop = props.isntDesktop; //Constant from parent
const isMobile = window.innerWidth < 768; //Checks if screen is smaller than mobile
const sidebar = props.sidebar; //Sidebar state from parent component
useEffect(() => {
axios.get('http://localhost:8080/products/get/')
.then(res => {
setRows(res.data);
}).catch(err => {
console.log(err);
});
});
return (
rows
.sort((a, b) => a.name.localeCompare(b.name))
//Map products
.map((row, index) => {
return (
/* Adjust structure for mobile */
isMobile ? (
/* Product card */
<div
key={index}
class={`${
sidebar ? 'border-0' : 'border'
} relative mt-6 py-8 px-5 rounded`}
>
{/* Heading */}
<h2 class="text-2xl font-semibold tracking-wide text-gray-700">{row.name}</h2>
<div class="flex items-center sm:items-start space-x-5">
{/* Image */}
<img
src={row.image}
alt="product"
class={`${
sidebar ? 'opacity-50' : 'opacity-100'
} mt-4 min-w-[125px] sm:w-[300px] h-auto`}
/>
<div class="mt-4">
{/* Price */}
<p class="text-2xl sm:text-xl font-semibold sm:font-medium text-gray-600">£ {row.price}</p>
{/* Keywords */}
<ul class="ml-5 mt-2 space-y-1">
<li class="list-disc text-gray-600">{row.keyword_one}</li>
<li class="list-disc text-gray-600" >{row.keyword_two}</li>
<li class="list-disc text-gray-600">{row.keyword_three}</li>
</ul>
</div>
</div>
<div
class={`${
sidebar ? 'bg-none' : 'bg-gray-100'
} mt-4 py-2 px-4 flex justify-center space-x-2.5`}>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 text-sky-500">
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 18.75a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m3 0h6m-9 0H3.375a1.125 1.125 0 01-1.125-1.125V14.25m17.25 4.5a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m3 0h1.125c.621 0 1.129-.504 1.09-1.124a17.902 17.902 0 00-3.213-9.193 2.056 2.056 0 00-1.58-.86H14.25M16.5 18.75h-2.25m0-11.177v-.958c0-.568-.422-1.048-.987-1.106a48.554 48.554 0 00-10.026 0 1.106 1.106 0 00-.987 1.106v7.635m12-6.677v6.677m0 4.5v-4.5m0 0h-12" />
</svg>
<label class="text-gray-600 text-sm">Free delivery on orders over £50</label>
</div>
<div class="mt-4 w-full mx-auto flex flex-col sm:flex-row space-y-2.5 sm:space-y-0 sm:space-x-2.5">
{sidebar && isntDesktop ? (
<Button
type="submit"
variant="contained"
sx={{
opacity: 0.5,
bgcolor: '#6366f1',
textTransform: 'none',
width: '100%',
':hover': {
backgroundColor: '#4f46e5',
},
}}
>
View product
</Button>
) : (
<Button
type="submit"
variant="contained"
sx={{
opacity: 1,
bgcolor: '#6366f1',
textTransform: 'none',
width: '100%',
':hover': {
backgroundColor: '#4f46e5',
},
}}
>
View product
</Button>
)}
<Button
type="submit"
sx={{
border: ' 1px solid #38bdf8',
color: '#38bdf8',
textTransform: 'none',
width: '100%',
':hover': {
color: '#ffffff',
backgroundColor: '#38bdf8',
},
}}
>
Add to cart
</Button>
</div>
</div>
) : (
<div key={index} class="flex mt-6 py-8 px-5 border rounded">
{/* Image */}
<img src={row.image} alt="product" class="mr-4 w-[300px] h-auto" />
<div class="relative w-full px-5 items-center">
{/* Heading */}
<h2 class="text-2xl font-semibold tracking-wide text-gray-700">{row.name}</h2>
<div class="mt-8 flex justify-between">
{/* Keywords */}
<ul class="ml-5 space-y-1">
<li class="list-disc text-gray-600">{row.keyword_one}</li>
<li class="list-disc text-gray-600" >{row.keyword_two}</li>
<li class="list-disc text-gray-600">{row.keyword_three}</li>
</ul>
{/* Promo */}
<div class="flex flex-col ml-5">
{/* Price */}
<p class="text-xl font-medium text-gray-600">£ {row.price}</p>
<div
class={`${
sidebar ? 'bg-none' : 'bg-gray-100'
} mt-4 py-2 px-4 flex space-x-2.5`}>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 text-sky-500">
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 18.75a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m3 0h6m-9 0H3.375a1.125 1.125 0 01-1.125-1.125V14.25m17.25 4.5a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m3 0h1.125c.621 0 1.129-.504 1.09-1.124a17.902 17.902 0 00-3.213-9.193 2.056 2.056 0 00-1.58-.86H14.25M16.5 18.75h-2.25m0-11.177v-.958c0-.568-.422-1.048-.987-1.106a48.554 48.554 0 00-10.026 0 1.106 1.106 0 00-.987 1.106v7.635m12-6.677v6.677m0 4.5v-4.5m0 0h-12" />
</svg>
<label class="text-gray-600 text-sm">Free delivery on orders over £50</label>
</div>
</div>
</div>
<div class="absolute bottom-0 w-full pr-5 flex space-x-2.5">
{sidebar && isntDesktop ? (
<Button
type="submit"
variant="contained"
sx={{
opacity: 0.5,
bgcolor: '#6366f1',
textTransform: 'none',
width: '100%',
':hover': {
backgroundColor: '#4f46e5',
},
}}
>
View product
</Button>
) : (
<Button
type="submit"
variant="contained"
sx={{
opacity: 1,
bgcolor: '#6366f1',
textTransform: 'none',
width: '100%',
':hover': {
backgroundColor: '#4f46e5',
},
}}
>
View product
</Button>
)}
<Button
type="submit"
sx={{
border: ' 1px solid #38bdf8',
color: '#38bdf8',
textTransform: 'none',
width: '100%',
':hover': {
color: '#ffffff',
backgroundColor: '#38bdf8',
},
}}
>
Add to cart
</Button>
</div>
</div>
</div>
)
)
})
)
}

Since you (probably) want to fetch your data once (on mount), you will have to add an empty dependency array to your useEffect to avoid infinitely running a query . You can read more about it here: https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects (In the Note section).
useEffect(() => {
axios.get('http://localhost:8080/products/get/').then(res => {
setRows(res.data);
}).catch(err => {
console.log(err);
});
}, [])
I saw that you render all your data on render and unfortunately, React is notoriously slow when it comes to rendering large lists. You might need to think about windowing your data, aka rendering a chunk of your data when your page first load and render more data as the user scroll further down. There's a great library that handles this for you: https://tanstack.com/virtual/v3/docs/guide/introduction.
I hope this helps!

Related

ReactJS Giving An Unexpected Warning: Each child in a list should have a unique "key" prop

It's a beautiful day but this thing is blocking my view:
I've tried everything (except asking a question ._.) and searched everywhere. But the expected result could not be obtained :'(
So, this is my code where I think the error is happening:
<div className="mt-3">
{data.activities?.map((act) => {
return <Chip className="mr-2 px-3" label={act} key={act.id} />;
})}
</div>{" "}
Well it's a part of my file packageCard.js but if you want the whole file here it is:
import Chip from "#material-ui/core/Chip";
import Clock from "./img/main_page_icons/clock.svg";
import Person from "./img/main_page_icons/person.svg";
import MapPin from "./img/main_page_icons/map-pin.svg";
import Package1 from "./img/main_page/package 1.png";
function PackageCard({ data }) {
console.log(data);
return (
<>
<div className="col-lg-4 col-md-6 col-sm-12 card-super-container">
<div className="card-container">
<div className="card-image-wrapper">
<div>
<div className="card-image-background">
<img
alt="package 1"
src={data.featuredImg}
style={{ borderRadius: "6px" }}
className="w-100 "
/>
</div>
</div>
</div>
<div className="card-details-container">
<span className="text-bold details-container-header">
{data.name}
</span>
<div>
<div className="card-details-text">
<div>{data.description}</div>
<div>
<h6 className="card-price">{`Price: ${data.price} USD`}</h6>
</div>
</div>
</div>
</div>
<div>
<div className="card-final-desc">
<div className="card-final-desc-item">
<span className="card-dot"></span>
<img alt="clock" src={Clock} className="mr-1" />
{` ${data.days}D / ${data.nights}N `}
</div>
<div className="card-final-desc-item">
<span className="card-dot"></span>
<img alt="clock" src={Person} className="mr-1" />
{` ${data.noOfPersons} Person `}
</div>
<div className="card-final-desc-item">
<span className="card-dot"></span>
<img alt="clock" src={MapPin} className="mr-1" />
{` ${data.destination} `}
</div>
</div>
</div>
<div className="card-btn-container">
<button className="btn zoki-btn">Book Now </button>
</div>
</div>
</div>
<div className="col-lg-4 mb-4">
<img
alt="package 1"
src={data.featuredImg}
style={{ height: 250 }}
className="w-100"
/>
<div
className="bg-white py-4 px-4 shadow-lg"
style={{ fontSize: 14, textAlign: "start" }}
>
<h6>{data.name}</h6>
<p>{data.description}</p>
<div className="d-flex justify-content-between w-100">
<h6 className="m-0">{`Price: ${data.price} USD`}</h6>
<h6 className="m-0" style={{ color: "#9C8E35", cursor: "pointer" }}>
Book Now
</h6>
</div>
/!*{" "}
<div className="mt-3">
{data.activities?.map((act) => {
return <Chip className="mr-2 px-3" label={act} key={act.id} />;
})}
</div>{" "}
*!/
</div>
<div
className="row shadow mx-3"
style={{ backgroundColor: "#9C8E35", fontSize: 12 }}
>
<div className="col-4 p-0 text-center">
<div
className="py-2 text-white d-flex w-100 justify-content-center"
style={{ borderRight: "1px solid white" }}
>
<img alt="clock" src={Clock} className="mr-1" />
<p className="mb-0">{` ${data.days} D / ${data.nights} N `}</p>
</div>
</div>
<div className="col-4 p-0">
<div
className="py-2 text-white d-flex w-100 justify-content-center"
style={{ borderRight: "1px solid white" }}
>
<img alt="clock" src={Person} className="mr-1" />
<p className="mb-0">{` ${data.noOfPersons} Person `}</p>
</div>
</div>
<div className="col-4 p-0">
<div className="py-2 text-white d-flex w-100 justify-content-center">
<img alt="clock" src={MapPin} className="mr-1" />
<p className="mb-0">{` ${data.destination} `}</p>
</div>
</div>
</div>
</div>
</>
);
}
export default PackageCard;
Packages.js Component
import { useState, useEffect } from "react";
import { getAvailablePackages } from "crud";
import PackageBg from "../../../assets/img/offers.jpg";
import Card from "./packageCard";
import CircularProgress from "#material-ui/core/CircularProgress";
import { useHistory } from "react-router-dom";
function Packages() {
const history = useHistory();
const [listPackages, setListPackages] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
const params = {
search: { query: "" },
sort: "name",
page: 1,
pageSize: 3,
};
setLoading(true);
getAvailablePackages(params)
.then((res) => {
// console.log(res.data.data)
setListPackages(res.data.data.travelPackages);
setLoading(false);
})
.catch((error) => {
console.log(error.response.data);
console.log(error.response.status);
setLoading(false);
});
}, []);
let checkData = [
{
name: "ahmad",
description: "this is description",
price: 5,
days: 5,
nights: 9,
noOfPersons: 9,
destination: "England",
featuredImg:
"https://media.gettyimages.com/photos/castle-combe-in-the-fall-wiltshire-england-picture-id157006201?s=612x612",
},
{
name: "ali",
description: "this is description",
price: 5,
days: 5,
nights: 9,
noOfPersons: 9,
destination: "homeland",
featuredImg:
"https://images.unsplash.com/photo-1538332576228-eb5b4c4de6f5?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8ZmlubGFuZHxlbnwwfHwwfHw%3D&w=1000&q=80",
},
{
name: "ali",
description: "this is description",
price: 5,
days: 5,
nights: 9,
noOfPersons: 9,
destination: "finland",
featuredImg:
"https://images.unsplash.com/photo-1538332576228-eb5b4c4de6f5?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8ZmlubGFuZHxlbnwwfHwwfHw%3D&w=1000&q=80",
},
];
return (
<div
id="zoki-packages"
className="text-center py-5 my-5"
style={{
backgroundImage: `url(${PackageBg})`,
backgroundPosition: "center",
backgroundSize: "cover",
}}
>
<div className="container" style={{ color: "#344767" }}>
<h6 className="text-bold font-italic"> EXPLORE GREAT PLACES </h6>
<h1 className=""> Popular Packages </h1>
<div className="row justify-content-center align-content-center">
{checkData?.map((pkg) => {
return <Card data={pkg} key={pkg.id} />;
})}
{loading ? (
<CircularProgress className="my-4 ml-auto mr-auto" />
) : listPackages.length ? (
listPackages?.map((pkg) => {
return <Card data={pkg} key={pkg.id} />;
})
) : (
<h4 className="my-4 ml-auto mr-auto">
No Package Available at that time.
</h4>
)}
</div>
{listPackages.length ? (
<button
onClick={() => history.push("/search-packages")}
className="btn btn-dark px-4 p-2 rounded-pill my-4"
style={{ color: "#CFBD45", backgroundColor: "black", fontSize: 14 }}
>
<p className="mb-0"> VEIW ALL PACKAGES </p>
</button>
) : null}
</div>
</div>
);
}
export default Packages;
I will be very grateful if you find a solution. I appreciate any help you can provide <33
It seems that the act does not have a unique id, try using the index of the map.
<div className="mt-3">
{data.activities?.map((act, i) => (
<Chip className="mr-2 px-3" label={act} key={i} />
))}
</div>
if you use the act.id as the key,should make sure every id is unique cause
Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity
here is the official docs

NextJS Image component is overlapping other components regardless of z-index

Stack: NextJS, JavaScript, React, Tailwind, Framer Motion
I've scoured the web for answers to this but mostly the answers were saying that the <div> that wraps the image must have a position of relative. I tried this and it didn't change anything.
Code for the text and images:
import React from "react";
import { motion } from "framer-motion";
import Image from "next/image";
export default function Card({ image, name, description }) {
return (
<motion.div
className="flex justify-center mt-5"
initial="hidden"
animate="visible"
variants={{
hidden: { scale: 0.9, opacity: 0 },
visible: {
scale: 1,
opacity: 1,
transition: { delay: 0.3, ease: [0.17, 0.67, 0.83, 0.67] },
},
}}
>
<div className="relative">
<Image
className="rounded-full absolute"
src={image}
width={100}
height={100}
alt="Leader profile picture"
/>
</div>
<div>
<motion.h1
className="text-xl pl-1 font-bourton"
initial="hidden"
animate="visible"
variants={{
hidden: { scale: 0.9, opacity: 0 },
visible: {
scale: 1,
opacity: 1,
transition: { delay: 0.35, ease: [0.17, 0.67, 0.83, 0.67] },
},
}}
>
{name}
</motion.h1>
<motion.p className="text-sm p-3 text-wrap w-48 text-slate-600 font-bourton">
{description}
</motion.p>
</div>
</motion.div>
);
}
The code for the sidebar:
import { useState } from "react";
import { motion } from "framer-motion";
import classnames from "classnames";
import { useRouter } from "next/router";
const path01Variants = {
open: { d: "M3.06061 2.99999L21.0606 21" },
closed: { d: "M0 9.5L24 9.5" },
};
const path02Variants = {
open: { d: "M3.00006 21.0607L21 3.06064" },
moving: { d: "M0 14.5L24 14.5" },
closed: { d: "M0 14.5L15 14.5" },
};
const Sidebar = () => {
const [showSidebar, setShowSidebar] = useState(false);
const [animation, setAnimation] = useState("closed");
const router = useRouter();
const onClick = () => {
setAnimation("moving");
setTimeout(() => {
setAnimation(animation === "closed" ? "open" : "closed");
}, 200);
setShowSidebar(!showSidebar);
};
return (
<>
{showSidebar ? (
<button
className="flex justify-center relative z-100 items-center w-12 h-12 border border-black rounded-full"
onClick={onClick}
>
<svg width="24" height="24" viewBox="0 0 24 24">
<motion.path
stroke="#fff"
animate={animation}
variants={path01Variants}
/>
<motion.path
stroke="#fff"
animate={animation}
variants={path02Variants}
/>
</svg>
</button>
) : (
<button
className="flex justify-center top-12 right-12 items-center w-12 h-12 border border-black rounded-full"
onClick={onClick}
>
<svg width="24" height="24" viewBox="0 0 24 24">
<motion.path
stroke="#000"
animate={animation}
variants={path01Variants}
/>
<motion.path
stroke="#000"
animate={animation}
variants={path02Variants}
/>
</svg>
</button>
)}
<div
className={` bg-unionred border-black opacity-90 top-0 right-0 lg:w-[35vw] sm:w-[50vw] p-10 pl-20 fixed text-white h-full ease-in-out duration-300 ${
showSidebar ? "translate-x-0" : "translate-x-full"
}`}
>
<button
className="flex justify-center absolute top-8 right-8 items-center w-12 h-12 border border-white rounded-full"
onClick={onClick}
>
<svg width="24" height="24" viewBox="0 0 24 24">
<motion.path
stroke="#fff"
animate={animation}
variants={path01Variants}
/>
<motion.path
stroke="#fff"
animate={animation}
variants={path02Variants}
/>
</svg>
</button>
<h3 className="mt-20 text-4xl underline font-bourton text-white">
<button className="underline" onClick={() => router.push("whoweare")}>
Who We Are
</button>
</h3>
<h3 className="mt-20 text-4xl font-bourton text-white">
<button
className="underline"
onClick={() => router.push("ourleaders")}
>
Our Leaders
</button>
</h3>
<h3 className="mt-20 text-4xl font-bourton text-white">
<button className="underline">News</button>
</h3>
</div>
</>
);
};
export default Sidebar;
Before opening sidebar
After opening sidebar
Any help would be appreciated! Thank you.
It looks like you're not applying a z-index to the sidebar itself. That <div> is the container that needs to overlap the main content, so that is where you need to add the utility class.
Additionally, it looks like you're trying to use z-100 which is not one of the Tailwind defaults (see: https://tailwindcss.com/docs/z-index). You can either add that new value to your tailwind.config.js or try z-50, which is the highest default.
For example:
<div className={`z-50 bg-unionred border-black opacity-90 top-0 right-0 lg:w-[35vw] sm:w-[50vw] p-10 pl-20 fixed text-white h-full ease-in-out duration-300 ${showSidebar ? "translate-x-0" : "translate-x-full"}`}>

exit animation for framer motion not working

below is a code for a stepper that displays certain text in each step , the number of the steps is highlighted (by changing the background color of the step number) , and it will display a text beneath it , my issue is i wanted to use framer-motion with AnimatePresense and exit prop for the transitioning from one text to another but its not wokring , i would appreciate the feed back
below is the animation variants
export const instructionVariant = {
hidden: {
opacity: 0,
x: "100vw",
},
visible: {
opacity: 1,
x: 0,
transition: {
type: "spring",
mass: 0.7,
damping: 7,
ease: "easeIn",
},
},
exit: {
x: "-100vw",
opacity: 0,
transition: { duration: 0.5, ease: "easeInOut" },
},
};
export const paragraphVariant = {
hidden: {
opacity: 0,
x: "10hw",
},
visible: {
opacity: 1,
x: 0,
transition: {
type: "spring",
mass: 0.7,
damping: 7,
ease: "easeIn",
},
},
exit: {
x: "-5hw",
opacity: 0,
transition: { duration: 0.5, ease: "easeInOut" },
},
};
below is the code , i used redux-toolkit for this , if i need to ppost the code for it if needed
const Instructions: FC = () => {
const step = useAppSelector((state) => state.instruction.step);
const dispatch = useAppDispatch();
return (
<motion.div
variants={instructionVariant}
initial="hidden"
animate="visible"
exit="exit"
className="h-2/4 w-3/5 bg-primary-200 border-2 border-solid border-black rounded-lg shadow-lg shadow-black relative"
>
<div className="grid grid-rows-[1fr_4fr]">
<div className="flex justify-around items-center m-10">
{data.map((item: InstructionsProps) => {
return (
<>
<div>
<h1
className={
step >= item.id
? "flex justify-center items-center text-4xl rounded-full bg-secondary-100 text-white w-16 h-16 transition-all duration-300 ease-in"
: "flex justify-center items-center text-4xl rounded-full bg-primary-100 text-white w-16 h-16 transition-all duration-300 ease-in"
}
>
{item.id - 1}
</h1>
</div>
<div
className={
step > item.id
? "last:hidden text-secondary-100 transition-all duration-300 ease-in"
: "last:hidden transition-all duration-300 ease-in"
}
>
<FaArrowRight size={20} />
</div>
</>
);
})}
</div>
<AnimatePresence exitBeforeEnter>
<motion.h1
variants={paragraphVariant}
initial="hidden"
animate="visible"
exit="exit"
className="m-10 text-xl font-bold font-rubik"
>
{data[step - 1].description}
</motion.h1>
</AnimatePresence>
</div>
<div className="absolute right-6 bottom-6 flex justify-between items-center w-32">
<button
onClick={() => dispatch(instructionsAction.instructionreverse())}
className="hover:scale-125 active:scale-110 active:text-secondary-200 hover:text-secondary-200 transition-all duration-300 ease-in"
>
<HiOutlineChevronDoubleLeft size={35} />
</button>
<button
onClick={() => dispatch(instructionsAction.instructioncomplt())}
className="hover:scale-125 active:scale-110 active:text-secondary-200 hover:text-secondary-200 transition-all duration-300 ease-in"
>
<HiOutlineChevronDoubleRight size={35} />
</button>
</div>
</motion.div>
);
};
From the AnimatePresence docs:
Direct children must each have a unique key prop so AnimatePresence
can track their presence in the tree.
const MyComponent = ({ isVisible }) => (
<AnimatePresence>
{isVisible && (
<motion.div
key="modal"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
/>
)}
</AnimatePresence>
)

run reduce function before map

In our scenario , I map the array and produce the result using jsx , but I want the ignore duplicate data before the map operation. so how can I do that? here snipped code may help you :
import React, { useContext } from "react";
import { Context } from "../Context/Context";
import "./Navbar.css"
import cartImg from "../../cart.png"
import close from "../../close.svg"
export default function Navbar() {
const { cart, cartReducer } = useContext(Context)
function getCart(products) {
return products.map((product, index) =>
<div key={index} style={{ borderBottom: "1px solid #d5cfcf" }} >
<img src={product.image} style={{ width: '100%' }} alt="" />
<div className="cart-info">
<div>
<img src={close} onClick={() =>
cartReducer({ product, type: 'remove' })} style={{ width: 30, paddingLeft: 10 }} alt="" />
</div>
<div>
{product.name}
</div>
</div>
</div>
)
}
return (
<nav className="navbar navbar-light bg-light">
<a className="navbar-brand" href="#">
<img src={cartImg} style={{ maxWidth: 30 }} alt="" />
{cart.length > 0 && <span className="badge badge-warning badge-header">{cart.length}</span>}
<div className="navbar-cart" style={cart.length > 0 ? { width: 150 } : { width: 'fit-content' }}>
<h6> {cart.length == 0 ? 'Cart is Empty' : getCart(cart)}</h6>
</div>
</a>
</nav>
)
}
You can remove duplicates by:
const duplicatedData = [1,2,3,4,5,1,2,1,1,1,1,3,4,5,6,7,8,2,1,3,4]
const notDuplicatedData = [...new Set(duplicatedData)]
console.log(notDuplicatedData)
So in your case:
[...new Set(products)].map((product, index)

Button/dropdown button disabled by default without specifying disabled

I am trying to add a dropdown button in my react project and its rendering as disabled by default. why is this happening ?
The first dropdown is working fine, i called the same dropdown in the navbar after this one and it renders as disabled. I tried other things as well, like adding a button also would not work for me.
The navbar is diplayed when i get a response from the backend and a different component is rendered (ResultTable)
import React from "react";
import ResultTable from "./ResultTable";
...
import DropdownButton from "react-bootstrap/DropdownButton";
import Dropdown from "react-bootstrap/Dropdown";
class MainContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
//more state values
threshold: 0.75
};
this.thresholdChange = this.thresholdChange.bind(this);
}
thresholdChange(input) {
this.setState({
threshold: input
});
}
toProperCase = function (txt) {
return txt.replace(/\w\S*/g, function (txt) { return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); });
};
render() {
const { responseData } = this.state;
return (
<div className="container-flex container-without-scroll wrapper"
style={{
backgroundImage: `url(${bg})`,
width: "100%",
height: "100vh",
backgroundSize: "cover",
backgroundRepeat: "no-repeat",
overFlow:'hidden'
}}
>
<div
className={`container-fluid `}>
{this.state.displayTable ? (
<Navbar
style={{
position: "fixed",
left: "50%",
top: "95%",
transform: "translate(-50%, -90%)",
backgroundColor: 'black'
}}
sticky="bottom"
>
<br />
<Navbar.Collapse className="justify-content-end">
<Button
variant="primary"
disabled={
this.props.initialTransaction &&
this.props.initialTransaction.version == 0 &&
this.props.initialTransaction
? true
: false
}
size="sm"
style={{ color: "#FFF" }}
onClick={this.undoAction}
>
<span className=" fa fa-undo "></span>
Undo
</Button>
<Button
variant="primary"
size="sm"
style={{ color: "#FFF" }}
disabled={
(this.props.initialTransaction &&
this.props.initialTransaction.version) <
(this.props.currentVersion &&
this.props.currentVersion.version)
? false
: true
}
onClick={this.redoAction}
>
<span className=" fa fa-repeat "></span>
Redo
</Button>
<Button
variant="success"
size="sm"
style={{ color: "#FFF" }}
disabled={
this.props.initialTransaction &&
this.props.initialTransaction.version == 0
? true
: false
}
onClick={() =>
this.exportExcel(this.props.initialTransaction)
}
>
<span className=" fa fa-download "></span>
Export
</Button>
<DropdownButton
size="md"
title={this.state.threshold}
>
{this.state.thresholdValues.map(eachValue => {
return (
<Dropdown.Item
key = {Math.random()}
onClick={() => this.thresholdChange(eachValue)}
as="button"
>
{eachValue}
</Dropdown.Item>
);
})}
</DropdownButton>
</Navbar.Collapse>
<br/>
</Navbar>
) : null}
{this.state.displayTable ? null : (
<div
className="col-md-4 col-md-offset-4"
style={{
position: "absolute",
left: "50%",
top: "50%",
transform: "translate(-50%, -50%)",
backgroundColor: 'rgba(14, 13, 13, 0.74)'
}}
>
<br />
<div className="row">
<div className="input-group col-md-9">
<div className="input-group-prepend">
<span
style={{ cursor: 'pointer' }}
onClick={this.onFormSubmit}
className="input-group-text"
id="inputGroupFileAddon01"
>
{" "}
Upload{" "}
</span>
</div>
<div className="custom-file">
<input
type="file"
className="custom-file-input"
id="inputGroupFile01"
onChange={this.onChange}
aria-describedby="inputGroupFileAddon01"
/>
<label
className="custom-file-label"
htmlFor="inputGroupFile01"
>
{this.props.active && this.props.active.filename}
</label>
</div>
</div>
<div className="col-md-3">
<DropdownButton
size="md"
id="dropdown-item-button"
title={this.state.threshold}
>
{this.state.thresholdValues.map(eachValue => {
return (
<Dropdown.Item
onClick={() => this.thresholdChange(eachValue)}
as="button"
>
{eachValue}
</Dropdown.Item>
);
})}
</DropdownButton>
</div>
</div>
<br />
</div>
)}
<div >
{this.state.displayTable ? (
<div className = "container-flex" style =
{{overflowY:'scroll', maxHeight:'80vh'}}>
<ResultTable
data={responseData}
afterMerge={params => {
this.afterMerge(params);
}}
/>
</div>
) : null}
</div>
</div>
</div>
);
}
}
// Maps state from store to props
const mapStateToProps = (state, ownProps) => {
return {
};
};
// Maps actions to props
const mapDispatchToProps = dispatch => {
return {
};
};
// Use connect to put them together
export default connect(
mapStateToProps,
mapDispatchToProps
)(MainContainer);

Resources