Вспоминаем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 автоматически создает три состояния для обработки вашего экшена:
pending— действие запущено, данные загружаются.fulfilled— запрос успешно выполнен.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, а затем отобразить его пользователю.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