Highlighting one object at a time with React Highlighting one object at a time with React reactjs reactjs

Highlighting one object at a time with React


Great start! Let's walk through some problems in your code first.

Separation of concerns

There's no need to create your entire nested structure inside your top-most component. This is very contrived:

for (i=0; i < this.props.menuitems.length; i++) {  if(this.props.menuitems[i].section !== lastSection) {    var section = this.props.menuitems[i].section;    var items = [];    for (j=0; j < this.props.menuitems.length; j++) {      if(this.props.menuitems[j].section == section) {        var itemName = this.props.menuitems[j].name;        items.push(<SectionItem title={itemName} key={itemName} />);      };    }    sections.push(<Section title={section} items={items} key={section} />);    lastSection = section;  }}

Quite on the contrary. You should try and make each component responsible for the rendering of their own piece of information. We could improve this if we first treated your data. The problem is that your sections are not nested. What if, instead of this...

var MENU_ITEMS = [  {section: "About", name: "Hey", key: "Hey", selected: true},  {section: "About", name: "No", key: "No", selected: false},  {section: "About", name: "Way", key: "Way", selected: false},  {section: "People", name: "Cakewalk", key: "Cakewalk", selected: false},  {section: "People", name: "George", key: "George", selected: false},  {section: "People", name: "Adam", key: "Adam", selected: false},  {section: "Projects", name: "Pirate raid", key: "Pirate raid", selected: false},  {section: "Projects", name: "Goosehunt", key: "Goosehunt", selected: false},];

We had this:

var sections = [  {    name: "About",     items: [      {name: "Hey", key: "Hey", selected: true},      {name: "No", key: "No", selected: false},      {name: "Way", key: "Way", selected: false}      ]  },{    name: "People",     items: [      {name: "Cakewalk", key: "Cakewalk", selected: false},      {name: "George", key: "George", selected: false},      {name: "Adam", key: "Adam", selected: false}    ]  },{    name: "Projects",     items: [      {name: "Pirate raid", key: "Pirate raid", selected: false},      {name: "Goosehunt", key: "Goosehunt", selected: false}    ]  }];

Then we could simplify Accordion quite a bunch. We simply render one Section for each section:

var Accordion = React.createClass({    render: function() {    return (      <div className="main">        {this.props.sections.map(function(section){          return <Section key={section.name} section={section}/>        })}      </div>    );  }});

Likewise, Section and SectionItem become quite simpler.

var Section = React.createClass({  handleClick: function(){    this.setState({      open: !this.state.open,      class: this.state.open ? "section" : "section open"    });  },  getInitialState: function(){     return {       open: false,       class: "section"     }  },  render: function() {    return (      <div className={this.state.class}>        <div className="sectionhead" onClick={this.handleClick}>{this.props.section.name}</div>        <div className="articlewrap">          <div className="article">            {this.props.section.items.map(function(item){              return <SectionItem key={item.name} item={item}/>            })}          </div>        </div>      </div>    );  }});var SectionItem = React.createClass({  handleClick: function(){    this.setState({      currentItem: this,      active: !this.state.active,      class: this.state.active ? "sectionitem" : "sectionitem active"    });  },  getInitialState: function(){     return {       active: false,       class: "sectionitem"     }  },  render: function() {    return (        <div className={this.state.class} onClick={this.handleClick}>{this.props.item.name}</div>     );  }});

Propagating state changes

Now, to your original question. In a more complex application, you could benefit from something more robust like Flux. However, for now, following the techniques exposed in Thinking in React should solve your problem.

Indeed, one good way is to bring your state of "what is open" to the Accordion component. You simply need to let your parent know that something is being clicked. We can do that through a callback passed as a prop.

So, Accordion could have an openSection state, and an onChildClick that receives the clicked section's name. It needs to pass onChildClick to each Section.

var Accordion = React.createClass({  getInitialState: function() {    return {      openSection: null    };  },  onChildClick: function(sectionName) {    this.setState({      openSection: sectionName    });  },  render: function() {    return (      <div className="main">        {this.props.sections.map(function(section){          return <Section key={section.name}                   onChildClick={this.onChildClick}                  open={this.state.openSection===section.name}                   section={section}/>        }.bind(this))}      </div>    );  }});

And Section simply calls this function when clicked, passing in it's own name.

var Section = React.createClass({  handleClick: function(){    this.props.onChildClick(this.props.section.name);  },  render: function() {    var className = this.props.open ? "section open" : "section"    return (      <div className={className}>        <div className="sectionhead" onClick={this.handleClick}>{this.props.section.name}</div>        <div className="articlewrap">          <div className="article">            {this.props.section.items.map(function(item){              return <SectionItem key={item.name} item={item}/>            })}          </div>        </div>      </div>    );  }});

You can extrapolate this solution to the SectionItem problem.

The resulting codepen is here: http://codepen.io/gadr90/pen/wamQXG?editors=001

Good luck on learning React! You are on the right path.