ReactおよびRedux-Observableを使用してアプリを作成しています。これは初めてで、ユーザーログインを実行するためのエピックを作成しようとしています。
私の叙事詩は以下の通りです:
_export const loginUserEpic = (action$: ActionsObservable<Action>) =>
action$.pipe(
ofType<LoginAction>(LoginActionTypes.LOGIN_ACTION),
switchMap((action: LoginAction) =>
ajax({
url,
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: { email: action.payload.username, password: action.payload.password },
}).pipe(
map((response: AjaxResponse) => loginSuccess(response.response.token)),
catchError((error: Error) => of(loginFailed(error))),
),
),
);
_
問題は、次の行でTypeScriptエラーが発生していることです:ofType<LoginAction>(LoginActionTypes.LOGIN_ACTION)
これ:
_Argument of type '(source: Observable<LoginAction>) => Observable<LoginAction>' is not assignable to parameter of type 'OperatorFunction<Action<any>, LoginAction>'.
Types of parameters 'source' and 'source' are incompatible.
Type 'Observable<Action<any>>' is not assignable to type 'Observable<LoginAction>'.
Type 'Action<any>' is not assignable to type 'LoginAction'.
Property 'payload' is missing in type 'Action<any>'.
_
私の行動はここにあります:
_export enum LoginActionTypes {
LOGIN_ACTION = 'login',
LOGIN_SUCCESS_ACTION = 'login-sucesss',
LOGIN_FAILED_ACTION = 'login-failed',
}
export interface LoginAction extends Action {
type: LoginActionTypes.LOGIN_ACTION;
payload: {
username: string;
password: string;
};
}
export function login(username: string, password: string): LoginAction {
return {
type: LoginActionTypes.LOGIN_ACTION,
payload: { username, password },
};
}
export interface LoginSuccessAction extends Action {
type: LoginActionTypes.LOGIN_SUCCESS_ACTION;
payload: {
loginToken: string;
};
}
export function loginSuccess(loginToken: string): LoginSuccessAction {
return {
type: LoginActionTypes.LOGIN_SUCCESS_ACTION,
payload: { loginToken },
};
}
export interface LoginFailedAction extends Action {
type: LoginActionTypes.LOGIN_FAILED_ACTION;
payload: {
error: Error;
};
}
export function loginFailed(error: Error): LoginFailedAction {
return {
type: LoginActionTypes.LOGIN_FAILED_ACTION,
payload: { error },
};
}
export type LoginActions = LoginAction | LoginSuccessAction | LoginFailedAction;
_
Epicでany
タイプを使用せずにこれを修正するにはどうすればよいですか?
redux-observable
が提供するofType
演算子は、共用体タイプを区別するための最良の方法ではありません。より良い方法は、 typesafe-actions
によって提供されるisOfType
関数を使用することです。
import { filter } from 'rxjs/operators';
import { isOfType } from 'typesafe-actions';
最初に、TypeScriptにアプリケーションで使用できるアクションについて伝えましょう。アクションストリームはActionsObservable<Action>
として定義するのではなく、yourアクションのストリームとして定義する必要があります:ActionsObservable<LoginActions>
。
export const loginUserEpic = (action$: ActionsObservable<LoginActions>) =>
これで、isOfType
述語をfilter
演算子と一緒に使用できます。これを交換してください:
ofType<LoginAction>(LoginActionTypes.LOGIN_ACTION)
これとともに:
filter(isOfType(LoginActionTypes.LOGIN_ACTION))
ストリームに渡されたアクションは、LoginAction
として正しく認識されます。
https://github.com/piotrwitek/react-redux-TypeScript-guide をチェックアウトして、React、Redux、およびRedux-Observableを使用する場合の標準的なすべての実行方法を確認してください。
タイプを実現するには、typesafe-actions
ライブラリを使用することをお勧めします。
いくつかの擬似コード:
アクション
これの代わりに
export interface LoginSuccessAction extends Action {
type: LoginActionTypes.LOGIN_SUCCESS_ACTION;
payload: {
loginToken: string;
};
}
export function loginSuccess(loginToken: string): LoginSuccessAction {
return {
type: LoginActionTypes.LOGIN_SUCCESS_ACTION,
payload: { loginToken },
};
}
インターフェースなしでtypesafe-actions
を使用
actions/login/LoginActions.ts
import { action } from "typesafe-actions"
export function loginSuccess(loginToken: string) {
return action(LoginActionTypes.LOGIN_SUCCESS_ACTION, { loginToken });
}
actions/login/LoginActionsModel.ts
import * as LoginActions from "./LoginActions";
import { ActionCreator } from "typesafe-actions";
export type LoginActionCreator = ActionCreator<typeof LoginActions>
次に、すべてのアクションをエクスポートします。
actions/index.ts
import { LoginActionCreator } from "./login/LoginActionModel";
export default type AllActions = LoginActionCreator
叙事詩
import { Epic } from "redux-observable";
import { isOfType } from "typesafe-actions";
import { filter } from "rxjs/operators";
export const loginUserEpic: Epic<AllActions> = (action$) =>
action$.pipe(
filter(isOfType((LoginActionTypes.LOGIN_ACTION))),
switchMap((action: LoginAction) =>
ajax({
url,
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: { email: action.payload.username, password: action.payload.password },
}).pipe(
map((response: AjaxResponse) => loginSuccess(response.response.token)),
catchError((error: Error) => of(loginFailed(error))),
),
),
);
エピックがredux-observable
ライブラリからのものである場合、AllActionsはエピックの入力および出力であるアクションです。
タイプは次のとおりです。
Epic<InputActions, OutputActions, Store>
Epic<Actions(Input&Output)>
Reduxからのストアを使用する場合は、RootState(ルートリデューサーから)が必要です。
export const someEpic: Epic<AllActions, AllActions, RootState> = (action$, store$)
=> action$.pipe(
filter(isOfType(SOMETYPE)),
mergeMap(action => {
const a = store$.value.a;
return someActions(a, action.payload.b);
})
TypeScriptは、{ type: string }
という形式のインターフェースであるredux ActionsObservable
をジェネリックAction
アクションに設定したため、エラーを出します。 TypeScriptは、今後のすべてのアクションにはタイプしかないと考えていますが、ofType
フィルタリング演算子を別のタイプのアクションに設定しています。これは、reduxアクションインターフェイスを拡張する場合でも、TypeScriptでは、そのインターフェイスを実装するものを渡すためにbivarianceHack
が必要です。最も簡単な回避策の1つは、ActionsObservable<Action>
をActionsObservable<AnyAction>
に変更し、AnyAction
をreduxからインポートすることです。