【问题标题】:Multiple dropdowns in ReactJS, only one open at a timeReactJS 中的多个下拉菜单,一次只打开一个
【发布时间】:2016-09-17 07:51:23
【问题描述】:

我有两个下拉菜单作为组件传递给组件。我可以用单独的状态控制这两种状态,但我认为这可以用一个状态来完成?

标题

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

从“反应”导入反应;

export default class HeaderDropdown extends React.Component {

    componentWillMount() {

    }

    render() {

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

我怎样才能让它一次只能打开一个,如果点击另一个,则关闭所有其他?当我将点击事件绑定到 HeaderDropdown 时,是否需要存储“this”中的内容?


我从问题中遗漏了一些东西。这需要在用户单击 DarkLabel 中的 right_icon 时发生。

深色标签

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>
        );
    }
}

【问题讨论】:

  • 你应该像这样在构造函数中初始化你的状态:this.state = {listOpen: false}
  • 与componentWillMount有什么区别?
  • 实际上,afaik 的功能并没有太大区别,但是构造函数是在类中初始化属性的更惯用的方式(React 组件就是这样)

标签: javascript reactjs ecmascript-6 fluxible


【解决方案1】:

您可以做的是向您的HeaderDropdown 添加一个onClick 方法,并在您的Header 中处理该状态。正确的方法是使用某种类型的商店(redux),但这可能超出了本示例的范围。

所以简而言之,我建议您将标题更改为:

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>
        )
    }
}

我为当前活动的下拉菜单添加了一个状态。然后,您可以在下拉列表中使用this.props.isActive 访问活动状态。

但这可能还不够,因为每次点击都会再次切换下拉菜单,当您点击选项时也是如此?但它可能会给你一个很好的起点。

【讨论】:

  • 谢谢,我会试试的,顺便说一句,我正在使用 Fluxible 商店。
【解决方案2】:

您可以创建一个高阶组件来检测组件外部的点击,例如

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;
      }
}

然后用这个 HOC 包装你的下拉菜单并根据它设置组件的状态

例如

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


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

你可以检查这个实现https://github.com/AvraamMavridis/react-clickoutside-component

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-02-26
    • 2021-10-19
    • 2017-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多