reactjs - How do I set state of sibling components easily in React? -
i have got beginnings of clickable list component serve drive select element. can see below, onclick of listitem, i'm passing state of child element (listitem in case) parents (selectablelist, , customselect component). working fine. however, change state of sibling components (the other listitems) can toggle selected states when 1 of listitems clicked.
at moment, i'm using document.queryselectorall('ul.cs-select li) grab elements , change class selected when doesn't match index of clicked listitem. works - extent. however, after few clicks, state of component has not been updated react (only client side js), , things start break down. change this.state.isselected of sibling list items, , use state refresh selectablelist component. offer better alternative i've written below?
var react = require('react'); var selectbox = require('./select-box'); var listitem = react.createclass({ getinitialstate: function() { return { isselected: false }; }, toggleselected: function () { if (this.state.isselected == true) { this.setstate({ isselected: false }) } else { this.setstate({ isselected: true }) } }, handleclick: function(listitem) { this.toggleselected(); this.props.onlistitemchange(listitem.props.value); var unboundforeach = array.prototype.foreach, foreach = function.prototype.call.bind(unboundforeach); foreach(document.queryselectorall('ul.cs-select li'), function (el) { // below trying // make sure when user clicks on list // item in selectablelist, *other* // list items class="selected" removed. // works first time move through // list clicking other items, then, on second // pass through, starts fail, requiring *two clicks* before // list item selected again. // maybe there's better more "reactive" method of doing this? if (el.dataset.index != listitem.props.index && el.classlist.contains('selected') ) { el.classlist.remove('selected'); } }); }, render: function() { return ( <li ref={"listsel"+this.props.key} data-value={this.props.value} data-index={this.props.index} classname={this.state.isselected == true ? 'selected' : '' } onclick={this.handleclick.bind(null, this)}> {this.props.content} </li> ); } }); var selectablelist = react.createclass({ render: function() { var listitems = this.props.options.map(function(opt, index) { return <listitem key={index} index={index} value={opt.value} content={opt.label} onlistitemchange={this.props.onlistitemchange.bind(null, index)} />; }, this); return <ul classname="cs-select">{ listitems }</ul>; } }) var customselect = react.createclass({ getinitialstate: function () { return { selectedoption: '' } }, handlelistitemchange: function(listindex, listitem) { this.setstate({ selectedoption: listitem.props.value }) }, render: function () { var options = [{value:"one", label: "one"},{value:"two", label: "two"},{value:"three", label: "three"}]; return ( <div classname="group"> <div classname="cs-select"> <selectablelist options={options} onlistitemchange={this.handlelistitemchange} /> <selectbox classname="cs-select" initialvalue={this.state.selectedoption} fieldname="custom-select" options={options}/> </div> </div> ) } }) module.exports = customselect;
the parent component should pass callback children, , each child trigger callback when state changes. hold of state in parent, using single point of truth, , pass "selected" value down each child prop.
in case, child this:
var child = react.createclass({ ontoggle: function() { this.props.ontoggle(this.props.id, !this.props.selected); }, render: function() { return <button onclick={this.ontoggle}>toggle {this.props.label} - {this.props.selected ? 'selected!' : ''}!</button>; } }); it has no state, fires ontoggle callback when clicked. parent this:
var parent = react.createclass({ getinitialstate: function() { return { selections: [] }; }, onchildtoggle: function(id, selected) { var selections = this.state.selections; selections[id] = selected; this.setstate({ selections: selections }); }, buildchildren: function(dataitem) { return <child id={dataitem.id} label={dataitem.label} selected={this.state.selections[dataitem.id]} ontoggle={this.onchildtoggle} /> }, render: function() { return <div>{this.props.data.map(this.buildchildren)}</div> } }); it holds array of selections in state , when handles callback child, uses setstate re-render children passing state down in selected prop each child.
you can see working example of here:
Comments
Post a Comment