JavaRush /Курсы /Модуль 3: React /Использование useSelector и useDispatch для управления со...

Использование useSelector и useDispatch для управления состоянием

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

Вспоминаем как использовать useSelector

Рассмотрим простой "срез" состояния, который управляет списком задач в приложении:

// features/todoSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';

interface Todo {
  id: number;
  text: string;
  completed: boolean;
}

interface TodoState {
  todos: Todo[];
}

const initialState: TodoState = {
  todos: [],
};

const todoSlice = createSlice({
  name: 'todo',
  initialState,
  reducers: {
    addTodo: (state, action: PayloadAction<string>) => {
      state.todos.push({
        id: Date.now(),
        text: action.payload,
        completed: false,
      });
    },
    toggleTodo: (state, action: PayloadAction<number>) => {
      const todo = state.todos.find((t) => t.id === action.payload);
      if (todo) {
        todo.completed = !todo.completed;
      }
    },
  },
});

export const { addTodo, toggleTodo } = todoSlice.actions;

export default todoSlice.reducer;

Теперь подключим этот срез к нашему хранилищу:

// store.ts
import { configureStore } from '@reduxjs/toolkit';
import todoReducer from './features/todoSlice';

export const store = configureStore({
  reducer: {
    todo: todoReducer,
  },
});

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

Обратите внимание на тип RootState — позже мы будем использовать его для типизации useSelector.

Отлично. Теперь мы в компоненте получим список задач с помощью useSelector:

// TodoList.tsx
import React from 'react';
import { FlatList, Text, View } from 'react-native';
import { useSelector } from 'react-redux';
import { RootState } from './store';

const TodoList: React.FC = () => {
  const todos = useSelector((state: RootState) => state.todo.todos);

  return (
    <FlatList
      data={todos}
      keyExtractor={(item) => item.id.toString()}
      renderItem={({ item }) => (
        <View style={{ padding: 10 }}>
          <Text style={{ textDecorationLine: item.completed ? 'line-through' : 'none' }}>
            {item.text}
          </Text>
        </View>
      )}
    />
  );
};

export default TodoList;

Вот и все! Мы подключились к глобальному состоянию и отобразили список задач. Обратите внимание, как мы использовали RootState для типизации состояния: это позволяет TypeScript следить за тем, что мы извлекаем корректные данные.

Как использовать useDispatch для изменения состояния

Теперь, когда мы можем извлекать данные, пришло время научиться что-то менять. Для этого мы используем useDispatch.

Допустим, у нас есть компонент, который добавляет новые задачи в список. Вот как это может выглядеть:

// AddTodo.tsx
import React, { useState } from 'react';
import { Button, TextInput, View } from 'react-native';
import { useDispatch } from 'react-redux';
import { addTodo } from './features/todoSlice';

const AddTodo: React.FC = () => {
  const [text, setText] = useState('');
  const dispatch = useDispatch();

  const handleAddTodo = () => {
    if (text.trim() !== '') {
      dispatch(addTodo(text));
      setText(''); // Очищаем поле ввода
    }
  };

  return (
    <View style={{ flexDirection: 'row', padding: 10 }}>
      <TextInput
        style={{ flex: 1, borderColor: '#ccc', borderWidth: 1, marginRight: 10 }}
        value={text}
        onChangeText={setText}
        placeholder="Add a new todo"
      />
      <Button title="Add" onPress={handleAddTodo} />
    </View>
    );
};

export default AddTodo;
Обратите внимание:

мы используем здесь dispatch для отправки действия addTodo. Redux внутри обрабатывает это действие, изменяя состояние хранилища.

Пример полного приложения

Соединим все вместе в одном приложении — список задач, где вы можете добавлять и переключать задачи между выполненными и невыполненными:

// App.tsx
import React from 'react';
import { Provider } from 'react-redux';
import { store } from './store';
import AddTodo from './AddTodo';
import TodoList from './TodoList';
import { SafeAreaView } from 'react-native';

const App: React.FC = () => {
  return (
    <Provider store={store}>
      <SafeAreaView style={{ flex: 1 }}>
        <AddTodo />
        <TodoList />
      </SafeAreaView>
    </Provider>
  );
};

export default App;
Обратите внимание:

как мы используем компонент Provider, чтобы предоставить доступ к хранилищу всему приложению. Это обязательный шаг для работы с Redux.

Потенциальные ошибки и их решение

Ошибка 1: useSelector не видит данные

Если вы видите ошибку типа "Cannot read property 'todos' of undefined", вероятно, вы забыли подключить ваш редьюсер к хранилищу в configureStore.

Ошибка 2: неверная типизация

Если TypeScript жалуется на типы в useSelect или useDispatch, убедитесь, что вы экспортировали RootState и AppDispatch из вашего хранилища, а затем корректно их используете.

// Правильная типизация для useDispatch
import { useDispatch } from 'react-redux';
import { AppDispatch } from './store';

const dispatch: AppDispatch = useDispatch();

Ошибка 3: слишком частые перерендеры

Если ваш компонент рендерится слишком часто, попробуйте оптимизировать селекторы. Одна из стратегий — использовать библиотеку reselect, чтобы мемоизировать результаты вычислений.

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