Как вызвать только один выпадающий список среди множества других созданных внутри.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
    })
  }
}
Другие вопросы по тегам