I'm building a multiple selection in React with a react-bootstrap ListGroup.
Despite having the active
property of an item set to false
though, the last clicked item still gets active in its class list.
I've been considering getting a reference to the list item and manipulate the class list, but that is too dirty.
I tried changing the variant
attribute of the ListGroup.Item
, but the active class overrides that. I'd prefer to not modify the css class definition.
I prefer using the onSelect handler of the ListGroup instead of using the ToggleButton's onClick event from a usability perspective.
Have you tried using a ListGroup to manipulate multiple selection?
Here is stripped version of the code:
import { useState } from "react";
import "./styles.css";
import ListGroup from "react-bootstrap/ListGroup";
import Container from "react-bootstrap/Container";
import Row from "react-bootstrap/Row";
import Col from "react-bootstrap/Col";
import ToggleButton from "react-bootstrap/ToggleButton";
import "bootstrap/dist/css/bootstrap.min.css";
import { cloneDeep } from "lodash";
export default function App() {
const allItems = [
{ id: 1, title: "A" },
{ id: 2, title: "B" },
{ id: 3, title: "C" },
{ id: 4, title: "D" },
{ id: 5, title: "E" }
];
const [selectedItems, setSelectedItems] = useState([
{ id: 2, title: "B" },
{ id: 4, title: "D" }
]);
function toggleSelection(eventKey, e) {
const itemId = parseInt(eventKey?.replace(/^itemSelect_/, ""), 10);
const newSelectedItems = cloneDeep(selectedItems);
const indexInSelection = selectedItems.findIndex(
(sitm) => sitm.id === itemId
);
if (indexInSelection >= 0) {
newSelectedItems.splice(indexInSelection, 1);
} else {
const newItem = allItems.find((itm) => itm.id === itemId);
newSelectedItems.push(newItem);
}
setSelectedItems(newSelectedItems);
}
return (
<div className="App">
<ListGroup onSelect={toggleSelection}>
{allItems.map((itm) => {
return (
<ListGroup.Item
eventKey={`itemSelect_${itm.id}`}
key={`itemSelect${itm.id}`}
active={
selectedItems.find((sitm) => sitm.id === itm.id) !== undefined
}
>
<Container>
<Row>
<Col sm="2">
{" "}
<ToggleButton
className="mx-1"
type="checkbox"
size="sm"
key={`check${itm.id}`}
checked={
selectedItems.find((sitm) => sitm.id === itm.id) !==
undefined
}
value={itm.id}
></ToggleButton>
</Col>
<Col sm="10">{itm.title}</Col>
</Row>
</Container>
</ListGroup.Item>
);
})}
</ListGroup>
</div>
);
}
And a live preview is available at Code Sandbox
Thanks in advance for your help!
It's something to do with eventKey
and onSelect
- ListGroup
and ListGroup.Item
have some internal implementations on various user actions as you can see in docs. Try this implementation instead. I removed onSelect
on ListGroup
and added onClick
event on ListGroup.Item
where we don't need eventKey anymore and pass itm.id
directly.