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')
);
To sort an array of objects in React:Create a shallow copy of the array. Call the sort() method on the array passing it a function. The function is used to define the sort order.
The sort() method will sort elements based on the return value of the compare() function with the following rules: If compare(a,b) is less than zero, the sort() method sorts a to a lower index than b . If compare(a,b) is greater than zero, the sort() method sort b to a lower index than a , i.e., b will come first.
Each component in React has a lifecycle which you can monitor and manipulate during its three main phases. The three phases are: Mounting, Updating, and Unmounting.
Attach a function to each <div/>
in <Sort/>
on clicking which it calls a parent function this.props.sortBy()
class Sort extends React.Component {
sort(field){
this.props.sortBy(field);
}
render() {
return (
<div className="sort-section">
<h1>Sort by</h1>
<div className="pill" id='age' onClick=this.sort.bind(this,'age')>Age</div>
<div className="pill" id='Meters' onClick=this.sort.bind(this,'height')>Height</div>
<div className="pill" id='Name' onClick=this.sort.bind(this,'name')>Name</div>
</div>
)
}
}
Pass this parent function sortBy
as props
from your <SortablePlayerTable/>
component.
class SortablePlayerTable extends React.Component {
state = {
players: [] // default state
}
sortBy(field){
// Your sorting algorithm here
// it should push the sorted value by field in array called sortedPlayers
// Then call setState
this.setState({
players: sortedPlayers
});
}
render() {
// calculate stats
return (
<div>
{/* some JSX */}
<Sort sortBy={sortBy}/>
<Roster
players={this.state.players}
/>
</div>
);
}
};
Now the sorted array will be available to your <Roster/>
component as this.props.players
. Render the array directly without applying any sort inside <Roster/>
component.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With