我应该如何输入 React 的调度函数才能像 Promise 一样执行 "then" 方法

How should I type React's dispatch function to be able to do a "then" method like a Promise

我有一个简单的应用程序,它在第一次加载时分派一个操作来填充商店。我希望能够在 dispatch 上 运行 一个 then 方法,但是打字稿对此有抱怨。

(根据 redux 的文档,调度操作的 return 值是 return value of the action itself

Codesandbox

中可用的代码
// app.jsx
function App() {
  const dispatch = useAppDispatch();

  useEffect(() => {
    dispatch(getTodos()).then((todos) => console.log(todos));
    //                   ^^^^^
    //                   Property 'then' does not exist on type '{ type: "GET_TODOS"; payload: Promise<AxiosResponse<Todo[], any>>; }'.
  }, []);
  return <div className="App">Hello World!</div>;
}

商店配置

我使用 @reduxjs/toolkit 配置我的商店并为其设置了 redux-promise-middleware 以便在“完成”我基于承诺的操作时也会执行 <ACTION>_FULFILLED 操作已派遣。

// store.ts
import { configureStore } from '@reduxjs/toolkit';
import promiseMiddleware from 'redux-promise-middleware';
import rootReducer from './reducer';
import { useDispatch } from 'react-redux';

const store = configureStore({
    reducer: rootReducer,
    middleware: [promiseMiddleware],
});

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

export const useAppDispatch = () => useDispatch<AppDispatch>();

export default store;

减速器

// reducer.ts
import produce, { Draft } from "immer";
import { Action } from "./action";

export type Todo = {
    userId: number;
    id: number;
    title: string;
    completed: boolean;
}

interface State {
  todos: Todo[];
}
const initialState: State = {
  todos: []
};

const reducer = produce((draft: Draft<State>, action: Action) => {
  switch (action.type) {
    case "GET_TODOS_FULFILLED": {
      const todos = action.payload.data;
      return todos;
    }
  }
}, initialState);

export default reducer;

行动

// action.ts
import axios from "axios";
import type { AxiosResponse } from "axios";
import type { Todo } from './reducer'

export const getTodos = (): {
  type: "GET_TODOS";
  payload: Promise<AxiosResponse<Todo[]>>;
} => ({
  type: "GET_TODOS",
  payload: axios.get("https://jsonplaceholder.typicode.com/todos")
});

export type Action = ReturnType<typeof getTodos>;

您可以创建一个承诺,然后您可以像这样使用 .then。

import { useEffect } from "react";
import { getTodos } from "./action";
import { useAppDispatch } from "./store";

function App() {
  const dispatch = useAppDispatch();
  const myPromise = ()=>Promise.resolve(dispatch(getTodos())); /// creating promise 

  useEffect(() => {
    myPromise().then((res:any)=>{
      console.log(res.value.data)
    })
              
  }, []);
  return <div className="App">Hello World!</div>;
}

export default App;

希望你看起来像这样。

dispatch 会 return 无论你采取什么行动 (thunk) returns.

好像是部分

({
  type: "GET_TODOS",
  payload: axios.get("https://jsonplaceholder.typicode.com/todos")
})

可能是错误的:你想要的可能是

const getTodos = (): ThunkAction => (dispatch) => 
  axios.get("https://jsonplaceholder.typicode.com/todos").then(todos => {
    dispatch({type: "GET_TODOS", payload: todos}); 
    return todos;
  });

通过这种方式,你们都可以触发您的操作,并且您的 Promise return从带有待办事项有效负载的 dispatch 中编辑。

您可能还想根据 https://redux.js.org/usage/usage-with-typescript#type-checking-redux-thunks

使用泛型键入您的 ThunkAction

好的,我自己想出来了。 问题是 useAppDispatch.

的输入不正确

所以在 store.ts 而不是

export const useAppDispatch = () => useDispatch<AppDispatch>();

我们应该

import type { ThunkAction, ThunkDispatch } from "redux-thunk";
export type AppThunk<RT = void> = ThunkAction<
  Promise<RT>,
  RootState,
  unknown,
  AnyAction
>;
export type AppThunkDispatch = ThunkDispatch<RootState, void, Action>;
export const useAppDispatch = () => useDispatch<AppThunkDispatch>();

然后在 action.ts 中,我们可以这样编写 thunk 操作:

export const getTodos =
  (): AppThunk<{ type: "SET_TODOS"; payload: Todo[] }> => (dispatch) =>
    axios
      .get<Todo[]>("https://jsonplaceholder.typicode.com/todos")
      .then((response) => response.data)
      .then((todos) => dispatch(setTodos(todos)));

export const setTodos = (
  todos: Todo[]
): {
  type: "SET_TODOS";
  payload: Todo[];
} => ({
  type: "SET_TODOS",
  payload: todos
});

最后,在我们应用的任何地方使用 dispatch:

dispatch(getTodos()).then((todos) => console.log(todos));