简体   繁体   English

ReactJS中有多个下拉菜单,一次只能打开一个

[英]Multiple dropdowns in ReactJS, only one open at a time

I have two dropdowns being passed in as props to components. 我有两个下拉菜单作为对组件的传递。 I can control both with seperate states but I think this can all be done with one state? 我可以用单独的状态来控制这两个状态,但是我认为可以用一个状态来完成全部操作吗?

Header 标头

import React from 'react';
import DarkLabel from './DarkLabel';
import HeaderDropdown from './HeaderDropdown';

export default class Header extends React.Component {

    componentWillMount() {
        this.setState({
            listOpen: false
        })
    }

    render() {
        ...
        return (
            <div className="row header">
                <div className="col-xs-10">
                    <DarkLabel classExtra="dark-label-lg" icon="/images/system-icons/document_empty.png"
                               content={taskCode}/>
                    <DarkLabel classExtra="dark-label-2x dark-label-lg" icon="/images/system-icons/building.png"
                               dropdown=<HeaderDropdown data={this.props.enquiry.entity ? this.props.enquiry.entity : null}/>
                               content={this.props.enquiry.entity ? this.props.enquiry.entity.name : 'ERROR'}
                               right_icon="/images/system-icons/bullet_arrow_down.png"/>
                    <DarkLabel classExtra="dark-label-md" icon="/images/system-icons/ceo.png"
                               dropdown=<HeaderDropdown data={this.props.enquiry.contact ? this.props.enquiry.contact : null}/>
                               content={this.props.enquiry.contact ? this.props.enquiry.contact.firstName + ' ' + this.props.enquiry.contact.lastName : '-'}
                               right_icon="/images/system-icons/bullet_arrow_down.png"/>
                    <DarkLabel classExtra="flag"
                               content={'/images/flags/large/'+this.props.enquiry.entity.country.countryCode+'.png'}
                               right_icon="/images/system-icons/cog.png" right_icon_callback={this.handleAddressModal.bind(this)}/>
                </div>
            </div>
        )
    }

}

HeaderDropdown HeaderDropdown

import React from 'react'; 从'react'导入React;

export default class HeaderDropdown extends React.Component {

    componentWillMount() {

    }

    render() {

        return (
            <div>
                <div className="dark-label-dropdown">
                    Test
                </div>
            </div>
        );
    }
}

How can I make it so only one can be open at a time and close all others if a different one is clicked? 我如何才能做到这样,一次单击只能打开一个,如果单击另一个按钮,则关闭所有其他按钮? Do I need to store something from 'this' when I bind a click event to HeaderDropdown? 当我将Click事件绑定到HeaderDropdown时,是否需要存储来自“ this”的内容?


I have missed something from the question. 我错过了这个问题。 This needs to happen when the user clicks on right_icon in the DarkLabel. 用户在DarkLabel中单击right_icon时,就需要发生这种情况。

DarkLabel 黑暗标签

import React from 'react';

export default class DarkLabel extends React.Component {

    componentWillMount() {

    }

    truncate(limit) {
...
    }

    render() {
        var icon = '', content = '';
        var rightIcon = '';
        ...

        return (
            <div className={'pull-left dark-label-wrapper '+this.props.classExtra}>
                {icon}
                <div>{content}</div>
                {rightIcon}
                {this.props.dropdown}
            </div>
        );
    }
}

What you could do is add a onClick method to your HeaderDropdown and in your Header handle the state for that. 您可以做的是将一个onClick方法添加到HeaderDropdown并在Header处理该状态。 The proper way would be using a store of some kind (redux) but that might be out of scope for this example. 正确的方法是使用某种类型的存储(redux),但这在本示例中可能超出范围。

So in short I suggest you alter you Header to this: 简而言之,我建议您将标题更改为:

import React from 'react';
import DarkLabel from './DarkLabel';
import HeaderDropdown from './HeaderDropdown';

export default class Header extends React.Component {

