JavaRush /Курсы /Модуль 3: React /Работа с useSelector и useDispatch для доступа к данным

Работа с useSelector и useDispatch для доступа к данным

Модуль 3: React
6 уровень , 4 лекция
Открыта

Основы использования useSelector

Итак, представьте, что у нас есть огромный холодильник с данными (Redux Store). Если мы хотим что-то из него достать, мы используем useSelector. Это наш пропуск к состоянию, хранящемуся в этом холодильнике.

Как работает useSelector?

useSelector — это хук, который позволяет извлекать данные из Redux Store в любом компоненте React. Мы передаём в него функцию-селектор, которая выбирает часть состояния, нужную компоненту.

Вот пример:

import { useSelector } from 'react-redux';
import { RootState } from './store';

const MyComponent = () => {
  // Извлекаем часть состояния из Store
  const count = useSelector((state: RootState) => state.counter.value);

  return (
    <div>
      <p>Текущее значение: {count}</p>
    </div>
  );
};

Здесь:

  • state: RootState — это весь Store, типизированный вашим интерфейсом. Мы заранее описали структуру в предыдущих лекциях.
  • (state: RootState) => state.counter.value — селектор, который лезет в хранилище и достаёт значение value из среза counter.

Почему useSelector важен?

  1. Минимизация ререндера. Компонент перерисовывается только при изменении данных, которые он выбирает.
  2. Локальное управление состоянием. Вы используете только те данные, которые нужны этому компоненту.

Типизация useSelector

TypeScript делает нас не только счастливыми, но и защищёнными от кучи глупых ошибок. Чтобы типизация в useSelector работала как часы, мы заранее описываем структуру состояния с помощью интерфейсов.

// Интерфейс для состояния
interface CounterState {
  value: number;
}

// Интеграция состояния с Slice
const initialState: CounterState = {
  value: 0,
};

const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    increment: (state) => {
      state.value += 1;
    },
    decrement: (state) => {
      state.value -= 1;
    },
  },
});

export const { increment, decrement } = counterSlice.actions;

// Типизация RootState
export interface RootState {
  counter: CounterState;
}

Теперь при использовании useSelector мы передаём RootState как тип для всего состояния, что обеспечит автодополнение и проверку типов на этапе написания кода.

Основы использования useDispatch

Если useSelector — это наш холодильник, то useDispatch — это кнопка вызова доставки продуктов к вам на стол. С его помощью мы отправляем действия (actions) в Store, чтобы изменить состояние.

Как работает useDispatch?

useDispatch возвращает функцию, которая позволяет отправлять действия на обработку редьюсером. Вот пример:

import { useDispatch } from 'react-redux';
import { increment, decrement } from './counterSlice';

const MyComponent = () => {
  const dispatch = useDispatch();

  return (
    <div>
      <button onClick={() => dispatch(increment())}>Увеличить</button>
      <button onClick={() => dispatch(decrement())}>Уменьшить</button>
    </div>
  );
};

Как видите, мы просто вызываем dispatch, передавая ему действие, которое заранее определено в нашем срезе.

Типизация useDispatch

Да, друзья, TypeScript здесь тоже приходит на помощь. Чтобы наш dispatch знал, какие действия он может принимать, нам нужно немного магии типизации.

Пример типизации:

import { AppDispatch } from './store';
import { useDispatch } from 'react-redux';

// Создаем свою версию useDispatch с типами
export const useTypedDispatch = () => useDispatch<AppDispatch>();

Теперь вы можете использовать свой типизированный хук useTypedDispatch вместо стандартного useDispatch.

Советы и рекомендации: избегаем лишних рендеров

Одной из типичных проблем при работе с useSelector является то, что компоненты могут перерисовываться чаще, чем нужно. Это происходит, если селектор возвращает ссылочные типы (например, массивы или объекты), которые создаются заново при каждом обновлении.

Пример проблемы:

const MyComponent = () => {
  const items = useSelector((state: RootState) => state.items); // Вернётся новый объект каждый раз!

  return (
    <ul>
      {items.map((item) => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
};

Чтобы минимизировать ошибки, вы можете использовать библиотеку reselect для создания мемоизированных селекторов. Это позволяет избежать повторного создания массивов и объектов.

Практический пример: приложение счетчика

Давайте напишем простое приложение, которое использует useSelector и useDispatch.

Компонент Counter:

import React from 'react';
import { useSelector } from 'react-redux';
import { useTypedDispatch } from './store';
import { increment, decrement } from './counterSlice';

export const Counter = () => {
  const count = useSelector((state: RootState) => state.counter.value);
  const dispatch = useTypedDispatch();

  return (
    <div>
      <h1>Счётчик: {count}</h1>
      <button onClick={() => dispatch(increment())}>+</button>
      <button onClick={() => dispatch(decrement())}>-</button>
    </div>
  );
};

Обратите внимание, как лаконично выглядит код с типизацией. Никаких магических строк, всё строго по интерфейсам.

Тестирование в консоли

Запустите приложение и попробуйте нажимать кнопки. Проверьте, как изменяется состояние в Redux DevTools (если вы подключили его раньше). Если всё сделано правильно, вы должны увидеть обновление состояния при каждом нажатии!

Полезные советы:

  • Старайтесь извлекать только те данные, которые необходимы для конкретного компонента.
  • Используйте типизацию для селекторов и dispatch, чтобы избежать непредсказуемого поведения.
  • Помните про оптимизацию рендеров, особенно при работе с большими приложениями и множеством компонентов.

Поздравляю! Теперь вы умеете извлекать данные из Store и отправлять действия для их изменения.

1
Задача
Модуль 3: React, 6 уровень, 4 лекция
Недоступна
Извлечение имени пользователя с useSelector
Извлечение имени пользователя с useSelector
1
Задача
Модуль 3: React, 6 уровень, 4 лекция
Недоступна
Управление состоянием счётчика с useDispatch
Управление состоянием счётчика с useDispatch
3
Опрос
Управление глобальным состоянием, 6 уровень, 4 лекция
Недоступен
Управление глобальным состоянием
Управление глобальным состоянием
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