简体   繁体   中英

What is wrong with my approach to redux-thunk dispatch? (React, typescript, TS-2345)

My action creators:

// file: redux/contest/actions.ts
import { ThunkAction } from 'redux-thunk'
import { ContestActionTypes, /* ... */ SET_CONTEST_CONFIG } from './types' ;
import { CONFIG_COLL, CONTEST_KEY, database } from '../../database'
import { RootState } from '../store';
/* ... */ 

export const setContestConfig: (arg0: IContestState) => ContestActionTypes =
  (contestState) => ({ type: SET_CONTEST_CONFIG, payload: contestState })

export const loadContestConfig = (): ThunkAction<void, RootState, unknown, ContestActionTypes> =>
  async dispatch => {
    const data = await database.collection(CONFIG_COLL).doc(CONTEST_KEY).get()
    dispatch(setContestConfig(data))
  }

// file: redux/contest/types.ts

/* ... various action structures defined as interface ... */

export type ContestActionTypes = ISerialAction | IStringArrayAction | ITypeOnlyAction | IStringOnlyAction | ISetContestAction 

This part compiles without any problems.

Now in my index.tsx I have this:

import React from 'react';
import ReactDOM from 'react-dom';
import { applyMiddleware, createStore } from 'redux'
import { Provider } from 'react-redux' 
import thunk from 'redux-thunk'
import App from './App';
import { rootReducer } from './redux/store'
import { loadContestConfig, saveContestConfig } from './redux/contest/actions';

const store = createStore( rootReducer, applyMiddleware( thunk ) )
store.dispatch( loadContestConfig() ) // here I want to initialize state from the database
/* ... */

And on the line 11 of index.tsx ( store.dispatch(...) ) I get error message TS-2345

Argument of type 'ThunkAction<void, CombinedState<{ app: AppState; contest: IContestState; }>, unknown, ContestActionTypes>' is not assignable to parameter of type 'ISetStationAction | ISaveAppStateAction | ISerialAction | IStringArrayAction | ITypeOnlyAction | IStringOnlyAction | ISetContestAction'.
  Type 'ThunkAction<void, CombinedState<{ app: AppState; contest: IContestState; }>, unknown, ContestActionTypes>' is missing the following properties from type 'ISetContestAction': type, payload  TS2345

Of course, the action passed as to dispatch (in function loadContestConfig in the first file) has both type and payload , so I have no idea what's wrong here. I followed tutorials but this is incomprehensible, I just can't see the problem.

Finally, I was able to dig the solution. It is necessary to specify the type of dispatch function because typescript is unable to determine the correct overload.

Solution highlights

in file ./redux/store.ts (store declarations)

import { combineReducers } from 'redux' 
import { ThunkDispatch } from 'redux-thunk'
import { AppActionTypes } from './station/types'
import { ContestActionTypes } from './contest/types'
import { stationReducer } from './station/reducers'
import { contestStateReducer } from './contest/reducers'

export const rootReducer = combineReducers({ station: stationReducer, contest: contestStateReducer })  
export type RootState = ReturnType<typeof rootReducer>
export type StoreActionTypes = AppActionTypes | ContestActionTypes 
// the following line is essential - we define proper function type for dispatch with thunk
export type DispatchFunctionType = ThunkDispatch<RootState, undefined, StoreActionTypes>

in file ./index.tsx (React app main TSX file)

import React from 'react';
import ReactDOM from 'react-dom';
import { applyMiddleware, createStore} from 'redux'
import { Provider } from 'react-redux' 
import thunk from 'redux-thunk'
import App from './App';
import { DispatchFunctionType, rootReducer, RootState } from './redux/store'
import { loadContestConfig } from './redux/contest/actions';
import { loadStation } from './redux/station/actions';

const store = createStore( rootReducer, 
   applyMiddleware<DispatchFunctionType, RootState>( thunk ) ) // <- this is essential
store.dispatch(loadContestConfig()) // here I had TS2345 before, now no more problems 
store.dispatch(loadStation())       //  - dtto -

The same dispatch function type must be declared wherever dispatch function is passed as parameter.

Example: in file StationDataModal.tsx

   ...
import { DispatchFunctionType, RootState } from "./redux/store"
   ...

// the following mapping function also caused TS-2345 
// when dispatch type was not declared explicitly
const mapDispatchToProps = ( dispatch: DispatchFunctionType ) => {
  return {
    setContestName: ( name: string ) => {
      dispatch(setContestName( name ))
      dispatch(saveContestConfig())
    },
    setStationData: ( mycall: string, mywwloc: string ) => {
      dispatch(setStation( { mycall, mywwloc } ))
      dispatch(saveStation())
    }
  }
}
 ...

class StationDataModal extends React.Component<StationDataProps> {
  /* component definition ... */
}

export default connect( mapStateToProps, mapDispatchToProps ) (StationDataModal)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM