Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React.js - Implementing sorting of components

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')
);
like image 269
Korley Labs Avatar asked Dec 17 '15 17:12

Korley Labs


People also ask

How do you sort components in React?

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.

How do you sort a functional component in react JS?

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.

What are the three main methods of component lifecycle in React?

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.


1 Answers

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.

like image 126
Naisheel Verdhan Avatar answered Oct 03 '22 14:10

Naisheel Verdhan