I'm trying to learn React concepts, especially re: state and dynamic UIs, by coding a small sports roster-like UI. I've included the code below and the whole app + visual is at http://codepen.io/emkk/pen/dGYXJO. This app basically creates player cards from an array of player objects I've defined earlier.
I would like to implement sorting of the player cards upon button click. I've created a <Sort/>
component that renders the said buttons. I'd attach event listeners but don't know how to have that reflected in my <Roster/>
component. I have tried many different approaches with this.state
but can't seem to be get this to work. So please any help with implementing sorting or general advice would be much apprieciated!
class ProfileCard extends React.Component {
render() {
return (
<section className="profile-card">
<figure>
<img src={this.props.player.picURL} alt={this.props.player.Name}></img>
<article>
<ul>
<li>{this.props.player.Name}</li>
<li>{this.props.player.position}, #{this.props.player.number}</li>
<li>{this.props.player.Club}</li>
<li>{this.props.player.Height} ({this.props.player.Meters} m)</li>
<li>{this.props.player.Age} years old</li>
</ul>
</article>
</figure>
</section>
);
}
}
class Roster extends React.Component {
render() {
// Store sorted version of data
// Where I'd implement selected sorting
var sorted = this.props.players.sort();
/*
* Create player cards from JSON collection
*/
var cards = [];
sorted.forEach(function(player) {
if (player.year > 2000) {
cards.push(<ProfileCard player={player} />);
}
});
return (<div>{cards}</div>);
}
}
class Sort extends React.Component {
render() {
return (
<div className="sort-section">
<h1>Sort by</h1>
<div className="pill" id='age'>Age</div>
<div className="pill" id='Meters'>Height</div>
<div className="pill" id='Name'>Name</div>
</div>
)
}
}
class SortablePlayerTable extends React.Component {
render() {
/*
* Prefix some interestings stats
* before roster
*/
var ages = [], heights = [];
this.props.players.forEach(function(player) {
if (player.year > 2000) {
ages.push(player.Age);
heights.push(player.Meters);
}
});
var avgAge = (ages.reduce( (a, b) => a + b) / 12).toPrecision(3);
var avgHeights = (heights.reduce( (a, b) => a + b) / 12).toPrecision(3);
// Return page with stats data and Roster
return (
<div>
<h1>2012 Olympic Men's Basketball Team</h1>
<h2>Average Age: {avgAge} years old</h2>
<h2>Average Height: 6 ft 7 in ({avgHeights} m)</h2>
<Sort/>
<Roster
players={this.props.players}
/>
</div>
);
}
};
React.render(
<SortablePlayerTable players={PLAYERS} />,
document.getElementById('container')
);
Solution:
Another thing that tripped me up on the way to this was that I was losing access to this.setState
, kept getting a this.setState is a not a function
error. Using a ES6 arrow function to lexically bind this
for my handler function rescued me though.
class ProfileCard extends React.Component {
render() {
return (
<section className="profile-card">
<figure>
<img src={this.props.player.picURL} alt={this.props.player.Name}></img>
<article>
<ul>
<li>{this.props.player.Name}</li>
<li>{this.props.player.position}, #{this.props.player.number}</li>
<li>{this.props.player.Club}</li>
<li>{this.props.player.Height} ({this.props.player.Meters} m)</li>
<li>{this.props.player.Age} years old</li>
</ul>
</article>
</figure>
</section>
);
}
}
class Roster extends React.Component {
render() {
// Create player cards from sorted, dynamic JSON collection
var cards = [];
this.props.players.forEach(function(player) {
if (player.year > 2000) {
cards.push(<ProfileCard player={player} />);
}
});
return (<div>{cards}</div>);
}
}
class Sort extends React.Component {
sortRoster(field){
var players = this.props.players;
this.props.sortRosterStateBy(field, players);
}
render() {
return (
<div className="sort-section">
<h1>Sort by</h1>
<div className="pill" onClick={this.sortRoster.bind(this,'Age')} >Age</div>
<div className="pill" onClick={this.sortRoster.bind(this,'Meters')} >Height</div>
<div className="pill" onClick={this.sortRoster.bind(this,'Name')} >Name</div>
<div className="pill" onClick={this.sortRoster.bind(this,'position')} >Position</div>
<div className="pill" onClick={this.sortRoster.bind(this,'number')} >Number</div>
<div className="pill" onClick={this.sortRoster.bind(this,'Club')} >Club</div>
</div>
)
}
}
class SortablePlayerTable extends React.Component {
state = {
'players': this.props.players // default state
}
sortRosterStateBy = (field, players) => {
// Sorting ...
var sortedPlayers = players.sort( (a, b) => {
if (a[field] > b[field]) {
return 1;
}
if (a[field] < b[field]) {
return -1;
}
return 0;
});
// Then call setState
this.setState({'players': sortedPlayers});
}
render() {
// Prefix some interestings stats before roster
var ages = [], heights = [];
this.props.players.forEach(function(player) {
if (player.year > 2000) {
ages.push(player.Age);
heights.push(player.Meters);
}
});
var avgAge = (ages.reduce( (a, b) => a + b) / 12).toPrecision(3);
var avgHeight = (heights.reduce( (a, b) => a + b) / 12).toPrecision(3);
// Return page with stats data and Roster
return (
<div>
<h1>2012 Olympic Men's Basketball Team</h1>
<h2>Average Age: {avgAge} years old</h2>
<h2>Average Height: 6 ft 7 in ({avgHeight} m)</h2>
<Sort players={this.props.players} sortRosterStateBy={this.sortRosterStateBy}/>
<Roster players={this.state.players}/>
</div>
);
}
};
ReactDOM.render(
<SortablePlayerTable players={PLAYERS} />,
document.getElementById('container')
);