Home > Software engineering >  How should I type React's dispatch function to be able to do a "then" method like a P
How should I type React's dispatch function to be able to do a "then" method like a P

Time:04-07

I have a simple app which dispatches an action on first load to populate the store. I want to be able to run a then method on dispatch but typescript complains about this.

(According to redux's documentation, the return value of dispatching an action is the return value of the action itself)

Code available in 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>;
}

Store Configuration

I use @reduxjs/toolkit to configure my store and have a redux-promise-middleware set up for it so that upon "fulfillment" of my promised-based actions an <ACTION>_FULFILLED action will also be dispatched.

// 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

// 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

// 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>;

CodePudding user response:

You can create a promise and then you can use .then like this.

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;

Hope you are looking something like this.

CodePudding user response:

dispatch would return whatever your action (thunk) returns.

It seems the part

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

might be wrong: what you wanted is probably

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

This way you both have your action fired and your Promise returned from dispatch with todos payload.

You may also want to type your ThunkAction with generics according to https://redux.js.org/usage/usage-with-typescript#type-checking-redux-thunks

  • Related