简体   繁体   中英

Change items in list in React when an item has been clicked

I'm quite new to ReactJS and I have trouble understand how different components can communicate with each other.

I do have a component that will render a list and each list item is a different component. I want to keep the components as small as possible.

Now, each list item can have a property named active and if the property is set to true, an additional class is added.

This is the class that defines a single item in the component.

See this below code for my component defining a single list item:

export default class OfficeRibbonTab extends React.Component {

  constructor(props) {
    super(props);

    this.state = {
      active: props.active ? props.active : false
    }

    // Assign all the correct event handlers.
    this.setActive = this.setActive.bind(this);
  }


  setActive() {
    this.setState({active: true});
  }

  render() {
    // When the tab is defined as active, add the "active" class on it.
    if (this.state.active)
    { var tabClassName = "active"; }

    return <li onClick={this.setActive} className={tabClassName}>{this.props.tabName}</li>;
  }
}

So, I have propery active which is passed to this component, which I store in the components state. When I click the list item, I set to state of the current item to be active. The problem is that I want all the other list items to become inactive, thus setting the state of active to false.

The code below is an overview of my list:

export default class OfficeRibbon extends React.Component {

  constructor(props) {
    // Call the 'base' constructor.
    super(props);
  }

  render() {
    var tabs = [];

    // Loop over all the tab elements and define how the element should be rendered.
    for (var i = 0; i < this.props.dataSource.tabs.length; i ++)
    {
      if (i == 1)
      { tabs.push(<OfficeRibbonTab active={true} key={this.props.dataSource.tabs[i].name} tabName={this.props.dataSource.tabs[i].name}></OfficeRibbonTab>); }
      else
      { tabs.push(<OfficeRibbonTab key={this.props.dataSource.tabs[i].name} tabName={this.props.dataSource.tabs[i].name}></OfficeRibbonTab>); }
    }

    return (<div>
      <div className="wrap-top">
        <OfficeRibbonTitle title={this.props.title}/>

        <ul className="tabs">
          {tabs}
        </ul>

      </div>

    </div>);
  }
}

It doesn't seem like rocket science, but I want to do it the React way without re-inventing the wheel.

Anyone who can guide me in the right direction?

Kind regards

It looks like OfficeRibbonTab manages its own state, which is fine, but it never informs its parent component of the state change. The most common approach would be to supply a callback function to each child component, so that it can then communicate back to the parent:

For example, OfficeRibbon will now contain a function handleTabSelect that gets passed down as a prop to each OfficeRibbonTab . And in OfficeRibbonTab , when a tab is clicked, you simply invoke the callback, and pass in the selected tab's index or id:

OfficeRibbonTab.jsx

 setActive(tabIndex) {
    this.props.handleTabSelect(tabIndex);
 }

OfficeRibbon.jsx

 handleTabSelect(tabIndex) {
    this.setState({activeIndex: tabIndex});
 }

Now in OfficeRibbon , you update your state to set the activeIndex or activeId , again either by the index or an identifier of your choosing. By setting state in OfficeRibbon , we necessarily force a render() of its children. As a result, we simply match the index of the iterator with the activeIndex of your state, when we iterate in render() :

<OfficeRibbonTab active={index === this.state.activeIndex} onClick={this.handleTabSelect} ... />

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM