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