我正在尝试实现一个包含许多项目的弹出窗口,用户可以在其中多选它们。当用户单击某个项目时,右侧会显示一个字体很棒的图标。
用户可以选择多个项目,右侧会显示一个图标,表示它已被选中。此图标在单击时切换。我的问题是我的事件处理程序绑定到所有项目,每当我单击一个项目时,都会检查所有项目。
我是钩子和反应的新手。 我还尝试在数组中分配所选项目的 ID。它不会追加。
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>
);
现在你正在使用一个布尔变量来检查是否应该显示图标,它不起作用,因为你DataList
中的每个项目都应该有自己的单独指标。
一种可能的解决方案是将new Map()
用于此目的,并将item.id
存储为值,并将索引和true/false
存储为值,因此您选择的状态将如下所示:
Map(3) {1 => true, 2 => true, 3 => false}
之后,您可以检查是否应按如下方式显示图标:
!!selected.get(item.id)
如果 HashTable 中的值为 true,它将返回 true,如果值为 false 或根本不存在,则返回 false。这应该足以实现您要求的功能。
对于真实示例,您可以从官方Facebook文档中查看平面列表可选部分 他们展示了如何使用这种技术实现多选。希望对您有所帮助。
最后,我有一个解决方案来解决我自己的问题。即使,我不需要上述解决方案,但我认为尝试解决它是一个很好的做法。弹出框无关紧要,因为它仅用作包装器。以下解决方案仍然可以放置在弹出框中。
CodeSandBox 链接:在这里使用类组件演示,我将尝试尽快使用钩子重写它。
这个解决方案依赖项是Bootstrap 4和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;
数据,即./数据种子
const cars = [
{
id: 1,
name: "BMW",
cost: 450000
},
{
id: 2,
name: "Audi",
cost: 430000
},
{
id: 3,
name: "Mercedes",
cost: 430000
}
];
export default cars;