如何使用 redux-observable 在一个史诗中发出多个动作?
How to emit multiple actions in one epic using redux-observable?
我是 rxjs/redux observable 的新手,想做两件事:
1) 改进这部史诗,使其更加地道
2) 从单个史诗调度两个动作
我看到的大多数示例都假设 api 库会在获取失败时抛出异常,但我将我的设计设计得更可预测,并且使用 Typescript 联合类型我在解压结果之前被迫检查 ok: boolean
值,因此理解如何在 rxjs 中执行此操作更具挑战性。
改进以下内容的最佳方法是什么?如果请求成功,我想同时发出成功操作(意味着用户已获得授权)和 'fetch account' 操作,这是一个单独的操作,因为有时我可能需要获取帐户在 'logging in' 之外。任何帮助将不胜感激!
const authorizeEpic: Epic<ActionTypes, ActionTypes, RootState> = action$ =>
action$.pipe(
filter(isActionOf(actions.attemptLogin.request)),
switchMap(async val => {
if (!val.payload) {
try {
const token: Auth.Token = JSON.parse(
localStorage.getItem(LOCAL_STORAGE_KEY) || ""
);
if (!token) {
throw new Error();
}
return actions.attemptLogin.success({
token
});
} catch (e) {
return actions.attemptLogin.failure({
error: {
title: "Unable to decode JWT"
}
});
}
}
const resp = await Auth.passwordGrant(
{
email: val.payload.email,
password: val.payload.password,
totp_passcode: ""
},
{
url: "localhost:8088",
noVersion: true,
useHttp: true
}
);
if (resp.ok) {
return [
actions.attemptLogin.success({
token: resp.value
})
// EMIT action 2, etc...
];
}
return actions.attemptLogin.failure(resp);
})
);
您可以压缩您的操作并 return 它们。
zip(actions.attemptLogin.success({
token: resp.value
})
// EMIT action 2, etc...
因此,现在您的两个操作都将被调用。
docs for switchMap
表示项目函数(您示例中的 lambda)可能 return 如下:
type ObservableInput<T> = SubscribableOrPromise<T> | ArrayLike<T> | Iterable<T>
当 Promise<T>
被 returned 时,简单地发出解析值。在您的示例中,如果您 return 来自 async
范围的数组,则 array 将直接发送到 Redux 存储。假设您没有特殊的 Redux 中间件设置来处理一系列事件,这可能不是您想要的。相反,我建议 return 在项目函数中设置一个可观察对象。这是对您的示例的轻微修改:
const authorizeEpic: Epic<ActionTypes, ActionTypes, RootState> = action$ =>
action$.pipe(
filter(isActionOf(actions.attemptLogin.request)), // or `ofType` from 'redux-observable'?
switchMap(action => {
if (action.payload) {
try {
const token: Auth.Token = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY) || "")
if (!token) {
throw new Error()
}
// return an observable that emits a single action...
return of(actions.attemptLogin.success({
token
}))
} catch (e) {
// return an observable that emits a single action...
return of(actions.attemptLogin.failure({
error: {
title: "Unable to decode JWT"
}
}))
}
}
// return an observable that eventually emits one or more actions...
return from(Auth.passwordGrant(
{
email: val.payload.email,
password: val.payload.password,
totp_passcode: ""
},
{
url: "localhost:8088",
noVersion: true,
useHttp: true
}
)).pipe(
mergeMap(response => response.ok
? of(
actions.attemptLogin.success({ token: resp.value }),
// action 2, etc...
)
: of(actions.attemptLogin.failure(resp))
),
)
}),
)
我没有你的 TypeScript 类型定义,所以我无法验证上面的例子是否正确。但是,我在较新版本的 TypeScript、RxJS 和 redux-observable 上取得了相当大的成功。上面没有任何突出的地方让我认为你应该遇到任何问题。
我是 rxjs/redux observable 的新手,想做两件事:
1) 改进这部史诗,使其更加地道
2) 从单个史诗调度两个动作
我看到的大多数示例都假设 api 库会在获取失败时抛出异常,但我将我的设计设计得更可预测,并且使用 Typescript 联合类型我在解压结果之前被迫检查 ok: boolean
值,因此理解如何在 rxjs 中执行此操作更具挑战性。
改进以下内容的最佳方法是什么?如果请求成功,我想同时发出成功操作(意味着用户已获得授权)和 'fetch account' 操作,这是一个单独的操作,因为有时我可能需要获取帐户在 'logging in' 之外。任何帮助将不胜感激!
const authorizeEpic: Epic<ActionTypes, ActionTypes, RootState> = action$ =>
action$.pipe(
filter(isActionOf(actions.attemptLogin.request)),
switchMap(async val => {
if (!val.payload) {
try {
const token: Auth.Token = JSON.parse(
localStorage.getItem(LOCAL_STORAGE_KEY) || ""
);
if (!token) {
throw new Error();
}
return actions.attemptLogin.success({
token
});
} catch (e) {
return actions.attemptLogin.failure({
error: {
title: "Unable to decode JWT"
}
});
}
}
const resp = await Auth.passwordGrant(
{
email: val.payload.email,
password: val.payload.password,
totp_passcode: ""
},
{
url: "localhost:8088",
noVersion: true,
useHttp: true
}
);
if (resp.ok) {
return [
actions.attemptLogin.success({
token: resp.value
})
// EMIT action 2, etc...
];
}
return actions.attemptLogin.failure(resp);
})
);
您可以压缩您的操作并 return 它们。
zip(actions.attemptLogin.success({
token: resp.value
})
// EMIT action 2, etc...
因此,现在您的两个操作都将被调用。
docs for switchMap
表示项目函数(您示例中的 lambda)可能 return 如下:
type ObservableInput<T> = SubscribableOrPromise<T> | ArrayLike<T> | Iterable<T>
当 Promise<T>
被 returned 时,简单地发出解析值。在您的示例中,如果您 return 来自 async
范围的数组,则 array 将直接发送到 Redux 存储。假设您没有特殊的 Redux 中间件设置来处理一系列事件,这可能不是您想要的。相反,我建议 return 在项目函数中设置一个可观察对象。这是对您的示例的轻微修改:
const authorizeEpic: Epic<ActionTypes, ActionTypes, RootState> = action$ =>
action$.pipe(
filter(isActionOf(actions.attemptLogin.request)), // or `ofType` from 'redux-observable'?
switchMap(action => {
if (action.payload) {
try {
const token: Auth.Token = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY) || "")
if (!token) {
throw new Error()
}
// return an observable that emits a single action...
return of(actions.attemptLogin.success({
token
}))
} catch (e) {
// return an observable that emits a single action...
return of(actions.attemptLogin.failure({
error: {
title: "Unable to decode JWT"
}
}))
}
}
// return an observable that eventually emits one or more actions...
return from(Auth.passwordGrant(
{
email: val.payload.email,
password: val.payload.password,
totp_passcode: ""
},
{
url: "localhost:8088",
noVersion: true,
useHttp: true
}
)).pipe(
mergeMap(response => response.ok
? of(
actions.attemptLogin.success({ token: resp.value }),
// action 2, etc...
)
: of(actions.attemptLogin.failure(resp))
),
)
}),
)
我没有你的 TypeScript 类型定义,所以我无法验证上面的例子是否正确。但是,我在较新版本的 TypeScript、RxJS 和 redux-observable 上取得了相当大的成功。上面没有任何突出的地方让我认为你应该遇到任何问题。