Как вызвать только один выпадающий список среди множества других созданных внутри.map
Очевидно, это очень нубский вопрос, но я застрял, поэтому, пожалуйста, помогите!
В моем компоненте MenuWithDropdown я создал несколько выпадающих меню. Выпадающий список должен быть активирован функцией handleClick(). То, чего я пытаюсь достичь, обычно делается с помощью this.state.isActive и троичного оператора:
className={this.state.isActive ? "dropdown is-active" : "dropdown"}
Тем не менее, я не понимаю, как может вызвать только один выпадающий список (например, номер 3). Должен ли я сделать state.isActive1, state.isActive2, state.isActive3 и каким-то образом получить к ним доступ позже в handleClick (ключ)? Или я должен получить доступ к ключевому свойству выпадающего меню, которое оно получает через карту, например if (Dropdown.key === 3) setState(isActive:true)
class MenuWithDropdown extends React.Component {
state = {
isActive: false
};
render() {
const { Menu } = this.props;
const items = Menu.map((menuitem, key) => (
<Fragment key={key}>
<hr className="dropdown-divider" />
<div key={key} className="dropdown is-left">
<div className="dropdown-header">
<NavLink to={menuitem.url}>
<span>{this.context.t(menuitem.name)}</span>
</NavLink>
<FontAwesomeIcon icon="chevron-down" size="2x" />
</div>
<div className="dropdown-menu">
<DropdownItem children={menuitem.children} />
</div>
</div>
</Fragment>
));
return <Fragment>{items}</Fragment>;
}
handleClick = (e, key) => {
e.preventDefault();
this.setState({
isActive: true
});
};
}
3 ответа
Одним из лучших и самых простых способов сделать это было бы сохранить индекс (ключ) DropDownItem, который был нажат в вашем состоянии menuWithDropDown из функции.map, а затем передать опору DropDownItem, чтобы он мог проверить, выполняется ли условие иметь тот же индекс, что и индекс элемента состояния меню (который вы ранее сохранили), он должен открываться или нет. Вот версия вашего кода, чтобы сделать то же самое, как описано выше: (надеюсь, это поможет)
class MenuWithDropdown extends React.Component {
state = {
activeItemKey: '',
};
render() {
const { Menu } = this.props;
const items = Menu.map((menuitem, key) => (
<Fragment key={key}>
<hr className="dropdown-divider" />
<div key={key} className="dropdown is-left">
<div className="dropdown-header">
<NavLink to={menuitem.url}>
<span>{this.context.t(menuitem.name)}</span>
</NavLink>
<FontAwesomeIcon icon="chevron-down" size="2x" />
</div>
<div className="dropdown-menu">
<DropdownItem shouldOpen={this.state.activeItemKey === key} onClick={()=>{this.handleClick(key)}} children={menuitem.children} />
</div>
</div>
</Fragment>
));
return <Fragment>{items}</Fragment>;
}
handleClick = (key) => {
this.setState({
activeItemKey: key
});
};
}
То, как я бы разработал это меню, в основном так:
(посмотрите этот рабочий пример: https://stackblitz.com/edit/toggling-menus)
Внутри моего компонента приложения у меня есть menus
состояние, в котором я храню данные для каждого меню. На componentDidMount
Я генерирую меню и передаю состояние MenusContainer через реквизит.
class App extends Component {
constructor() {
super();
this.state = {
menus: []
};
}
componentDidMount(){
const menus = [
{
id: 1,
title: 'Menu one',
content: 'some content for menu 1'
},
{
id: 2,
title: 'Menu two',
content: 'some content for menu 2'
},
{
id: 3,
title: 'Menu three',
content: 'some content for menu 3'
}
];
this.setState({menus})
}
render() {
return (
<div>
<MenusContainer menus={this.state.menus}/>
</div>
);
}
}
MenusContainer будет отвечать за рендеринг меню, повторяя его menus
опора и настройка текущего меню. Для этого у него будет currentMenu
состояние и метод setMenu, передавая их как реквизиты DropdownMenu
дети от props.menus
,
export default class MenusContainer extends React.Component {
constructor(props) {
super();
this.state = {
currentMenu: -1,
}
}
setMenu = (id) => {
if(id === this.state.currentMenu) {
this.setState({currentMenu: -1})
} else {
this.setState({currentMenu: id})
}
}
render() {
return <div id='container'>
{this.props.menus.map((menu, i) => {
return <DropdownMenu
key={i}
data={menu}
currentItem={this.state.currentMenu}
setMenuCallback={this.setMenu}
/>
})}
</div>
}
}
DropdownMenu узнает (из реквизита), какое меню запрашивается в данный момент, и запустит метод, чтобы установить себя в качестве текущего меню из своих реквизитов.
Обратите внимание, что меню будет отображать содержимое в соответствии с props.currentItem
,
export default class DropdownMenu extends React.Component {
toggleSelf = () => {
const {id} = this.props.data;
this.props.setMenuCallback(id)
}
render() {
return <div className='menu-item' onClick={this.toggleSelf}>
<h1>{this.props.data.title}</h1>
{
this.props.currentItem === this.props.data.id ?
<div>{this.props.data.content}</div> : null
}
</div>
}
}
Хорошо, решение, благодаря Mehrnaz.sa, было действительно простым - сохранить активный ключ в состоянии, а не логическое значение true-false, открывающее-закрывающее раскрывающийся список.
Рабочий код:
class MenuWithDropdown extends React.Component {
state = {
activeKey: 0
}
render() {
const { Menu } = this.props
const items = Menu.map((menuitem, key) =>
<Fragment key={key}>
<hr className="dropdown-divider" />
<div key={key}
className={this.state.activeKey === key ? "dropdown is-left is-active" : "dropdown is-left"}>
<div className="dropdown-header">
<NavLink to={menuitem.url}>
<span>{this.context.t(menuitem.name)}</span>
</NavLink>
//clicking on this icon trigger the dropdown
<FontAwesomeIcon icon="chevron-down" size="2x" onClick={e => {this.handleClick(e, key)}}/>
</div>
<div className="dropdown-menu">
// this is just a text, links to other parts of an app
<DropdownItem children={menuitem.children} />
</div>
</div>
</Fragment>
)
return (
<Fragment>
{items}
</Fragment>
)
}
handleClick = (e, key) => {
e.preventDefault()
this.setState({
activeKey: key
})
}
}