React : press X button to delete image from screen - reactjs

I'm a complete beginner in React and I have an app that returns a few pics from an API . I have inserted an 'X' on each pic to delete a pic if user clicks on 'X' . However I have no idea how to use React Hooks to delete a specific image.
My code :
function City(){
// custom func that returns the pics from the api and the loading state of my app
const {loading , pics ,query , setQuery} = useFetch('nature');
//I believe I need something like this but have no idea how to use it (initial state is all
my pics and deleteImage must delete a single pic)
const [images,deleteImage] = useState(pics);
return (<div className="city-info">
{
//if app is loading display loading else return all pics
!loading ?
(pics.length>0 && pics.map((pic) =>{
return <div className="info" key = {pic.id}>
<span className="close">
//I want to use onClick on this button to delete the specific image
<span className="inner-x">
×
</span>
</span>
<img src = {pic.src.original} alt ="img"/>
</div>
})
):<div> Loading </div>
}
</div>);
}
UPDATE : With the code below I remove the images from my list using hooks but they are not deleted from my picture :
function City(){
const {loading , pics ,query , setQuery} = useFetch('athens');
const [images , setImages] = useState([]);
const removeImage = (id) =>{
//images are inserted correctly cannot delete
setImages((oldState)=>oldState.filter((item)=> item.id !== id))
images.map((im)=>console.log(im.id)) //images are deleted from list not from screen
}
useEffect(()=>{
setImages(pics);
} , [pics])
return (<div className="city-info">
{
!loading ?
(pics.length>0 && pics.map((pic) =>{
return <div className="info" key = {pic.id}>
<span className="close" onClick= {()=>removeImage(pic.id)} >
<span
className="inner-x">
×
</span>
</span>
<img src = {pic.src.original} alt ="img"/>
</div>
})
):<div> Loading ... </div>
}
</div>);
}
export default City;

So this is a simple local state example on how to remove items inside of an array
import React, { useState, useEffect } from "react";
import "./styles.css";
const allImages = [
{
id: 1,
imgUrl: "https://via.placeholder.com/100"
},
{
id: 2,
imgUrl: "https://via.placeholder.com/100"
},
{
id: 3,
imgUrl: "https://via.placeholder.com/100"
}
];
const App = () => {
const [pics, setPics] = useState([]);
const removeImage = (id) => {
// this is the line that you are looking for
setPics((oldState) => oldState.filter((item) => item.id !== id));
};
useEffect(() => {
//fake fetch data
setPics(allImages);
}, []);
return (
<div className="App">
{pics.map((pic) => {
return (
<div style={{ marginBottom: "100px" }}>
{pic.id}
<img
src={pic.imgUrl}
width="100px"
height="100px"
alt="placeholder grey 100px"
/>
<button onClick={() => removeImage(pic.id)}>X</button>
</div>
);
})}
</div>
);
};
export default App;
sandbox link

Related

REACT.JS how to detect only one of the cards is clicked from a component

