使用TypeScript

时间:2019-06-07 16:35:57

标签: javascript reactjs typescript redux redux-thunk

我正在将React Native应用程序转换为TypeScript,并且无法在商店外部调度重击动作。这是我目前的商店设置方式:

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

商店/注册中心

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

当我想使用注册表分派重击操作时出现问题:

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可以将store派发方法替换为一种可以派发thunk动作常规动作的方法。 / p>

我认为我需要以某种方式键入注册表,以便TypeScript可以看到调度方法不是默认允许仅正常操作的默认方法。但是,我对如何执行此操作不知所措。我找不到任何其他人在做同样事情的例子。

感谢您的帮助。


编辑:建议的How to dispatch an Action or a ThunkAction (in TypeScript, with redux-thunk)? 重复项不能解决我的问题。我可以在组件内部调度重击动作。使用上面定义的商店注册表,我只会遇到组件外部的问题。


编辑2 :因此,当我分派重击操作来消除错误时,似乎可以使用以下类型断言:

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

但这是非常不切实际的。我还没有找到一种方法来使TypeScript知道dispatch方法应该总是能够调度重击动作。

2 个答案:

答案 0 :(得分:1)

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

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

在使用中间件的情况下,上面的函数应该不返回Store<State>,而是返回Store<S & StateExt, A> & Ext,其中Ext将被重载dispatch,将能够分派{{3} }(就像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. 按照functions的建议使用createStore。以中间件作为第二个参数调用的createStore本身Redux official doc。但是用于redux和redux-thunk的TypeScript声明文件已预先配置为使用createStore。因此,退货的商店将有dispatch的{​​{3}}。 (请注意Ext and StateExt的{​​{1}}类型参数。它们将与结果存储区相交,并添加StoreEnhancer<Ext, StateExt>的重载版本,该版本将接受函数作为参数。)

  2. 还可以从dispatch的返回类型中推断出默认导出功能的返回类型。不会是createStore

答案 1 :(得分:1)

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

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