React Hooks: component re-renders one step behind on state update

Kate Lupachova :

I have an array of objects, which should be sorted by the user's defined property (in dropdown). Here is the code:

import React, {useState, useEffect} from 'react';

const repos = [
  {
    name: 'repo1',
    forks_count: 5,
    stargazers_count: 3,
    updated_at: 12
  },
  {
    name: 'repo2',
    forks_count: 7,
    stargazers_count: 2,
    updated_at: 17
  },
  {
    name: 'repo3',
    forks_count: 8,
    stargazers_count: 11,
    updated_at: 5
  }
];

function App() {
  const[data, setData] = useState([]);
  const[sortType, setSortType] = useState('stars');

  useEffect(() => {
    const sortRepos = (type) => {
      const types = {
          stars: 'stargazers_count',
          forks: 'forks_count',
          upd: 'updated_at'
      };
      const sortProperty = types[type]; 
      const sorted = repos.sort((a,b) => b[sortProperty] - a[sortProperty]);         
      setData(sorted);
    }; 

    sortRepos(sortType);
    }, [sortType]);


  return (
    <div className="App">
      <select onChange={(e) => setSortType(e.target.value)}>
        <option value='stars'>Stars</option>
        <option value='forks'>Forks</option>
        <option value='upd'>Updated</option>
      </select>

      <div >
      {data.map(repo => (
            <div key={repo.name}>
              <div>Name {repo.name}</div>
              <div>Forks {repo.forks_count}</div>
              <div>Stars {repo.stargazers_count}</div>
              <div>Update {repo.updated_at}</div>
            </div>
        ))} 
      </div>
    </div>
  );
}

But the problem is that sorted data re-renders one step behind after changing the value in the dropdown.

What am I doing wrong? My guess is that the problem is in comparing objects, as I've tested to set primitive value in data variable and everything works fine. But not with an object.

T.J. Crowder :

The problem is (somewhat indirectly) that you're directly modifying state data, which is not allowed in React. You're calling sort on the array that you have in state, which sorts it in place. You need to copy it and sort the copy. You're not seeing updates in a timely manner because you're calling setData with the same array that it already had, so it's ignoring the call.

const sorted = [...repos].sort((a,b) => b[sortProperty] - a[sortProperty]);         
// −−−−−−−−−−−−^^^^^^^^^^

Live Example:

const {useState, useEffect} = React;

const repos = [
  {
    name: 'repo1',
    forks_count: 5,
    stargazers_count: 3,
    updated_at: 12
  },
  {
    name: 'repo2',
    forks_count: 7,
    stargazers_count: 2,
    updated_at: 17
  },
  {
    name: 'repo3',
    forks_count: 8,
    stargazers_count: 11,
    updated_at: 5
  }
];

function App() {
  const[data, setData] = useState([]);
  const[sortType, setSortType] = useState('stars');

  useEffect(() => {
    const sortRepos = (type) => {
      const types = {
          stars: 'stargazers_count',
          forks: 'forks_count',
          upd: 'updated_at'
      };
      const sortProperty = types[type]; 
      const sorted = [...repos].sort((a,b) => b[sortProperty] - a[sortProperty]);         
      setData(sorted);
    }; 

    sortRepos(sortType);
    }, [sortType]);


  return (
    <div className="App">
      <select onChange={(e) => setSortType(e.target.value)}>
        <option value='stars'>Stars</option>
        <option value='forks'>Forks</option>
        <option value='upd'>Updated</option>
      </select>

      <div >
      {data.map(repo => (
            <div key={repo.name}>
              <div>Name {repo.name}</div>
              <div>Forks {repo.forks_count}</div>
              <div>Stars {repo.stargazers_count}</div>
              <div>Update {repo.updated_at}</div>
            </div>
        ))} 
      </div>
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.11.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script>

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=26184&siteId=1