I tried to add click handler on my code, the idea is when i click on my first card, the first card add new class "selected" nad when i click on my third card or else the third card or else will add new class "selected". There is a problem when i was clicking on any card it was always first card was selected. Please help me. Thank you.
Parent code
import React, { useState } from 'react';
import CardBus from '../CardBus/CardBus.component';
import './BusSelector.style.css'
function BusSelector() {
const [buses, setBuses] = useState([
{
busNumber: 1,
destination: 'Cibiru - Cicaheum',
stopTime: '09:20 - 09.45',
stopLocation: 'Jl.Jendral Sudirman',
isSelected: false
},
{
busNumber: 2,
destination: 'Cicaheum - Cibereum',
stopTime: '09:10 - 09.20',
stopLocation: 'Jl.Soekarno Hatta',
isSelected: false
},
]);
return (
<div className="bus-selector--container">
{buses.map((bus) => {
return <CardBus key={bus.busNumber} eachBus={bus} buses={buses} setBuses={setBuses} />
})}
</div>
);
}
export default BusSelector;
Child code:
import React from 'react';
import './CardBus.style.css';
import TimeProgressThin from '../../icon/Time_progress_thin.svg';
import PinLight from '../../icon/Pin_light_thin.svg';
function CardBus(props) {
const [isSelected, setIsSelected] = useState(false)
let { eachBus, buses, setBuses} = props;
const selectedHandler = () => {
if (isSelected) {
const card = document.querySelector('.card');
card.classList.add('selected');
return setIsSelected(!isSelected);
}
else {
const card = document.querySelector('.card');
card.classList.remove('selected');
return setIsSelected(!isSelected);
}
}
return (
<div key={eachBus.key} className="card" onClick={selectedHandler}>
<div className="bus--left">
<h1>{eachBus.busNumber}</h1>
</div>
<div className="bus--right">
<div className="title">
<h1>{`Armada ${eachBus.busNumber}`}</h1>
<h2>{eachBus.destination}</h2>
</div>
<div className="detail">
<div className="detail--item">
<div>
<img src={TimeProgressThin} alt="Time Progress Logo" />
</div>
<div className="detail_content">
<h3>Last stopped</h3>
<h3>{eachBus.stopTime}</h3>
</div>
</div>
<div className="detail--item">
<div>
<img src={PinLight} alt="Pin Light Logo" />
</div>
<div className="detail_content">
<h3>Location Stopped</h3>
<h3>{eachBus.stopLocation}</h3>
</div>
</div>
</div>
</div>
</div>
);
}
export default CardBus;
Allow multiple selections
function CardBus(props) {
const [isSelected, setIsSelected] = useState(false);
let { eachBus, buses, setBuses } = props;
return (
<div key={eachBus.key} className={`card ${isSelected ? 'selected' : ''}`} onClick={() => setIsSelected(!isSelected)}>
...
</div>
);
}
export default CardBus;
Allow single select
You can simplify the code a lot if you move the selected child logic to the parent.
Parent code:
function BusSelector() {
const [buses, setBuses] = useState([
{
busNumber: 1,
destination: 'Cibiru - Cicaheum',
stopTime: '09:20 - 09.45',
stopLocation: 'Jl.Jendral Sudirman',
isSelected: false
},
{
busNumber: 2,
destination: 'Cicaheum - Cibereum',
stopTime: '09:10 - 09.20',
stopLocation: 'Jl.Soekarno Hatta',
isSelected: false
},
]);
const [selectedBus, setSelectedBus] = useState(-1);
return (
<div className="bus-selector--container">
{buses.map((bus) => {
return <CardBus
key={bus.busNumber}
eachBus={bus}
buses={buses}
setBuses={setBuses}
onClick={() => setSelectedBus(bus.busNumber)}
isSelected={bus.busNumber === selectedBus} />;
})}
</div>
);
}
export default BusSelector;
Child code:
function CardBus(props) {
let { eachBus, isSelected, buses, setBuses, onClick } = props;
return (
<div key={eachBus.key} className={`card ${isSelected ? 'selected' : ''}`} onClick={onClick}>
...
</div>
);
}
export default CardBus;

Getting error when adding a function to my react app

