← Назад к вопросам

Как получить dispatch внутри асинхронной функции без redux-funk?

2.3 Middle🔥 171 комментариев
#React

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как получить 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. Она решает много проблем автоматически: кэширование, переподгрузка при потере соединения, синхронизация между вкладками.

Как получить dispatch внутри асинхронной функции без redux-funk? | PrepBro