Reputation: 1814
I have a Legend, which contains multiple Legend.Items children. I'm having a problem where currently onClick
it is possible to deselect all of the Legend Items which has consequences that I'd like to avoid. Is it possible to set some sort of onClick
handler in the Legend
component that can have some state clicked
and check whether there are n - 1
legend items "selected/faded", n
being the total number of legend items? I looked at the JSX Spread Attributes, but because I'm using {this.props.children}
, I'm not sure how to use them or if they would work in this context.
I also took a look at this blogpost (http://jaketrent.com/post/send-props-to-children-react/), but it looked a bit hacky to me and I thought there might be a more conventional way. I'm new to ReactJS so if I need to provide more context, let me know!
MY CODE:
LEGEND.JSX
var React = require('react');
var cn = require('classnames');
// Have legend hold state about how many clicked
var Legend = React.createClass({
getInitialState: function () {
return { clicked: 0 }
},
render: function () {
console.log(this.props.children);
return (
<ul className="legend inline-list">
{this.props.children}
</ul>
);
},
});
Legend.Item = React.createClass({
getInitialState: function () {
return { hover: false, clicked: false };
},
handleMouseOver: function () {
this.setState({ hover: true });
this.props.mouseOver(this.props.name);
},
handleMouseOut: function () {
this.setState({ hover: false });
this.props.mouseOut(this.props.name);
},
handleClick: function() {
if (this.state.clicked) {
this.setState({ clicked: false });
this.props.click(this.props.name);
} else {
this.setState({ clicked: true });
this.props.click(this.props.name);
};
},
render: function () {
var swatchClasses = cn({ 'swatch': true, 'legend-item-fade': this.state.hover, 'c3-legend-item-hidden': this.state.clicked })
var spanClasses = cn({ 'legend-item-fade': this.state.hover, 'c3-legend-item-hidden': this.state.clicked })
return (
<li className="legend-item">
<i className={swatchClasses}
onClick={this.handleClick}
onMouseEnter={this.handleMouseOver}
onMouseLeave={this.handleMouseOut}
style={{ "backgroundColor": this.props.color }}></i>
<span className={spanClasses}
onClick={this.handleClick}
onMouseEnter={this.handleMouseOver}
onMouseLeave={this.handleMouseOut}>
{this.props.name}
</span>
</li>
);
},
});
module.exports = {
Legend: Legend,
};
RESPONSE.JSX RENDER FUNCTION
<Legend>
{newColumns.map(function (column) {
return (
<Legend.Item name={column.name}
color={column.color}
click={this.onLegendClick}
mouseOut={this.onLegendMouseOut}
mouseOver={this.onLegendMouseOver}/>
);
}.bind(this))}
</Legend>
Upvotes: 1
Views: 869
Reputation: 4931
I think the best and simplest way is to use callbacks.
In Legend
recreate the components from the children, augmenting their props with a callback to Legend
:
let legendItems = React.Children.map(this.props.children, child =>
React.cloneElement(child, { updateLegendCounter: this.updateLegend})
);
The callback in Legend
is something like this:
updateLegend() {
this.setState({clicked: clicked + 1})
}
And finally, in your render method, you discriminate when
if (this.state.clicked === children.length-1)
Also, I would pass the initial state of clicked
as a prop to the Item
element. In this way it becomes really easy to select/deselect all.
Upvotes: 1