I'm new to react and followed a tutorial to do a shopping cart. In the end, I added a function that would give me the total cost of the products, but I get an error when adding products and clicking on Cart "productList.reduce is not a function". You can see this function (getTotalCost) in Cart. I tried to solve this but I get other errors. These are my files:
ProductsPage :
import React, {useState} from 'react'
import './ProductsPage.css'
import Products from '../../components/Products'
import Cart from '../../components/Cart'
const PAGE_PRODUCTS = 'products';
const PAGE_CART = 'cart';
function ProductsPage() {
const [cart, setCart] = useState ([]);
const [page, setPage] = useState (PAGE_PRODUCTS);
const addToCart = (product) => {
setCart ([...cart, {...product}])
}
const removeFromCart = (productToRemove) => {
setCart(cart.filter(product => product !== productToRemove))
}
const navigateTo = (nextPage) => {
setPage(nextPage);
};
return (
<div className="productspage">
<header>
<button className="cart-btn" onClick={()=> navigateTo(PAGE_CART)}>
Go to Cart ({cart.length})
</button>
<button className="products-btn" onClick={()=> navigateTo(PAGE_PRODUCTS)}>
View Products
</button>
</header>
{page === PAGE_PRODUCTS && <Products addToCart={addToCart}/>}
{page === PAGE_CART && <Cart cart={cart} removeFromCart={removeFromCart} />}
<div>
</div>
</div>
);
};
export default ProductsPage;
Products:
import React, {useState} from 'react'
function Products ({ addToCart }) {
const [products] = useState ([
{
name: 'Breakfast ',
cost:'9.99$',
image: 'https://images.unsplash.com/photo-1569420067112-b57b4f024595?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=634&q=80',
},
{
name: 'Breakfast box ',
cost:'8.99$',
image: 'https://images.unsplash.com/photo-1569419910356-f63064754fc9?ixlib=rb-1.2.1&auto=format&fit=crop&w=700&q=80',
},
{
name: 'Snack box ',
cost:'6.99$',
image: 'https://images.unsplash.com/photo-1569419882964-7db5d339951b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=634&q=80',
},
{
name: '4 small breakfast bowls ',
cost:'9.99$',
image: 'https://images.unsplash.com/photo-1570649857669-4ad9f896435d?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=703&q=80',
}
])
return (
<>
<h1 className="products-title">Products</h1>
<div className="products">
{products.map((product , index) => (
<div className="product" key={index}>
<h3>{product.name}</h3>
<h4>{product.cost}</h4>
<img src={product.image} alt={product.name}/>
<p></p>
<button onClick={() => addToCart(product)}>
Add to Cart
</button>
</div>
))}
</div>
</>
)
}
export default Products;
Cart
import React from 'react'
import products from '../../components/Products'
function Cart ({ cart, removeFromCart }) {
const getTotalCost = (productList) => (
productList.reduce((totalCost, { cost: itemCost }) => totalCost += parseFloat(itemCost), 0)
);
return (
<>
<h1>Cart</h1>
<div className="products">
{cart.map((product , index) => (
<div className="product" key={index}>
<h3>{product.name}</h3>
<h4>{product.cost}</h4>
<img src={product.image} alt={product.name}/>
<button onClick={() => removeFromCart(product)}>
Remove
</button>
{getTotalCost(products)}
</div>
))}
</div>
</>
)
}
export default Cart;
In the second code snippet, where you are calling {getTotalCost(products)}, you are using the products component you are importing on line 2, which is a Component, not a List, so the reduce function does not exist on it, which is why you're seeing that error.
You probably want {getTotalCost(cart)} instead.

React array mapping, toggles all drop-downs on click, I want to open the dropdown for the clicked card only

