Srachakr
Srachakr

Reputation: 35

How to show a block of collapsible text on click of button

I am trying to implement a collapsible component. I have designed it such as, on click of a button, a block of dynamic text will appear. I made a functional component and using the tags in a class. The name of the component is, CustomAccordion.jsx and using this component in Container.jsx

I have tried to create a button and a function for onClick event.

Part of the CustonAccordion.jsx

const handleToggle = () : string =>{
    let content = this.nextElementSibling;

    if (content.style.maxHeight){
        content.style.maxHeight = null;

    }else{
        content.style.maxHeight = content.scrollHeight +'px';
    }
}

export default function CustomAccordion(props: PropType): React.Component<*> {
    const { title, children } = props

    return(
        <div>
        <AccordionButton onClick={() => this.handleToggle()}>{title}</AccordionButton>
        <AccordionContent>
        <p>{children}
        </p>
        </AccordionContent>
        </div>
    )
}

Part of calling Container.jsx

<CustomAccordion title = {this.props.name}>
    <p>This is the text passed to component.</p>
</CustomAccordion> 
<br />

This does not show the expanded text and it seems that the click event does not work properly. I am very new in react, guessing the syntax might be incorrect.

Upvotes: 0

Views: 938

Answers (1)

Martin Kadlec
Martin Kadlec

Reputation: 4975

In react you should generally try to avoid touching DOM directly unless you really have to.

Also you are accessing the handleToggle function wrongly. It should be onClick={() => handleToggle()} because this in your case is window/null and so it has no handleToggle method.

Instead you can use a stateful class component to achieve the same thing.

export default class CustomAccordion extends React.Component {
  state = {show: false};
  toggle = () => this.setState({show: !this.state.show});
  render() {
    const {title, children} = this.props;
    const {show} = this.state;
    return (
      <div>
        <AccordionButton onClick={this.toggle}>{title}</AccordionButton>
        {show && (
          <AccordionContent>
            <p>{children}</p>
          </AccordionContent>
        )}
      </div>
    )
  }
}

If you want to have some kind of animation, you can set different className based on the show state instead of adding/removing the elements.

Upvotes: 1

Related Questions