Hans
Hans

Reputation: 137

React recursively call method on children

I'm making a collapsible list with React. So far it works but now I want to implement a button that expands/collapses everything. Therefore the button need to adjust the state of all elements. I'm not sure what's the best way to tackle this problem though. This is what I have:

import React, {Component} from 'react';

class CollapsibleList extends Component {

    constructor(props) {
        super(props);
        this.state = {
            collapsed: true
        };
        this.subLists = [];
        this.papers = [];
        if (this.props.subtitles) {
            for (let subList of this.props.subtitles) {
                this.subLists.push(
                    <CollapsibleList level={this.props.level + 1} subtitles={subList.subtitles} title={subList.title}/>
                );
            }
        }
        this.toggleCollapse = this.toggleCollapse.bind(this);
        this.expandAll = this.expandAll.bind(this);
        this.collapseAll = this.collapseAll.bind(this);
    }

    expandAll() {
        this.setState({collapsed: false});
        this.subLists.forEach(subList => subList.expandAll());
    }

    collapseAll() {
        this.setState({collapsed: true});
        this.subLists.forEach(subList => subList.collapseAll());
    }

    toggleCollapse() {
        this.setState(prevState => {
            return {collapsed: !prevState.collapsed};
        });
    }

    render() {
        return (this.state.collapsed ?
                <li className={'collapsibleListItem'}>
                    <div onClick={this.toggleCollapse}>
                        {this.props.title}
                    </div>
                    <img title={'Expand all'} className={'icon'} alt={'Expand all'} src={require('../expand_all.png')} onClick={this.expandAll}/>
                    <img title={'Collapse all'} className={'icon'} alt={'Collapse all'} src={require('../collapse_all.png')} onClick={this.collapseAll}/>
                </li> :
                <li className={'collapsibleListItem'}>
                    <div onClick={this.toggleCollapse}>
                        {this.props.title}
                    </div>
                    <img title={'Expand all'} className={'icon'} alt={'Expand all'} src={require('../expand_all.png')} onClick={this.expandAll}/>
                    <img title={'Collapse all'} className={'icon'} alt={'Collapse all'} src={require('../collapse_all.png')} onClick={this.collapseAll}/>
                    <ul className={'collapsibleList'}>
                        {this.subLists}
                    </ul>
                </li>
        );
    }
}

export default CollapsibleList;

Unfortunately, that doesn't seem to work though.

Upvotes: 0

Views: 705

Answers (1)

Arslan Tariq
Arslan Tariq

Reputation: 2528

I can't understand what you are trying to do in your code but you should have 2 different components; one for the list and one for the list item. It should be something like this:

// Parent component
import React from 'react';
import ListItem from './ListItem';

class List extends React.Component {
  constructor() {
    super();

    this.state = {
      collapsed: false
    }
  }

  render() {
    const data = ['abc', 'def', 'ghi']; // whatever you want to have

    return(
      <div>
        <button onClick={() => this.setState({collapsed: !this.state.collapsed})}>
          Collapse
        </button>
        <ul>
          {
            this.state.collapsed &&
            data.map((val, key) => {
              return(
                <li>
                  <ListItem value={val} key={key} />
                </li>
              )
            })
          }
        </ul>
      </div>
    )
  }
}

And this is the child component

// child component
import React from 'react';

class ListItem extends React.Component {
  constructor() {
    super();
  }

  render() {
    return(
      <div>
        {/*// render anything you want*/} 
        <p>{this.props.value}</p>
      </div>
    )
  }
}

export default ListItem;

This code is just to give you an insight.

Upvotes: 2

Related Questions