Reputation: 332
I just started using react.js and i love the way components work. I created a Card
component, which will add a css-class expanded
on click. This works like a charm. Now I want all the other cards to loose there expanded
property once another card is clicked. In jQuery i'd use siblings
to do this.
What is the proper solution with react.js?
Here's my code:
var Card = React.createClass({
getInitialState: function () {
return {
expanded: false,
//clickCount: 0
};
},
handleClick: function (event) {
this.setProps({expanded: !this.props.expanded});
},
render: function () {
var cx = React.addons.classSet;
var classes = cx({
'card': true,
'expanded': this.state.expanded
});
return (
<li className={classes} onClick={this.handleClick}>
<h1>{this.props.name}</h1>
<h2>{this.props.entf.toFixed(2)} km</h2>
</li>
)
}
});
var App = React.createClass({
handleClick: function (event) {
console.log("clicked");
},
render: function () {
var pools = this.props.data.map(function (pool) {
return (
<Card onClick={this.handleClick} name={pool.name} entf={pool.entf} />
);
});
return (<ul>
{pools}
</ul>
)
}
});
Upvotes: 1
Views: 1209
Reputation: 283
Isn't React super?
To get the behaviour you want without too much modification, I suggest passing a callback onClick
or onExpand
to each <Card/>
. When a Card is activated, it runs the callback which can set a variable (perhaps activeCard
) in App's state. In App's render method, modify the map function to give each Card an expanded
boolean prop, which will be true depending on if the card's name/id matches activeCard
. Use the new expanded
prop instead of this.state.expanded
in each Card's logic.
var Card = React.createClass({
render: function () {
var cx = React.addons.classSet;
var classes = cx({
'card': true,
'expanded': this.props.expanded
});
return (
<li className={classes} onClick={this.props.onClick}>
<h1>{this.props.name}</h1>
<h2>{this.props.entf.toFixed(2)} km</h2>
</li>
)
}
});
var App = React.createClass({
getInitialState: function () {
return { activeCard: '' }
},
render: function () {
var pools = this.props.data.map(function (pool) {
return (
<Card onClick={this.handleClick.bind(this,pool.name)} name={pool.name} entf={pool.entf} expanded={pool.name == this.state.activeCard} />
);
}.bind(this));
return (<ul>
{pools}
</ul>
)
},
handleClick: function (value) {
this.setState({ activeCard: value })
}
});
When working in React, think about how to keep components as stateless as possible. In paradigms like jQuery these pieces often manage themselves, but if you want to use React's potential then always keep an eye out for how to 'trickle' down state.
Upvotes: 0
Reputation: 10629
Instead of using siblings
, you should keep an identifier to the expanded card in the App
component's state
and let React re-render if the selected card changes.
Here is an example how you would do it with react:
JSfiddle: http://jsfiddle.net/vdw27xpf/
var Card = React.createClass({
render: function () {
var cx = React.addons.classSet;
var classes = cx({
'card': true,
'expanded': this.props.expanded
});
return (
<li className={classes} onClick={this.props.onClick}>
<h1>{this.props.name}</h1>
<h2>{this.props.entf.toFixed(2)} km</h2>
</li>
)
}
});
var App = React.createClass({
getInitialState : function(){
return { expandedCardName : null };
},
handleClick: function (value) {
this.setState({ expandedCardName : value });
},
render: function () {
var pools = this.props.data.map(function (pool) {
return (
<Card key={pool.name}
expanded = { pool.name == this.state.expandedCardName }
onClick={this.handleClick.bind(this,pool.name)}
name={pool.name} entf={pool.entf} />
);
}.bind(this));
return (<ul>
{pools}
</ul>
)
}
});
var data = [ { name:"card1", entf:60 },{ name:"card2", entf:50 } ];
React.render(<App data={data} />, document.body);
Upvotes: 2