我的背景是.NET开发,WPF正是如此。当你想在C#中调用经过身份验证的操作时,这就是你必须要做的事情。
var res = action();
if(res == Not authenticated)
{
var cred = await showDialog();
action(cred);
}
非常简单,如果您未经过身份验证,则可以调用操作显示对话框窗口,用户可以在其中输入凭据并再次使用凭据调用操作。
现在我正在尝试使用epics在redux中实现相同的功能。
export const pullCurrentBranchEpic = (action$: ActionsObservable<Action>, store: Store<ApplicationState>) =>
action$
.filter(actions.pullCurrentBranch.started.match)
.mergeMap(action => Observable.merge(
Observable.of(repoActions.authenticate.started({})),
waitForActions(action$, repoActions.authenticate.done)
.mergeMap(async _=>{
const repository = await getCurrentRepository(store.getState());
await pullCurrentBranch(repository);
})
.map(_=>actions.pullCurrentBranch.done({params:{},result:{}}))
.race(
action$.ofType(repoActions.authenticate.failed.type)
.map(() => actions.pullCurrentBranch.failed({error:"User not authenticated",params:{}}))
.take(1))
));
export const waitForActions = (action$, ...reduxActions) => {
const actionTypes = reduxActions.map(x => x.type);
const obs = actionTypes.map(type => action$.ofType(type).take(1));
return Observable.forkJoin(obs);
};
基本上epic将处理一个动作(我甚至不检查用户是否应该被认证,因为它会使这个代码翻倍)并且动作将运行另一个动作 - authenticate.started将改变reducer中的状态以打开在窗口中,用户将能够输入他的凭据,当他这样做时,它将调用authenticate.done操作。当它完成后,史诗将继续承诺调用一个动作,该动作将在完成时引发另一个动作。难道我做错了什么?对于这么简单的事情,这不是太复杂吗?我认为这不是redux的问题,而是redux可观察和史诗。
答案 0 :(得分:2)
这可能符合您的需求。需要注意的重要事项是将问题分解为多个史诗。
const startAuthEpic = action$ => action$
.filter(action.pullCurrentBranch.start.match)
.map(action => repoActions.authenticate.started({});
const authDoneEpic = (action$, store) => action$
.ofType(repoActions.authenticate.done.type)
.mergeMap(() => {
const repo = getCurrentRepository(store.getState());
return Observable.fromPromise(pullCurrentBranch(repo))
.map(() => actions.pullCurrentBranch.done({ ... }))
.catch(error => Observable.of(actions.pullCurrentBranch.failed({ ... })))
});
有多种方法可以包含更多经过身份验证的操作,其中一种方法是:
// each new authenticated action would need to fulfill the below contract
const authenticatedActions = [
{
action: 'pull',
match: action.pullCurrentBranch.start.match,
call: store => {
const repo = getCurrentRepository(store.getState());
return pullCurrentBranch(repo);
},
onOk: () => actions.pullCurrentBranch.done({}),
onError: () => actions.pullCurrentBranch.failed({})
},
{
action: 'push',
match: action.pushCurrentBranch.start.match,
call: store => {
const repo = getCurrentRepository(store.getState());
return pushCurrentBranch(repo);
},
onOk: () => actions.pushCurrentBranch.done({}),
onError: () => actions.pushCurrentBranch.failed({})
}
];
const startAuthEpic = action$ => action$
.map(action => ({ action, matched: authenticatedActions.find(a => a.match(action)) }))
.filter(wrapped => wrapped.matched)
.map(wrapped => repoActions.authenticate.started({ whenAuthenticated: wrapped.matched }));
const authDoneEpic = (action$, store) => action$
.ofType(repoActions.authenticated.done.type)
.pluck('whenAuthenticated')
.mergeMap(action => {
const { call, onOk, onError } = action;
return Observable.fromPromise(call(store))
.map(onOk)
.catch(err => Observable.of(onError(err)))
});
您只需将whenAuthenticated
属性传递给authenticate.done
操作。