0

I want to have client side sorting that enables the user to sort by different keys. The keys are integers only.

Right now I am calling my sort function right before the map function:

.sort(function(a, b) {
        return b.sortKey- a.sortKey;
      }

When the user presses a button, the sortKey should change to a specified value. This is all included in a react component, not a function. I read that properties on a component should not be changed by the component itself, so how would i go about changing the sortKey on the button press?

My render function contains the two sorting options and the list:

render() {
    return (
      <div className="row">
        <div className="col s12">
          <button onClick={() => this.changeSortKey("yes")} style={{ marginRight: 5, marginTop: 5 }} className="btn">
            Yes Votes
          </button>
          <button onClick={() => this.changeSortKey("no")} style={{ marginTop: 5 }} className="btn">
            No Votes
          </button>
        </div>

        {this.renderSurveys()}
      </div>
    );
  }

The changeSortKey(key) function would ideally change the sortKey and then render the list anew, but I'm not sure how to render the list again, after the user has clicked.

If i just supply the sort function with a known key, the sorting works perfectly.

EDIT: Fetching the data from API

export const fetchSurveys = () => async dispatch => {
  const response = await axios.get("/api/surveys");
  dispatch({ type: FETCH_SURVEYS, payload: response.data });
};

EDIT: RenderSurveys helper function

renderSurveys() {
    //custom helper method
    return this.props.surveys
      .sort(function(a, b) {
        return b.yes - a.yes;
      })
      .map(survey => {
        const data = [
          { text: "Yes", value: survey.yes },
          { text: "No", value: survey.no }
        ];
        //reverse the array and then map over it, newest first
        return (
          <div className="col s6">
            <div key={survey._id} className="card darken-1">
              <div className="card-content">
                <span className="card-title">{survey.title}</span>
                <p>Sent On: {new Date(survey.dateSent).toLocaleDateString()}</p>
                <p>
                  Last responded on:{" "}
                  {new Date(survey.lastResponded).toLocaleDateString()}
                </p>
              </div>

              <div className="card-action">
                <a href="#">Yes: {survey.yes}</a>
                <a href="#">No: {survey.no}</a>
                <BarChart width={100} height={70} data={data} />
                <button
                  onClick={() => this.props.deleteSurvey(survey._id)}
                  className="btn-floating btn-large red right"
                >
                  <i className="material-icons">clear</i>
                </button>
              </div>
            </div>
          </div>
        );
      });
  }
Leth
  • 1,033
  • 3
  • 15
  • 40
  • can you be a little more specific on how you implement your keys? – EnriqueDev Oct 04 '17 at 08:45
  • I'm guessing you're not changing your state so react doesn't know it needs to update the UI. Check out [this question](https://stackoverflow.com/questions/30626030/can-you-force-a-react-component-to-rerender-without-calling-setstate) and maybe try `forceUpdate()`, or sort the values and store the result in your state. – Jason Goemaat Oct 04 '17 at 08:46
  • @EnriqueDev My sortKey hasn't been fully implemented yet. I am getting my keys in a JSON object from an action creator using redux. this.props.fetchSurveys(); – Leth Oct 04 '17 at 09:01
  • any improvements? – EnriqueDev Oct 04 '17 at 10:05
  • @EnriqueDev I stored my state on the lifecycle hook, but I'm unsure how I should call sortList() correctly. It says "," expected the array.sort() call. – Leth Oct 04 '17 at 10:29
  • If you copy pasted my code I just noticed there is a mistake in it, there was a parenthesis missing and a semicolon, I've just updated it. – EnriqueDev Oct 04 '17 at 11:07

2 Answers2

1

If you store the sorted version of your data in state, when you re-sorted and then set it to the state the component will be rendered with the new sorted array.

bennygenel
  • 23,896
  • 6
  • 65
  • 78
  • I am getting my data from an API on my backend, using an action creator with redux. When the component loads, the action to fetch the data is invoked and the state is mapped to the component properties. – Leth Oct 04 '17 at 09:07
  • Rather than explaining every step of your code please add more complete example of it. Without knowing how you render your list or how and where you implement sort it hard to give a good answer. – bennygenel Oct 04 '17 at 09:25
1

I would do as follows:

  1. First, on your componentWillMount method I would store your votes on state.

  2. Then, when your user clicks the button, store the votes value on a let variable and sort that variable.

  3. Finally, update your state with the new sorted array.

This way your component will re render every time you sort your array:

componentWillMount() {
  this.setState({ myArray: this.props.yourReduxStoredProp });
}

_sortList() => {
  // retrieve your array from state
  let myArray = this.state.myArray;

  // sort the array locally
  myArray.sort(function(a, b) {
    return b.sortKey- a.sortKey;
  });

  // update the state and, because of the change it state, the component re-renders
  this.setState({ myArray: myArray });
}

Remember that Redux becomes handy when you have to share data between many views, but if you are just showing data in a single view it is way more usefull to use the component state.

I would use redux to store de values on the long term and keep them stored in your app, but the state to manipulate the display of that data.

EnriqueDev
  • 1,207
  • 2
  • 12
  • 25
  • Thanks for your answer. What do you mean by storing the votes on state? How would i update the state after the sort? – Leth Oct 04 '17 at 09:12