networkcore
networkcore

Reputation: 145

ReactJs Accordion Automatic Close Mechanism

I'm currently working on an accordion component in react version 16.3.2, that receives props from another component and displays the accordion accordingly. Unfortunately I cannot use hooks.

This works pretty smoothly, what I want to implement now is a way so that only one accordion can be opened at the same time on the same page. So, once you open one accordion (while another is already opened) it should automatically close the already opened one.

I have an Id (string which describes the current section, e.g 'contact', 'info', etc.) and the state of an accordion gets saved in the state (set to true when you toggle the accordion). I'm not quite sure on how I could implement this mechanism and am looking for tips on how I could solve this in a smart way. Any pointers?

example: https://codesandbox.io/s/reactjs-accordion-automatic-close-mechanism-6dys1 (I didn't add all of the styling, animations since this is more about functionality)

Upvotes: 1

Views: 7173

Answers (2)

Matt Sugden
Matt Sugden

Reputation: 874

You could do something like this, using the state hook in the App component

export default function App() {
  const items = [
    { id: 1, title: 'First Accordion', content: 'Hello' },
    { id: 2, title: 'Click me', content: 'Hello 2' },
    { id: 3, title: 'Third Accordion Accordion', content: 'Hello 3' },
  ]

  const [selectedItem, setSelectedItem] = useState(1)

  const handleClick = id => {
    setSelectedItem(id)
  }
  return (
    <div className="App">
      {items.map(x => {
        return (
          <Accordion 
            key={x.id} 
            id={x.id}
            title={x.title} 
            open={x.id === selectedItem}
           onClick={handleClick}
          >
            <p>{x.title}</p>
          </Accordion>
        )
      })}
    </div>
  );
}

Then your accordion component is a bit simpler

class Accordion extends React.Component {
  accToggle() {
    this.props.onClick(this.props.id);
  }

  sectionClasses() {
    let classes = "accordion";
    classes += this.props.open ? " sec-on" : "";
    classes += "sec-underway";

    return classes.trim();
  }

  render() {
    return (
      <section className={this.sectionClasses()} id={this.props.id}>
        <div className="acc-title" onClick={this.accToggle.bind(this)}>
          <h3 className="acc-text">{this.props.title}</h3>
          <div className="acc-nav">
            <span className="acc-toggle" />
          </div>
        </div>

        <div className="acc-content">{this.props.children}</div>
      </section>
    );
  }
}

Accordion.defaultProps = {
  open: false
};

Accordion.propTypes = {
  id: PropTypes.number.isRequired,
  children: PropTypes.any,
  onFocus: PropTypes.func,
  progress: PropTypes.oneOfType([
    PropTypes.number,
    PropTypes.string,
    PropTypes.bool
  ]),
  title: PropTypes.string,
  open: PropTypes.bool
};

export default Accordion;

The accordion calls a function on the app component, which updates the state and the display logic is passed down in the props

Upvotes: 3

Lazar Nikolic
Lazar Nikolic

Reputation: 4394

You can find solution for your problem in the following codesandbox https://codesandbox.io/s/reactjs-accordion-automatic-close-mechanism-yejc0

Change prop names as it fits your code base, but the logic is solid

Upvotes: 1

Related Questions