Как обновить состояние компонента?react-9

В React существует несколько способов обновления состояния компонента в зависимости от его типа (классовый или функциональный) и версии React. Рассмотрим основные подходы.

1. В классовых компонентах

Для обновления состояния в классовых компонентах используется метод this.setState().

class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  increment = () => {
    // Обновление с новым значением
    this.setState({ count: this.state.count + 1 });

    // Или с функцией обновления (updater function), если новое состояние зависит от предыдущего
    this.setState(prevState => ({
      count: prevState.count + 1
    }));
  }

  render() {
    return <button onClick={this.increment}>{this.state.count}</button>;
  }
}

Важные нюансы:

  • setState является асинхронным
  • Для зависимых обновлений используйте функцию updater
  • Можно передать callback вторым аргументом: this.setState(updater, callback)

2. В функциональных компонентах

С появлением Hooks (хуков) в React 16.8, для управления состоянием используется хук useState.

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  const increment = () => {
    // Простое обновление
    setCount(count + 1);

    // Функциональное обновление, если новое состояние зависит от предыдущего
    setCount(prevCount => prevCount + 1);
  };

  return <button onClick={increment}>{count}</button>;
}

Особенности useState:

  • Возвращает массив с текущим состоянием и функцией для его обновления
  • При обновлении тем же значением (по сравнению через Object.is) ререндер не происходит
  • Можно использовать несколько вызовов useState для разных частей состояния

3. Использование useReducer

Для более комплексного состояния или когда следующее состояние зависит от предыдущего в сложной логике.

import { useReducer } from 'react';

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  return (
    <button onClick={() => dispatch({ type: 'increment' })}>
      {state.count}
    </button>
  );
}

4. Обновление состояния на основе пропсов

Иногда состояние нужно синхронизировать с пропсами (хотя это часто антипаттерн).

class EmailInput extends React.Component {
  state = { email: this.props.defaultEmail };

  componentDidUpdate(prevProps) {
    if (this.props.defaultEmail !== prevProps.defaultEmail) {
      this.setState({ email: this.props.defaultEmail });
    }
  }

  // ...
}

5. Оптимизации

Для предотвращения лишних ререндеров:

  • React.memo для мемоизации компонента
  • useMemo/useCallback для мемоизации значений и функций

Опасные подходы

  1. Прямое изменение состояния:
// НЕ ДЕЛАЙТЕ ТАК!
this.state.count = 1;
  1. Использование forceUpdate() (почти всегда плохая практика)

Резюмируем

  1. Классовые компоненты: this.setState()
  2. Функциональные компоненты: useState или useReducer
  3. Для сложной логики обновлений используйте функциональную форму setState/setCount
  4. Избегайте прямого изменения состояния и производных антипаттернов
  5. Для производительности используйте мемоизацию

Правильное обновление состояния - ключ к предсказуемому поведению React-приложений.