JavaRush /Курсы /Модуль 3: React /Создание асинхронных экшенов с createAsyncThunk — взаимод...

Создание асинхронных экшенов с createAsyncThunk — взаимодействие с API

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

ВспоминаемcreateAsyncThunk

Redux Toolkit предоставляет нам инструмент под названием createAsyncThunk, который значительно упрощает управление такими сценариями. createAsyncThunk — это удобный способ написать асинхронный код, сохраняя структуру Redux предельно ясной и организованной.

Преимущества createAsyncThunk

  • Легкость в использовании. Вы не пишете отдельные экшены или редьюсеры для каждого состояния (загрузка, успех, ошибка), createAsyncThunk делает это за вас.
  • Безопасность типов. Благодаря TypeScript вы точно знаете, какие данные ожидаются и возвращаются.
  • Отслеживание статусов. Redux Toolkit автоматически создает состояния pending, fulfilled и rejected для асинхронного действия.

Структура createAsyncThunk

Вот как выглядит createAsyncThunk:

import { createAsyncThunk } from '@reduxjs/toolkit';

export const fetchData = createAsyncThunk(
  'data/fetchData',
  async (arg: any, thunkAPI) => {
    try {
      const response = await fetch('https://api.example.com/data');
      if (!response.ok) throw new Error('Failed to fetch data');
      const data = await response.json();
      return data; // Успешный результат
    } catch (error) {
      return thunkAPI.rejectWithValue(error.message); // Обработка ошибок
    }
  }
);

Разберем по частям

  • Имя действия: 'data/fetchData' — строка, которая уникально идентифицирует ваше действие.
  • Асинхронная функция: вторая часть — это функция, которая выполняет вашу асинхронную логику.
    • arg: параметр, который вы можете передать при вызове действия.
    • thunkAPI: специальный объект, предоставляемый Redux Toolkit для поддержки обработки ошибок и получения состояния.

Ключевые состояния

createAsyncThunk автоматически создает три состояния для обработки вашего экшена:

  1. pending — действие запущено, данные загружаются.
  2. fulfilled — запрос успешно выполнен.
  3. rejected — произошла ошибка.

Эти состояния можно обрабатывать в редьюсере.

Пример: получение списка пользователей

Давайте создадим приложение, которое запрашивает список пользователей с сервера и отображает их. Начнем с создания среза.

Шаг 1: создание API-запроса с createAsyncThunk

В срезе создадим асинхронный экшен для запроса данных.

import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';

// Типы данных
interface User {
  id: number;
  name: string;
  email: string;
}

interface UsersState {
  users: User[];
  loading: boolean;
  error: string | null;
}

// Начальное состояние
const initialState: UsersState = {
  users: [],
  loading: false,
  error: null,
};

// Асинхронный экшен
export const fetchUsers = createAsyncThunk<User[]>(
  'users/fetchUsers',
  async (_, thunkAPI) => {
    try {
      const response = await fetch('https://jsonplaceholder.typicode.com/users');
      if (!response.ok) throw new Error('Failed to fetch users');
      return await response.json();
    } catch (error: any) {
      return thunkAPI.rejectWithValue(error.message);
    }
  }
);

// Срез
const usersSlice = createSlice({
  name: 'users',
  initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(fetchUsers.pending, (state) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(fetchUsers.fulfilled, (state, action) => {
        state.loading = false;
        state.users = action.payload;
      })
      .addCase(fetchUsers.rejected, (state, action) => {
        state.loading = false;
        state.error = action.payload as string;
      });
  },
});

export default usersSlice.reducer;

Шаг 2: подключение Redux к приложению

Добавим fetchUsers в наш store и настроим Provider.

store.ts:

import { configureStore } from '@reduxjs/toolkit';
import usersReducer from './usersSlice';

const store = configureStore({
  reducer: {
    users: usersReducer,
  },
});

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

export default store;

index.tsx:

import React from 'react';
import { Provider } from 'react-redux';
import { NavigationContainer } from '@react-navigation/native';
import store from './store';
import App from './App';

export default function Main() {
  return (
    <Provider store={store}>
      <NavigationContainer>
        <App />
      </NavigationContainer>
    </Provider>
  );
}

Шаг 3: использование в компоненте

Теперь, когда всё настроено, напишем компонент, который будет отправлять запрос и отображать данные.

UsersList.tsx:

import React, { useEffect } from 'react';
import { View, Text, ActivityIndicator, FlatList, StyleSheet } from 'react-native';
import { useSelector, useDispatch } from 'react-redux';
import { RootState, AppDispatch } from './store';
import { fetchUsers } from './usersSlice';

const UsersList = () => {
  const dispatch = useDispatch<AppDispatch();
  const { users, loading, error } = useSelector((state: RootState) => state.users);

  useEffect(() => {
    dispatch(fetchUsers());
  }, [dispatch]);

  if (loading) {
    return <ActivityIndicator size="large" color="#0000ff" />;
  }

  if (error) {
    return <Text style={styles.error}>Error: {error}</Text>;
  }

  return (
    <FlatList
      data={users}
      keyExtractor={(item) => item.id.toString()}
      renderItem={({ item }) => (
        <View style={styles.item}>
          <Text style={styles.name}>{item.name}</Text>
          <Text style={styles.email}>{item.email}</Text>
        </View>
      )}
    />
  );
};

const styles = StyleSheet.create({
  error: {
    color: 'red',
    fontSize: 16,
    textAlign: 'center',
  },
  item: {
    padding: 16,
    borderBottomWidth: 1,
    borderBottomColor: '#ccc',
  },
  name: {
    fontSize: 18,
  },
  email: {
    fontSize: 14,
    color: 'gray',
  },
});

export default UsersList;

Шаг 4: вывод компонента в приложении

Добавим наш компонент UsersList на главный экран.

import React from 'react';
import { SafeAreaView } from 'react-native';
import UsersList from './UsersList';

const App = () => {
  return (
    <SafeAreaView>
      <UsersList />
    </SafeAreaView>
  );
};

export default App;

Разбор обработки ошибок

Если что-то пойдет не так (например, сервер недоступен), мы используем метод rejectWithValue в createAsyncThunk. Это позволяет нам передавать сообщение об ошибке в состояние error, а затем отобразить его пользователю.

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