Search code examples
reactjsbuttonevent-handlingstatecollapsable

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.


Solution

  • 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'/>