Как получить dispatch внутри асинхронной функции без redux-funk?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Как получить dispatch внутри асинхронной функции без redux-thunk?
Альтернативы redux-thunk
В современной разработке React есть несколько способов получить доступ к dispatch внутри асинхронных операций без использования redux-thunk. Каждый подход имеет свои преимущества и применяется в зависимости от архитектуры проекта.
Redux Toolkit (современный стандарт) — встроенная поддержка async thunks. Хотя название похоже, это не тот же redux-thunk. RTK использует createAsyncThunk, которая автоматически генерирует pending/fulfilled/rejected экшены. Это чистер и удобнее, чем ручной redux-thunk.
React Context + useReducer — легковесная альтернатива для небольших приложений. Не требует external library, логика полностью в компоненте.
Custom Hooks — можно создать свой хук, который будет управлять асинхронной логикой без Redux.
React Query / TanStack Query — лучший выбор для работы с сервер-стейтом. Автоматически кэширует, переподгружает, обрабатывает ошибки.
// Способ 1: Redux Toolkit createAsyncThunk
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
// createAsyncThunk автоматически генерирует dispatch
export const fetchUser = createAsyncThunk(
"user/fetchUser",
async (userId, { rejectWithValue }) => {
try {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) throw new Error("Failed to fetch");
return await response.json();
} catch (error) {
return rejectWithValue(error.message);
}
}
);
const userSlice = createSlice({
name: "user",
initialState: { data: null, loading: false, error: null },
extraReducers: (builder) => {
builder
.addCase(fetchUser.pending, (state) => {
state.loading = true;
})
.addCase(fetchUser.fulfilled, (state, action) => {
state.loading = false;
state.data = action.payload;
})
.addCase(fetchUser.rejected, (state, action) => {
state.loading = false;
state.error = action.payload;
});
}
});
// В компоненте:
function UserProfile() {
const dispatch = useDispatch();
const user = useSelector(state => state.user);
useEffect(() => {
dispatch(fetchUser(123));
}, [dispatch]);
return <div>{user.data?.name}</div>;
}
// Способ 2: React Context + useReducer (без Redux)
const DataContext = React.createContext();
function DataProvider({ children }) {
const [state, dispatch] = useReducer(dataReducer, initialState);
const fetchData = useCallback(async (url) => {
dispatch({ type: "LOADING" });
try {
const response = await fetch(url);
const data = await response.json();
dispatch({ type: "SUCCESS", payload: data });
} catch (error) {
dispatch({ type: "ERROR", payload: error.message });
}
}, []);
return (
<DataContext.Provider value={{ state, fetchData }}>
{children}
</DataContext.Provider>
);
}
// Способ 3: Custom Hook для асинхронных операций
function useAsyncAction() {
const dispatch = useDispatch();
const executeAsync = useCallback(async (asyncFn) => {
dispatch({ type: "START_LOADING" });
try {
const result = await asyncFn();
dispatch({ type: "SET_DATA", payload: result });
return result;
} catch (error) {
dispatch({ type: "SET_ERROR", payload: error.message });
throw error;
}
}, [dispatch]);
return executeAsync;
}
// Использование:
function MyComponent() {
const executeAsync = useAsyncAction();
const handleLoad = async () => {
await executeAsync(async () => {
const response = await fetch("/api/data");
return response.json();
});
};
return <button onClick={handleLoad}>Load Data</button>;
}
// Способ 4: React Query (TanStack Query) - лучший для API
import { useQuery, useMutation } from "@tanstack/react-query";
function UserProfile() {
const { data, isLoading, error } = useQuery({
queryKey: ["user", 123],
queryFn: async () => {
const response = await fetch("/api/users/123");
if (!response.ok) throw new Error("Failed");
return response.json();
}
});
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <div>{data.name}</div>;
}
Лучшие практики
Если используешь Redux, выбирай Redux Toolkit с createAsyncThunk — это современный стандарт. Если стейт небольшой и простой, React Context + useReducer вполне достаточно.
Для работы с API и сервер-стейтом я бы рекомендовал React Query. Она решает много проблем автоматически: кэширование, переподгрузка при потере соединения, синхронизация между вкладками.