Отправка действия Redux Thunk вне компонента React с помощью TypeScript - PullRequest
2 голосов
/ 07 июня 2019

Я нахожусь в процессе преобразования приложения 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>
));

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

Проблема возникает, когда я хочу использовать реестр для отправки группового действия:

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 мог видеть, что метод отправки не по умолчанию, который позволяет только нормальныйдействия.Я, однако, в растерянности от того, как это сделать.Я не могу найти примеров того, как кто-то еще делал то же самое.

Любая помощь приветствуется.


Редактировать : Предлагаемый дубликат Как отправить действие или действие ThunkAction (в TypeScript, с использованием redux-thunk)? не решает мою проблему.Я могу отправлять Thunk действия хорошо внутри компонентов.У меня проблемы только с внешними компонентами, я использую реестр хранилища, определенный выше.


Редактировать 2 : Таким образом, я могу использовать следующее утверждение типа при отправке действия thunkизбавиться от ошибки:

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

Это очень непрактично.Мне еще предстоит найти способ сделать это, чтобы TypeScript знал, что метод dispatch всегда должен иметь возможность отправлять действие thunk.

1 Ответ

1 голос
/ 08 июня 2019

Ваш код почти правильный.Вы неправильно установили тип возврата для экспорта по умолчанию.

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

В случае использования промежуточного программного обеспечения вышеприведенная функция должна возвращать не Store<State>, а Store<S & StateExt, A> & Ext, где Ext будет перегружено dispatch, что приведет куметь отправлять функции (как в случае с избыточным числом).

Чтобы упростить, просто удалите точный тип возвращаемого значения и позвольте самому TypeScript выводить сам тип

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

Это решаетваша проблема.

В качестве альтернативы вы можете использовать более классический подход к созданию хранилища.

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

Основные вещи здесь:

  1. Использование createStoreв соответствии с рекомендациями Redux Официальный документ .createStore вызывается с промежуточным программным обеспечением, поскольку второй аргумент сам будет вызывать его .Но файлы объявлений TypeScript для redux и redux-thunk предварительно сконфигурированы для такого использования createStore.Таким образом, возвращенный магазин будет иметь модифицированную версию из dispatch.(Запишите Ext and StateExt параметры типа StoreEnhancer<Ext, StateExt>. Они будут пересечены с результирующим хранилищем и добавят перегруженную версию dispatch, которая будет принимать функции в качестве аргументов).

  2. Такжевозвращаемый тип экспортируемой по умолчанию функции будет выведен из возвращаемого типа createStore.Это НЕ будет Store<State>.

...