React Hooks: компонент обновляет один шаг назад при обновлении состояния - PullRequest
4 голосов
/ 04 марта 2020

У меня есть массив объектов, которые должны быть отсортированы по определенному пользователем свойству (в раскрывающемся списке). Вот код:

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>
  );
}

Но проблема в том, что отсортированные данные повторно визуализируются на один шаг после изменения значения в раскрывающемся списке.

Что я делаю не так? Я предполагаю, что проблема заключается в сравнении объектов, поскольку я проверил, чтобы установить примитивное значение в переменной данных, и все работает отлично. Но не с объектом.

1 Ответ

6 голосов
/ 04 марта 2020

Проблема (несколько косвенная) в том, что вы напрямую изменяете данные состояния, что недопустимо в React . Вы вызываете sort для массива, который у вас есть в состоянии, который сортирует его вместо . Вам нужно скопировать его и отсортировать копию. Вы не видите обновления своевременно, потому что вы вызываете setData с тем же массивом, который у него уже был, поэтому он игнорирует вызов.

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

Live Пример:

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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...