Используйте обратный вызов в setState при ссылке на предыдущее состояние в React - PullRequest
1 голос
/ 07 августа 2020

Я выполняю сортировку для таблицы, она должна сортировать столбцы в алфавитном порядке, в обратном алфавитном порядке и возвращаться к исходной форме при каждом вызове метода.

Вот мой код:

export default class GenericTable extends React.PureComponent {
  constructor(props) {
    super(props);

    this.state = {
      otherStudd: '',
      currentSort: 'default',
          rows: this.props.rows, // I receive the rows data on props        
       };
    }

  onSortChange = index => {
    const sortMap = {
      default: 'up',
      up: 'down',
      down: 'default',
    };

    const { currentSort, currentIndex } = this.state;

    const nextSort = currentIndex === index ? sortMap[currentSort] : 'default';

    const newRows = [...this.state.rows]; // line 40 - here is the error

    switch (nextSort) {
      case 'up':
        newRows.sort((a, b) => (a.cells[index] <= b.cells[index] ? -1 : 1));
        break;
      case 'down':
        newRows.sort((a, b) => (b.cells[index] <= a.cells[index] ? -1 : 1));
        break;
    }

    this.setState({
      rows: newRows,
      currentSort: nextSort,
      currentIndex: index,
    });
  };

...

}

Я думаю, что код выглядит правильно, но я получаю сообщение об ошибке es-lint:

 Line 40:25:  Use callback in setState when referencing the previous state  react/no-access-state-in-setstate

Это должна быть функция обратного вызова, но я не знаю, как заставить ее работать .

Есть идеи?

Ответы [ 3 ]

0 голосов
/ 07 августа 2020

Чао, вы можете попробовать такой подход:

orderRows = (data, index, nextSort) => {
    switch (nextSort) {
      case 'up':
        data.sort((a, b) => (a.cells[index] <= b.cells[index] ? -1 : 1));
        break;
      case 'down':
        data.sort((a, b) => (b.cells[index] <= a.cells[index] ? -1 : 1));
        break;
    }
    return data;
}

onSortChange = index => {
    const sortMap = {
      default: 'up',
      up: 'down',
      down: 'default',
    };

    const { currentSort, currentIndex } = this.state;

    const nextSort = currentIndex === index ? sortMap[currentSort] : 'default';

    this.setState((prevState) => ({
      rows: this.orderRows(prevState.rows, index, nextSort),
      currentSort: nextSort,
      currentIndex: index,
    }));
  };
0 голосов
/ 07 августа 2020

Назначение props для состояния и использование их из него - это анти-шаблон React (вы можете прочитать об этом здесь ).

Когда сценарий, которого вы пытаетесь достичь, требует что рекомендуемый способ - создать полностью управляемый компонент или, в данном случае, частично управляемый компонент, в котором не только rows используются из свойств, но и любое преобразование на них также выполняется через свойства (в данном случае , сортировка).

Приведенный ниже фрагмент демонстрирует упрощенную версию рассматриваемой проблемы. Мы создаем компонент Parent, который хранит rows в своем состоянии, а также предоставляет метод, выполняющий сортировку. Затем он отображает полностью управляемый компонент Child, которому не нужно иметь собственное состояние, и вместо этого получает строки, а также свою функцию сортировщика в качестве свойств.

Метод sortRows показывает, как использовать setState с обратным вызовом, который предлагает преимущество получения более согласованных результатов для состояния по сравнению с предоставлением вместо этого объекта состояния.

class Child extends React.PureComponent {
    render() {
        const { rows, sortRows } = this.props;
        return (
            <div>
                <ul>{ rows.map((row, i) => (<li key={ i }>{ row }</li>)) }</ul>
                <button onClick={ () => { sortRows(true) }}>Sort Ascending</button>
                <button onClick={ () => { sortRows(false) }}>Sort Descending</button>
            </div>
        );
    }
}
class Parent extends React.PureComponent {
    constructor(props) {
        super(props);
        this.state = {
            rows: ['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter']
        };
    }

    sortRows = (isSortAscending) => {
        this.setState((prevState) => ({
            rows: prevState.rows.slice().sort((a, b) => {
                if (a < b) return -(isSortAscending ? 1 : -1);
                if (a > b) return (isSortAscending ? 1 : -1);
                return 0;
            })
        }));
    }

    render() {
        return <Child rows={ this.state.rows } sortRows={ this.sortRows } />;
    }
}
ReactDOM.render(<Parent />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
0 голосов
/ 07 августа 2020

Вам необходимо использовать setState с функцией обратного вызова, которая выполняет обработку данных и устанавливает для вас новое состояние.

Вот преобразованная версия для вашего случая:

  onSortChange = index => {
     const sortMap = {
      default: 'up',
      up: 'down',
      down: 'default',
  };

  this.setState(({ rows, currentIndex, currentSort }) => {

    const nextSort = currentIndex === index ? sortMap[currentSort] : 'default';

     const newRows = [...rows];

      switch (nextSort) {
       case 'up':
       newRows.sort((a, b) => (a.cells[index] <= b.cells[index] ? -1 : 1));
       break;
      case 'down':
      newRows.sort((a, b) => (b.cells[index] <= a.cells[index] ? -1 : 1));
      break;
      default: break;
      }

      return {
        rows: newRows,
        currentSort: nextSort,
        currentIndex: index,
      };
  });

  }
...