rowlandev
rowlandev

Reputation: 37

Collapse each button uniquely, instead of all at once

I have a <div> tag that encloses a varied number of buttons. When each button is clicked, I have a react variable that shows. My problem is that every hidden text shows whenever I click any button; however I only want the buttons to display their hidden text if I click them, so that they behave uniquely.

I have tried some ideas with conditional rendering, but I'm not sure how to attack that correctly.

I have a function that creates the buttons for each church. Here is that function:

  createButtonsForChurches(arr) {
    var listOfButtons = [];

    for (var i = 1; i < arr.length; i++) { // loop through each church

      var currentButton = (
        <div>
          <button onClick={this.toggle} className="churchButton">{arr[i].name}</button>
          {
            this.state.on && (
              <div className="allContent">
                <div className="individualContent">
                  {(arr[i].femaleStudents[0]) && this.createContentParagraphTextForArray(arr[i].femaleStudents, "Female Students")}
                </div>
                <div className="individualContent">
                  {(arr[i].femaleLeaders[0]) && this.createContentParagraphTextForArray(arr[i].femaleLeaders, "Female Leaders")}
                </div>
                <div className="individualContent">
                  {(arr[i].maleStudents[0]) && this.createContentParagraphTextForArray(arr[i].maleStudents, "Male Students")}
                </div>
                <div className="individualContent">
                  {(arr[i].maleLeaders[0]) && this.createContentParagraphTextForArray(arr[i].maleLeaders, "Male Leaders")}
                </div>
              </div>
            )
          }
      </div>
      );

      listOfButtons.push(currentButton);

    }

    return listOfButtons;
  }

You can see the toggle function that is called for the onClick attribute as well. Here is my toggle method and my constructor:

  constructor(props) {
    super(props);
    this.toggle = this.toggle.bind(this);
    this.state = {
      on: false
    }
  }

  toggle() {
    this.setState({
      on: !this.state.on
    });
  }

I don't want to put too much code and complicate things - arr is an array of objects. Those objects contain 4 different arrays, and each of those arrays hold a different number of objects. So - arr is the list of churches, which contains the churches as objects, and each church contains 4 arrays (female and male students and leaders), and inside each of those 4 arrays there are members as objects wherever they belong

I do not know how to only show the hidden text for the buttons that I click. Would love some help.

Upvotes: 0

Views: 127

Answers (1)

Engineer
Engineer

Reputation: 1261

You have to create an array for buttons and give property isHidden to show/hide data on toggle. This is the working solution of your question.

class App extends React.Component {
  state = {
    buttons: []
  };
  arr = [
    {
      name: "churchName1",
      femaleStudents: ["student1", "student1"],
      femaleLeaders: ["leaders1", "leaders1"],
      maleStudents: ["student1", "student1"],
      maleLeaders: ["leaders1", "leaders1"]
    },
    {
      name: "churchName2",
      femaleStudents: ["student2", "student2"],
      femaleLeaders: ["leaders2", "leaders2"],
      maleStudents: ["student2", "student2"],
      maleLeaders: ["leaders2", "leaders2"]
    },
    {
      name: "churchName3",
      femaleStudents: ["student3", "student3"],
      femaleLeaders: ["leaders3", "leaders3"],
      maleStudents: ["student3", "student3"],
      maleLeaders: ["leaders3", "leaders3"]
    }
  ];
  componentDidMount() {
    // create buttons array
    let buttons = [];
    for (let item of this.arr) {
      let buttonObj = { id: item.name, isHidden: true };
      buttons.push(buttonObj);
    }
    this.setState({ buttons });
  }
  createContentParagraphTextForArray = (para1, para2) => {
    return (
      <div>
        {para1}&nbsp; {para2}
      </div>
    );
  };
  createButtonsForChurches = arr =>
    arr.map((item, index) => {
      let isHidden =
        this.state.buttons.length > 0
          ? this.state.buttons[index].isHidden
          : true;
      return (
        <div key={item.name}>
          <button
            onClick={() => this.clickHandler(item.name)}
            className="churchButton"
          >
            {item.name}
          </button>
          {!isHidden && (
            <div className="allContent">
              <div className="individualContent">
                {item.femaleStudents[0] &&
                  this.createContentParagraphTextForArray(
                    item.femaleStudents,
                    "Female Students"
                  )}
              </div>
              <div className="individualContent">
                {item.femaleLeaders[0] &&
                  this.createContentParagraphTextForArray(
                    item.femaleLeaders,
                    "Female Leaders"
                  )}
              </div>
              <div className="individualContent">
                {item.maleStudents[0] &&
                  this.createContentParagraphTextForArray(
                    item.maleStudents,
                    "Male Students"
                  )}
              </div>
              <div className="individualContent">
                {item.maleLeaders[0] &&
                  this.createContentParagraphTextForArray(
                    item.maleLeaders,
                    "Male Leaders"
                  )}
              </div>
            </div>
          )}
        </div>
      );
    });
  clickHandler = buttonId => {
    let buttons = this.state.buttons;
    buttons.forEach(button => {
      if (button.id === buttonId) {
        button.isHidden = !button.isHidden;
      }
    });
    this.setState({ buttons });
  };
  render() {
    return (
      <React.Fragment>{this.createButtonsForChurches(this.arr)}</React.Fragment>
    );
  }
}

ReactDOM.render(<App/>,  document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='root'/>

Upvotes: 1

Related Questions