I'm in the process of converting a React Native app into TypeScript and I'm having trouble with dispatching thunk actions outside of the store. Here is how my store is set up currently:
store/index.ts
import { createStore, applyMiddleware, combineReducers, Reducer, Store } from 'redux';
import thunk, { ThunkMiddleware } from 'redux-thunk';
export interface State { ... }
export interface ActionTypes { ... } // All of the non-thunk actions
const reducer: Reducer<State> = combineReducers({ ... });
export default (): Store<State> => {
return applyMiddleware(
thunk as ThunkMiddleware<State, ActionTypes>
)(createStore)(reducer);
}
index.tsx
import { Provider } from 'react-redux';
import createStore from './store/index';
import { registerStore } from './store/registry';
const store = createStore();
registerStore(); // Registers the store in store/registry.ts
AppRegistry.registerComponent(appName, () => () => (
<Provider store={store}>
<App />
</Provider>
));
store/registry.ts
import { Store } from 'redux';
import { State } from './index';
let store: Store<State>;
export const registerStore = (newStore: Store<State>) => {
store = newStore;
};
export const getStore = () => store;
So when the store is created, I'm storing it in the store registry so I can call getStore() from anywhere.
This works fine in components (where I'm not using the registry), for example in my App.tsx:
import { connect } from 'react-redux';
import { ThunkDispatch } from 'redux-thunk';
import { checkAuthStatus as checkAuthStatusAction } from './store/modules/auth/actions';
import { ActionTypes, State as AppState } from './store/index';
interface State = { ... }
interface StateProps { ... }
interface DispatchProps {
checkAuthStatus: () => Promise<boolean>;
}
type Props = StateProps & DispatchProps;
class App extends Component<Props, State> {
async componentDidMount() {
const promptSkipped: boolean = await checkAuthStatus(); // Thunk action, works fine!
}
...
}
const mapStateToProps = ...;
const mapDispatchToProps = (dispatch: ThunkDispatch<AppState, null, ActionTypes>): DispatchProps => ({
checkAuthStatus: () => dispatch(checkAuthStatusAction()),
});
export default connect<StateProps, DispatchProps, {}, AppState>(
mapStateToProps,
mapDispatchToProps,
)(App);
The problem comes when I want to use the registry to dispatch a thunk action:
lib/notacomponent.ts
import { getStore } from '../store/registry';
import { checkAuthStatus, setLoggedIn } from '../store/modules/auth/actions'
const someFunction = () => {
const store = getStore();
const { auth } = store.getState(); // Accessing state works fine!
store.dispatch(setLoggedIn(true)); // NON-thunk action, works fine!
store.dispatch(checkAuthStatus()); // Uh-oh, thunk action doesn't work.
}
This gives me the error:
Argument of type 'ThunkAction<Promise<boolean>, State, null, Action<any>>' is
not assignable to parameter of type 'AnyAction'.
Property 'type' is missing in type 'ThunkAction<Promise<boolean>, State, null, Action<any>>'
but required in type 'AnyAction'. ts(2345)
As far as I am aware, using thunk as ThunkMiddleware<State, ActionTypes> as a middleware allows Redux Thunk to replace the stores dispatch method with one that makes it possible to dispatch thunk actions and normal actions.
I think I need to somehow type the registry in a way that TypeScript can see that the dispatch method is not the default one that only allows normal actions. I am, however, at a loss on how to do this. I can't find any examples of anyone else doing the same thing.
Any help is appreciated.
Edit: The suggested duplicate of How to dispatch an Action or a ThunkAction (in TypeScript, with redux-thunk)? doesn't solve my issue. I can dispatch thunk actions fine inside components. I'm only having issues outside components, using the store registry defined above.
Edit 2: So it seems I can use the following type assertion when dispatching the thunk action to get rid of the error:
(store.dispatch as ThunkDispatch<State, void, ActionTypes>)(checkAuthStatus())
That is very impractical though. I'm yet to find a way to make it so TypeScript knows that the dispatch method should always be able to dispatch a thunk action.