javascript - Highlighting one object at a time with React -
i'm pretty new both react , javascript, , i've started learning building simple collapsible menu. works expected, except can't figure out how highlight 1 item @ time. suspect onclick method , state has owned higher level class sectionitem, i'm stuck in how make work. first instinct iterate on items in menu every time clicked, , make sure other items switched active=false.
is right way think this? explain how states work in react in context, , how should implement here?
the entire code available here: menu on codepen.io
this code item want make highlightable. i've not yet been able implement 1 item should highlighted @ once.
var sectionitem = react.createclass({ handleclick: function(){ if(!this.state.active) { this.setstate({ currentitem: this, active: true, class: "sectionitem active"}); } }, getinitialstate: function(){ return { active: false, class: "sectionitem" } }, render: function() { return ( <div classname={this.state.class} onclick={this.handleclick}>{this.props.title}</div> ); } });
great start! let's walk through problems in code first.
separation of concerns
there's no need create entire nested structure inside top-most component. contrived:
for (i=0; < this.props.menuitems.length; i++) { if(this.props.menuitems[i].section !== lastsection) { var section = this.props.menuitems[i].section; var items = []; (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 contrary. should try , make each component responsible rendering of own piece of information. improve if first treated data. problem sections not nested. 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 simplify accordion quite bunch. render 1 section 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 , 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, original question. in more complex application, benefit more robust flux. however, now, following techniques exposed in thinking in react should solve problem.
indeed, 1 way bring state of "what open" accordion component. need let parent know being clicked. can through callback passed prop.
so, accordion have opensection state, , onchildclick receives clicked section's name. needs pass onchildclick 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 calls 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 solution sectionitem problem.
the resulting codepen here: http://codepen.io/gadr90/pen/wamqxg?editors=001
good luck on learning react! on right path.
Comments
Post a Comment