TextQuoteCard
import React, {useRef, useState} from 'react'
import {Link} from "react-router-dom";
import {QuoteCardDropdown} from "../../utils/dropdowns";
export const TextQuoteCard = () => {
const [open, setOpen] = useState(false)
const toggle = () => setOpen(!open)
const [textQuote, setTextQuote] = useState([
{
userId: '123',
userName: 'Tr',
userImageUrl: 'https://qph.fs.quoracdn.net/main-thumb-892821828-200-lrcgeycqieflgsovvoxglqawinbcjhtv.jpeg',
quoteId: 'TQ122',
postDateTime: 'Fri',
quoteAuthorId: '123',
quoteAuthorName: 'Jhon Mart',
quoteCategory: 'Motivational',
quoteType: 'textQuote',
quoteText: 'If there’s no market, about finding market opportunities, or creating opportunities. If there’s no market, then you need to grow one',
quoteImageUrl: 'https://qph.',
bookmarkStatus: 2,
likesCount: 3300,
commentsCount: 123,
overallShareCount: 1203,
fbShareCount: 423,
twtShareCount: 1232,
waShareCount: 1023,
viewCount: 1923
},
{
userId: '124',
userName: 'nr',
userImageUrl: 'https://qph.fi.jpeg',
quoteId: 'TQ123',
postDateTime: 'Fri',
quoteAuthorId: '123',
quoteAuthorName: 'Wall Mart',
quoteCategory: 'Motivational',
quoteType: 'textQuote',
quoteText: 'Best thing to do. ',
quoteImageUrl: '',
bookmarkStatus: 1,
likesCount: 3300,
commentsCount: 123,
overallShareCount: 1203,
fbShareCount: 423,
twtShareCount: 1232,
waShareCount: 1023,
viewCount: 1923
}
])
const handleBookmark = (event) => {
console.log(event)
}
const idGetter = (id) =>{
console.log(id)
}
const test = Object.keys(textQuote).map(item => item)
console.log(test)
return(
<div>
{
textQuote.map((quote) => (
<div className="QuoteCardPrimaryContainer" key={quote.quoteId}>
<div>{quote.userName}</div>
<div className="ddContainer">
<span className="QuoteCardEngagementActionButtonIconContainer">
<span className="QuoteCardEngagementActionButtonIcon"
onClick={() => toggle(!open)}
>
options
</span>
</span>
{open && <QuoteCardDropdown targetLink={quote.quoteId}/>}
</div>
</div>
))
}
</div>
)
}
**
QuoteCardDropdown.js
import React, {useState} from 'react'
import {Link} from "react-router-dom";
import '../../global/assets/css/dropdowns.css'
export const QuoteCardDropdown = (props) => {
const [ddItems, SetDdItems] = useState([
{
ddOptionIcon: 'icon',
ddOptionText: 'Share',
ddOptionTip: 'Tip text goes here',
ddOptionBorder: 'no',
targetId: props.targetId,
targetLink: props.targetLink
},
{
ddOptionIcon: 'icon',
ddOptionText: 'Bookmark',
ddOptionTip: 'Tip text goes here',
ddOptionBorder: 'no',
targetId: props.targetId,
targetLink: props.targetLink
}
])
return (
<div>
<div className="quoteCardDropdownPrimaryContainer">
<div className="quoteCardDropdownPrimaryBody">
<div className="quoteCardDropdownPrimaryBodyInner">
{
ddItems.map(item => (
<Link to=
{
item.ddOptionText === 'Edit this Quote' ?
`${'edit/' + props.targetLink}` :
item.ddOptionText === 'Share' ?
`${'share/' + props.targetLink}` : ''
}
>
<div className="quoteCardDropdownContentWrapper">
<div className="quoteCardDropdownContentItem">
<div className="quoteCardDropdownItem" key={item.ddOptionText}>
{item.ddOptionText}
</div>
</div>
</div>
</Link>
))
}
</div>
</div>
</div>
<div className="quoteCardPointer" data-placement='top'> </div>
</div>
)
}
I have array of objects mapping to which showed multiple card on-page/feed. each card has a dropdown that the user can perform several actions for the clicked card. think of FB feed or any other social media feed card that the user can click to open a dropdown and pick option for the card. I am trying to achieve something similar but the problem is when I click on the button to open the dropdown it toggles all the dropdowns for all the cards instead of opening the dropdown for the clicked card.
Expected Behavior: open the dropdown for the clicked card only.
Change the open to take the id:
const [open, setOpen] = useState() // undefined is nothing open
const toggle = id => setOpen(open === id ? undefined : id) // close if currently open
// the JSX
return(
<div>
{textQuote.map((quote) => (
<div className="QuoteCardPrimaryContainer" key={quote.quoteId}>
<div>{quote.userName}</div>
<div className="ddContainer">
<span className="QuoteCardEngagementActionButtonIconContainer">
<span className="QuoteCardEngagementActionButtonIcon"
onClick={() => toggle(quote.quoteId)}>
options
</span>
</span>
{open === quote.quoteId && <QuoteCardDropdown targetLink={quote.quoteId}/>}
</div>
</div>
))}
</div>
)
Yes, you are trying to control all dropdowns using a single state variable open.
{open && <QuoteCardDropdown targetLink={quote.quoteId}/>}
When you click on any dropdown it will toggles open and then all dropdowns will open because that single variable controls all of them.
Instead, what you can do is maintain a separate state variable for each dropdown.
I have an example to maintain separate state variable for dropdown-
toggle = (index) => {
this.setState(prevState => {
[`open+${index}`]: !prevState[`open${index}`]
}
}
This way you can keep track of/or toggles open for particular dropdown you just need to change below code -
{
textQuote.map((quote, index) => ( //add 2nd parameter as index
<div className="QuoteCardPrimaryContainer" key={quote.quoteId}>
<div>{quote.userName}</div>
<div className="ddContainer">
<span className="QuoteCardEngagementActionButtonIconContainer">
<span className="QuoteCardEngagementActionButtonIcon"
onClick={() => this.toggle(index)}
>
options
</span>
</span>
{ this.state[`open${index}`] && <QuoteCardDropdown targetLink={quote.quoteId}/>}
</div>
</div>
</div>
))
}
Note - Unfortunately I am not aware of handling state as dynamically inside the function component, but I have given you the exact same use case using class component.
Use an object indexed by quote ID to track a card's open status by the quote ID. I copied only the relevant code, make sure this has all of the other code you need:
export const TextQuoteCard = () => {
const [openById, setOpenById] = useState({});
const toggle = (quoteId) => {
setOpenById((currOpenById) => {
const nextOpenById = { ...currOpenById };
if (currOpenById[quoteId]) {
delete nextOpenById;
} else {
nextOpenById[quoteId] = true;
}
return nextOpenById
})
}
// ...removed code not relevant to example
return (
<div>
{textQuote.map((quote) => (
<div className="QuoteCardPrimaryContainer" key={quote.quoteId}>
<div>{quote.userName}</div>
<div className="ddContainer">
<span className="QuoteCardEngagementActionButtonIconContainer">
<span
className="QuoteCardEngagementActionButtonIcon"
onClick={() => toggle(quote.quoteId)}
>
options
</span>
</span>
{openById[quote.quoteId] && <QuoteCardDropdown targetLink={quote.quoteId} />}
</div>
</div>
))}
</div>
);
};

Reset pagination to the first page by clicking a button outside the component

I'm using material UI usePagination hook to create a custom pagination component, so far so good, the functionality works as expected but I was wondering how I can be able to reset the pagination to the first page by triggering a button that is not part of the pagination component.
Does anyone has an idea on how to trigger that?
This is my component.
import React from "react";
import PropTypes from "prop-types";
import { usePagination } from "hooks";
function arrow(type) {
return (
<i
className={`fa fa-chevron-${
type === "next" ? "right" : "left"
} page-icon`}
/>
);
}
function Pagination({ data, itemCount, onChange }) {
const { items } = usePagination({
count: Math.ceil(data.length / itemCount, 10),
onChange
});
return (
<nav aria-label="Paginator">
<ul className="pagination-component">
{items.map(({ page, type, selected, ...item }, index) => {
let children;
if (type === "start-ellipsis" || type === "end-ellipsis") {
children = "…";
} else if (type === "page") {
children = (
<button
type="button"
automation-tag={`page-${page}`}
className={`page-button ${selected ? "selected" : ""}`}
{...item}
>
{page}
</button>
);
} else {
children = (
<button
automation-tag={type}
className="page-button"
type="button"
{...item}
>
<span className="d-none">{type}</span>
{arrow(type)}
</button>
);
}
return (
// eslint-disable-next-line react/no-array-index-key
<li key={index} className="page-item">
{children}
</li>
);
})}
</ul>
</nav>
);
}
What I'm trying is to create a select component that the onChange function will sort the data, depending on the selection, but when the data is sorted I want to return the pagination component to the first page
const TableVizContainer = props => {
const [currentPage, setCurrentPage] = useState(1);
const [sortColumn, setSortColumn] = useState(1);
const [range, setRange] = useState({
start: 0,
end: 25
});
const onChangePage = (_event, page) => {
setCurrentPage(page);
setRange({
start: 25 * (page - 1),
end: 25 * page
});
};
const onSelectChange = event => {
const { value } = event.target;
setCurrentPage(1);
setSortColumn(parseInt(value, 10));
};
return (
<div
className="table-viz-container container-fluid my-4 float-left"
automation-tag={`table-viz-${automationId}`}
>
<div className="d-flex justify-content-between mb-3 leaderboard-meta">
<span className="leaderboard-title">{visualization.title}</span>
<div className="mr-5">
<label htmlFor="sort-table-select">
Sort By:
<select
id="sort-table-select"
onChange={onSelectChange}
value={sortColumn}
>
{visualization.columns.map((column, index) => {
const uniqueId = uuidv1();
return (
<option key={uniqueId} value={index}>
{setSelectValue(column, visualization.metrics)}
</option>
);
})}
</select>
</label>
</div>
</div>
<div className="d-block d-sm-flex justify-content-between align-items-center my-2 px-2">
<span className="page-items-count" automation-tag="pagination-count">
{`Showing ${range.start === 0 ? 1 : range.start + 1} - ${
range.end <= visualization.rows.length
? range.end
: visualization.rows.length
} of ${visualization.rows.length}.`}
</span>
<Pagination
currentPage={currentPage}
data={visualization.rows}
itemCount={25}
onChange={onChangePage}
/>
</div>
</div>
);
};
Does anyone has an idea on how to reset and move the pagination page to the first one without clicking the component?
There are two ways.
1. Passing Props
Let's just say you have a function called jump() and passing 1 as an argument will reset the pagination. So, you can pass the jump function as a property and reuse that on other components.
function jump(){
setCurrentPage(1)
}
<MyCompnent resetPage={jump} />
// MyComponent
function MyComponent({resetPage}){
return (
<button onClick={resetPage(1)}></button>
)
}
2. On Changing Route
You can reset your pagination when your route will change. For example, you are using a router npm package and that package has a method called onChange or routeChangeStart. With those methods or after creating that method you can implement a function like below.
Router.events.on("routeChangeStart", () => {
jump(1);
});

React Hook select multiple items with icons

I am trying to implement a popover with many items where a user can multi select them. When a user clicks a item, a font-awesome icon is shown to the right.
A user is able to select multiple items and an icon is shown on the right to show it has been checked. This icon toggles when clicking. My problem is my event handler is tied to all the items and whenever I click one, all gets checked.
I am new to hook and react. I am also trying to assign the Id of the selected item in an array. It won't append.
const SettingsComponent = (props) => {
const urlStofTyper = stofTyperUrl;
const stofTyper = [];
const [isPopoverOpen, setPopoverOpen] = useState(false);
const [isItemChecked, setItemChecked] = useState(false);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(null);
const [stoftype, setStoftyper] = useState({ DataList: [] });
const toggle = () => setPopoverOpen(!isPopoverOpen);
const sectionClicked = (e) => {
setItemChecked(!isItemChecked);
let secId = e.target.parentNode.getAttribute("data-section");
if (!isItemChecked) {
stofTyper.push(secId);
} else {
stofTyper.filter((sec) => sec == secId);
}
};
useEffect(() => {
fetchStoftyper({ setError, setLoading, setStoftyper });
}, []);
const fetchStoftyper = async ({ setError, setLoading, setStoftyper }) => {
try {
setLoading(true);
const response = await Axios(urlStofTyper);
const allStofs = response.data;
setLoading(false);
setStoftyper(allStofs);
} catch (error) {
setLoading(false);
setError(error);
}
};
return (
<React.Fragment>
<div className='list-header-icons__fontawesome-icon' id='PopoverClick'>
<FontAwesomeIcon icon={faCog} />
</div>
<Popover
isOpen={isPopoverOpen}
placement='bottom'
toggle={toggle}
target='PopoverClick'>
<PopoverHeader>formatter</PopoverHeader>
<div className='popover-body'>
<ul className='individual-col--my-dropdown-menu-settings'>
{stoftype.DataList.map((item) => (
<li key={item.Id} className='section-items'>
<a
onClick={sectionClicked}
className='dropdown-item'
data-section={item.Sections[0].SectionId}
data-format={
item.Formats.length > 0
? item.Formats[0].FormatId
: ""
}
aria-selected='false'>
<span className='formatter-name'>
{item.Name}
</span>
{isItemChecked && (
<span className='formatter-check-icon'>
<FontAwesomeIcon icon={faCheck} size='lg' />
</span>
)}
</a>
</li>
))}
</ul>
</div>
</Popover>
</React.Fragment>
);
Right now you are using one boolean variable to check if the icon should be displayed, it will not work because every item in your DataList should have it's own individual indicator.
One of the possible solution is to use new Map() for this purposes and store item.id as and index and true/false as a value, so your selected state will be something like this:
Map(3) {1 => true, 2 => true, 3 => false}
After that you can check if you should display your icon as follow:
!!selected.get(item.id)
It will return true if the value in your HashTable is true, and false if it's false or doesn't exist at all. That should be enough to implement the feature you asked for.
For the real example you can check flatlist-selectable section from official Facebook docs They show how to achieve multi-selection using this technique. Hope it helps.
Finally, I have a solution for my own question. Even though, I did not need above solution but I though it would be a good practice to try solve it. Popover is not relevant as that was used only as a wrapper. The below solution can still be placed in a Popover.
CodeSandBox link: Demo here using Class component, I will try to re-write this using hooks as soon as possible.
This solution dependency is Bootstrap 4 and Fontawesome.
import React, { Component } from "react";
import Cars from "./DataSeed";
class DropDownWithSelect extends Component {
constructor(props) {
super(props);
this.toggleDropdown = this.toggleDropdown.bind(this);
this.state = {
isDropDownOpen: false,
idsOfItemClicked: []
};
}
toggleDropdown = evt => {
this.setState({
isDropDownOpen: !this.state.isDropDownOpen
});
};
toggleItemClicked = id => {
this.setState(state => {
const idsOfItemClicked = state.idsOfItemClicked.includes(id)
? state.idsOfItemClicked.filter(x => x !== id)
: [...state.idsOfItemClicked, id];
return {
idsOfItemClicked
};
});
};
render() {
return (
<div className="dropdown">
<button
onClick={this.toggleDropdown}
className="btn btn-secondary dropdown-toggle"
type="button"
id="dropdownMenuButton"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
Dropdown button
</button>
{this.state.isDropDownOpen && (
<div
className="dropdown-menu show"
aria-labelledby="dropdownMenuButton"
>
{Cars.map(x => {
return (
<div
key={x.id}
onClick={() => this.toggleItemClicked(x.id)}
className="dropdown-item d-inline-flex justify-content-between"
>
<span className="d-flex"> {x.name} </span>
<span className="d-flex align-self-center">
{this.state.idsOfItemClicked.includes(x.id) && (
<i className="fa fa-times" aria-hidden="true" />
)}
</span>
</div>
);
})}
</div>
)}
</div>
);
}
}
export default DropDownWithSelect;
Data i.e., ./DataSeed
const cars = [
{
id: 1,
name: "BMW",
cost: 450000
},
{
id: 2,
name: "Audi",
cost: 430000
},
{
id: 3,
name: "Mercedes",
cost: 430000
}
];
export default cars;

Resources