    componentWillMount() {
        this.setState({
            listOpen: false,
            activeDropdown: 1
        })
    }

    render() {
        const headerDropdownA = (
            <HeaderDropdown 
                data={...} 
                onClick={() => this.setState({activeDropDown: 1})} 
                        isActive={this.state.activeDropdown === 1}
                />
        )

        const headerDropdownA = (
            <HeaderDropdown 
                data={...} 
                onClick={() => this.setState({activeDropDown: 2})} 
                        isActive={this.state.activeDropdown === 2}
                />       
        )
        return (
            <div className="row header">
                <div className="col-xs-10">
                   <DarkLabel  classExtra="..."
                                         dropdown={headerDropdownA}
                               content={...}
                               right_icon="/images/system-icons/bullet_arrow_down.png"/>
                    <DarkLabel classExtra="..."
                               dropdown={headerDropdownB}
                               content={...}
                               right_icon="/images/system-icons/bullet_arrow_down.png"/>
                </div>
            </div>
        )
    }
}

I added a state for the current active dropdown. 我为当前的活动下拉列表添加了状态。 In your dropdown you can then access the active state with this.props.isActive . 然后,您可以在此下拉列表中使用this.props.isActive访问活动状态。

But propably this will not already be sufficient since every click will toggle the dropdown again, also when you click the options? 但这可能还不够,因为每次单击都将再次切换下拉菜单,甚至在您单击选项时也是如此? But it might give you a good starting point. 但这可能会为您提供一个良好的起点。

You can create a high-order-component that will detect clicks outside of your component eg 您可以创建一个高阶组件,以检测组件外部的点击,例如

import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';

const clickOutsideEvents = [ 'mousedown', 'touchstart' ];
const isDescendant = ( el, target ) => target !== null ? el === target || isDescendant( el, target.parentNode ) : false;

export default class ClickOutside extends Component {

      static propTypes =
      {
            children       : PropTypes.node,
            onClickOutside : PropTypes.any,
      };

      componentDidMount()
      {
            if ( !this.props.onClickOutside ) return;
            clickOutsideEvents.forEach( e => document.addEventListener( e, this.handleClickOutside ) )
      }

      /**
       * Remove the listener in case the props change and there is not ClickAway handler
       * @param  { Object } prevProps
       */
      componentDidUpdate( prevProps )
      {
            if ( prevProps.onClickOutside !== this.props.onClickOutside )
            {
                  clickOutsideEvents.forEach( e => document.removeEventListener( e, this.handleClickOutside ) );

                  if ( this.props.onClickOutside )
                  {
                      clickOutsideEvents.forEach( e => document.addEventListener( e, this.handleClickOutside ) )
                  }
            }
      }

      /**
       * Remove listeners when Component unmount
       */
      componentWillUnmount()
      {
           clickOutsideEvents.forEach( e => document.removeEventListener( e, this.handleClickOutside ) );
      }

      /**
       * Call callback on ClickAway and pass the event
       * @param  event
       */
      handleClickOutside = ( e ) =>
      {
            const el = ReactDOM.findDOMNode( this );

            if ( document.documentElement.contains( e.target ) && !isDescendant( el, e.target ) )
            {
                this.props.onClickOutside( e );
            }
      };

      /**
       * Render the Elements that are Wrapped by the ClickAway
       */
      render()
      {
           return this.props.children;
      }
}

Then wrap your dropdowns with this HOC and set the state of the component based on that 然后使用此HOC包装您的下拉列表,并根据该状态设置组件的状态

eg 例如

setDropdownState(){
   this.setState({ listOpen: false });
}


render(){ 
  return(<ClickOutside onClickOutside={ this.setDropdownState.bind( this ) }>
     <HeaderDropdown listOpen={ this.state.listOpen }>
  </ClickOutside>)
}

You can check this implementation https://github.com/AvraamMavridis/react-clickoutside-component 您可以检查此实现https://github.com/AvraamMavridis/react-clickoutside-component

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM