Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bootstrap dropdowns in ReactJS, only one open at a time

I have a page contains multiple Bootstrap Cards and each card is a component and each card footer is also a component. Card Footer contains buttons. When you click on a button, drop down will be opened like below

enter image description here

At any point of time when I click on a button, other drop downs should be in closed state. But its happening like this...

enter image description here

Requirement: One more thing is when I click on the same button, the respective drop down should be closed.

Requirement: When I click on any item inside drop down the respective drop down should be closed

My Architecture is like below

enter image description here

HOME PAGE COMPONENT CODE -START

class HomePage extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      items: [],
      activatedIdStoredInParent: ""
    };
  }
  toggleCountersMenu = (name) => {
    var name1 = name;
    this.setState((prevState) => {
      return {
        activatedIdStoredInParent: name1
      }
    });
  } 

  render() {
   
    const products = this.state.items.map((item, index) => {     
      return <div>
        <Card
          product={item}
          activatedIdStoredInParent={this.state.activatedIdStoredInParent}
          toggleCountersMenu={this.toggleCountersMenu}
        >
        </Card>;
      </div>
    });

    return (
      <div>
        <div className="card-columns">
          {products}
        </div>
      </div >
    );
  }
}

export default HomePage;

HOME PAGE COMPONENT CODE - END

CARD COMPONENT CODE - START

class Card extends React.Component {
    handleActionClick = (name) => {
        this.props.toggleCountersMenu(name);
    }
    render() {
        return (
            <div key={this.props.product.name}>
                <CardHeader product={this.props.product}  />
                <CardBody product={this.props.product}  />
                <CardFooter
                    product={this.props.product}                    
                    onActionItemClick={this.handleActionClick}
                    activatedIdStoredInParent={this.props.activatedIdStoredInParent}
                />
            </div>
        );
    }
}

export default Card;

CARD FOOTER COMPONENT CODE - START

class CardFooter extends React.Component {

    handleActionItemClick = (name) => {
        this.props.onActionItemClick(name);
    }

    render() {
        console.log('Card Footer Drop Down comp rendered');
        return (
            <div className=" card-footer text-center">
                <ButtonDropdown text="F" className="danger"
                    product={this.props.product}
                    onActionItemClick={this.handleActionItemClick}
                    activatedIdStoredInParent={this.props.activatedIdStoredInParent}
                ></ButtonDropdown>            
            </div>
        );
    }
}

export default CardFooter;

ButtonDropdown COMPONENT CODE - START

class ButtonDropdown extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            open: false,
            show: ' none',
            localActivatedId: 'none'
        }
    }
    toggleOpen = (e) => {
        var name = e.target.name;

        this.setState((prevState, props) => {
            var item = {
                localActivatedId: name
            }
            if (props.activatedIdStoredInParent === name) {
                if (prevState.show === ' show') {
                    item.show = ' none';
                }
                else {
                    item.show = ' show';
                }
            }
            return item;
        });
        this.props.onActionItemClick(name);
    }

    numberClick = (e) => {
        var qty = e.target.innerText;
        this.setState((prevState, props) => {
            var item = {
                show: ' none'
            }
            return item;
        });
    }
    render() {
        return (
            <div className="btn-group" >
                <button type="button" className={`btn btn-${this.props.className}  mr-1`} name={this.props.product.name + '$$' + this.props.text} onClick={this.toggleOpen}>
                    {this.props.text} (classAdded={this.state.show})
                </button>
                
                <div className={`dropdown-menu ${this.state.show}`}>
                    <span className="dropdown-item cursor-pointer " onClick={this.numberClick}>
                        -1
                    </span>
                    <span className="dropdown-item cursor-pointer" onClick={this.numberClick}>
                        -2
                    </span>
                </div>
            </div>



        );
    }
}

export default ButtonDropdown;

When I add multiple buttonDropdown components in Card Footer the end product is like this. How can I close other dropdowns.

enter image description here

I would like to know is my architecture is correct.. I am not using Redux/Flux etc..

like image 858
Abhi Avatar asked Oct 01 '18 09:10

Abhi


People also ask

How do I stop bootstrap dropdown from closing?

This behavior can be changed by using the autoClose property. By default, autoClose is set to the default value true and behaves like expected. By choosing false, the dropdown menu can only be toggled by clicking on the dropdown button.

How Use dropdown in bootstrap React JS?

We can use the following approach in ReactJS to use the react-bootstrap Dropdown Component. Dropdown Props: alignRight: It is used to align the menu to the right side of the Dropdown toggle. as: It can be used as a custom element type for this component.

Why my dropdown is not working in React?

you just have to add a css to resolve this issue. when you click on dropdown button, it adds a class "show" to that element and as per bootstrap that much is sufficient to make a dropdown button work.


1 Answers

You can use the componentDidUpdate lifecycle, in order to update your state's property that is opening the dropdown. I don't know if it's the open or show property that displays the content of the dropdown but here's my logic.

class ButtonDropdown extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      // 
    };
  }

  componentDidUpdate(prevProps) {
    const name = this.props.product.name + '$$' + this.props.text;

    if (prevProps.activatedIdStoredInParent !== this.props.activatedIdStoredInParent && this.props.activatedIdStoredInParent !== name) {
      this.closeDropDown();
    }
  }

  closeDropDown = () => this.setState({ isOpen: false });

  toggleOpen = (e) => {
    //
  }

  numberClick = (e) => {
   //
  }

  render() {
   //
  }
}

export default ButtonDropdown;
like image 88
soupette Avatar answered Oct 19 '22 11:10

soupette