繁体   English   中英

使用 TypeScript 在 React 组件之外调度 Redux Thunk 操作

[英]Dispatching a Redux Thunk action outside of a React component with TypeScript

我正在将 React Native 应用程序转换为 TypeScript,但在将 thunk 操作分派到商店外时遇到了问题。 以下是我的商店目前的设置方式:

存储/索引.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);
}

索引.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;

因此,当创建商店时,我将它存储在商店注册表中,以便我可以从任何地方调用getStore()


这在组件中运行良好(我没有使用注册表),例如在我的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);

当我想使用注册表来调度 thunk 操作时,问题就出现了:

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.
}

这给了我错误:

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)

据我所知,使用thunk as ThunkMiddleware<State, ActionTypes>作为中间件允许 Redux Thunk 用一种可以调度 thunk 操作正常操作的方法替换商店调度方法。

我想我需要以某种方式键入注册表,TypeScript 可以看到 dispatch 方法不是仅允许正常操作的默认方法。 然而,我不知道如何做到这一点。 我找不到任何其他人做同样事情的例子。

任何帮助表示赞赏。


编辑How to dispatch an Action or a ThunkAction (in TypeScript, with redux-thunk)的建议副本 不能解决我的问题。 我可以在组件内部很好地调度 thunk 操作。 我只在组件之外遇到问题,使用上面定义的商店注册表。


编辑 2 :因此,在调度 thunk 操作以消除错误时,我似乎可以使用以下类型断言:

(store.dispatch as ThunkDispatch<State, void, ActionTypes>)(checkAuthStatus())

然而这是非常不切实际的。 我还没有找到一种方法来使 TypeScript 知道dispatch方法应该始终能够调度 thunk 操作。

你的代码几乎是正确的。 您错误地设置了默认导出的返回类型。

export default (): Store<State> => {
    return applyMiddleware(
        thunk as ThunkMiddleware<State, ActionTypes>
    )(createStore)(reducer);
}

在中间件使用的情况下,上面的函数不应返回Store<State> ,而是Store<S & StateExt, A> & Ext ,其中Ext将被重载dispatch ,这将能够调度函数(如 redux-thunk 所做的那样)。

为简化起见,只需删除确切的返回类型并让 TypeScript 自行推断类型

export default () => {
    return applyMiddleware(
        thunk as ThunkMiddleware<State, ActionTypes>
    )(createStore)(reducer);
}

那就是解决你的问题。

或者,您可以使用更经典的方法来创建存储。

export default () => {
    return createStore(reducer, applyMiddleware(thunk as ThunkMiddleware<State, ActionTypes>));
}

这里的基本内容:

  1. 使用Redux 官方文档推荐的createStore 使用中间件作为第二个参数调用的createStore本身会调用它 但是针对 redux 和 redux-thunk 的 TypeScript 声明文件是为createStore这种使用预先配置的。 所以返回的 store 将有dispatch 修改版本 (记下StoreEnhancer<Ext, StateExt>Ext and StateExt类型参数。它们将与结果存储相交并添加将接受函数作为参数的重载dispatch版本)。

  2. 此外,默认导出功能的返回类型将从createStore的返回类型中推断出来。 它不会是Store<State>

const boundActions = bindActionCreators( { checkAuthStatus }, store.dispatch);
boundActions.checkAuthStatus();

这有效并且看起来不像“Edit2”那样笨拙

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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