gpt4 book ai didi

redux - 发出 Action ,然后使用 redux-observable 和 rxjs 请求并发出另一个 Action

转载 作者:行者123 更新时间:2023-12-01 11:10:27 24 4
gpt4 key购买 nike

因此,我有一个接收 SUBMIT_LOGIN Action 的史诗,然后它应该触发 generateDeviceId 函数,该函数返回一个以 id 作为有效负载的 Action 。在reducer处理并更新store之后,它应该请求登录,然后将其解析为store,最后将用户重定向到我们的仪表板

const generateDeviceId = (deviceId)  => (({type: GENERATE_DEVICE_ID, payload: deviceId}));

const resolveLogin = (response) => ({type: RESOLVE_LOGIN, payload: response});

const submitLogin = (email, password) => ({type: SUBMIT_LOGIN, payload: {email, password}});

const requestLogin = (email, password) => ({type: REQUEST_LOGIN, payload: {email, password}});

const loadAbout = () => ({type: LOAD_ABOUT});

const submitLoginEpic = (action$) =>
action$
.ofType(SUBMIT_LOGIN)
.mapTo(generateDeviceId(uuidv1()))
.flatMap(({payload}) => login(payload.email, payload.password)
.flatMap(({response}) => [resolveLogin(response.content), loadAbout()])
);

ps:login函数是 ajax来自 rx-dom返回一个流:
const AjaxRequest = (method, url, data) => {

const state = store.getState();
const {token, deviceId} = state.user;

return ajax({
method,
timeout: 10000,
body: data,
responseType: 'json',
url: url,
headers: {
token,
'device-id': deviceId,
'Content-Type': 'application/json'
}
});

};



const login = (email, password) => AjaxRequest('post', 'sign_in', {email, password});

ps2:uuidv1函数只是生成一个随 secret 钥(它是一个库)

我认为(实际上我很确定)我做错了,但两天后我真的不知道如何进行。 :/

更新

在 Sergey 的第一次更新之后,我将我的史诗改成了那样,但不幸的是,由于某种原因 rx-dom's ajax 不像 Sergey 的 login$ 那样工作可观察的。我们目前正在为此努力。
const generateDeviceId = (deviceId)  => (({type: GENERATE_DEVICE_ID, payload: deviceId}));

const resolveLogin = (response) => ({type: RESOLVE_LOGIN, payload: response});

const submitLogin = (email, password) => ({type: SUBMIT_LOGIN, payload: {email, password}});

const requestLogin = (email, password) => ({type: REQUEST_LOGIN, payload: {email, password}});

const loadAbout = () => ({type: LOAD_ABOUT});

const submitLoginEpic = action$ =>
action$.ofType(SUBMIT_LOGIN)
.mergeMap(({payload}) =>
Observable.of(generateDeviceId(uuid()))
.concat(login(payload.email, payload.password)
.concatMap(({response}) => [resolveLogin(response.content), loadAbout()])

更新 2

在 Sergey 的第二次更新之后,我再次更改了我的代码并最终得到了一个使用 two epics 的解决方案。和 .concatMap运算符以 synchronously调度 Action 和它 works as expected .
const generateDeviceId = (deviceId)  => (({type: GENERATE_DEVICE_ID, payload: deviceId}));

const resolveLogin = (response) => ({type: RESOLVE_LOGIN, payload: response});

const submitLogin = (email, password) => ({type: SUBMIT_LOGIN, payload: {email, password}});

const requestLogin = (email, password) => ({type: REQUEST_LOGIN, payload: {email, password}});

const loadAbout = () => ({type: LOAD_ABOUT});

const submitLoginEpic = (action$) =>
action$
.ofType(SUBMIT_LOGIN)
.concatMap(({payload}) => [
generateDeviceId(uuid()),
requestLogin(payload.email, payload.password)
]);

const requestLoginEpic = (action$) =>
action$
.ofType(REQUEST_LOGIN)
.mergeMap(({payload}) => login(payload.email, payload.password)
.concatMap(({response}) => [resolveLogin(response.content), loadAbout()])

最佳答案

如果我猜对了,您希望您的史诗产生以下 Action 序列来响应每个 SUBMIT_LOGIN :

GENERATE_DEVICE_ID -- RESOLVE_LOGIN -- LOAD_ABOUT

另外,我猜 GENERATE_DEVICE_ID需要在 SUBMIT_LOGIN 之后立即发出收到,
RESOLVE_LOGINLOAD_ABOUT仅应在 login() 返回的流之后发出发出。

如果我的猜测是正确的,那么您只需要启动嵌套的 observable(每个 SUBMIT_LOGIN 创建的)
GENERATE_DEVICE_ID行动和 startWith运营商正是这样做的:
const submitLoginEpic = action$ =>
action$.ofType(SUBMIT_LOGIN)
.mergeMap(({ payload }) =>
login(payload.email, payload.password)
.mergeMap(({ response }) => Rx.Observable.of(resolveLogin(response.content), loadAbout()))
.startWith(generateDeviceId(uuidv1()))
);

更新:一种可能的替代方法是使用 concat运营商: obs1.concat(obs2)订阅 obs2仅当 obs1已完成。

另请注意,如果 login()需要在 GENERATE_DEVICE_ID 之后调用已发送,您可能希望将其包装在“冷”可观察对象中:
const login$ = payload =>
Rx.Observable.create(observer => {
return login(payload.email, payload.password).subscribe(observer);
});

const submitLoginEpic = action$ =>
action$.ofType(SUBMIT_LOGIN)
.mergeMap(({ payload }) =>
Rx.Observable.of(generateDeviceId(uuidv1()))
.concat(login$(payload).map(({ response }) => resolveLogin(response.content)))
.concat(Rx.Observable.of(loadAbout()))
);

这边 GENERATE_DEVICE_IDlogin() 之前发出被称为,即序列将是
GENERATE_DEVICE_ID -- login() -- RESOLVE_LOGIN -- LOAD_ABOUT

更新 2:原因 login()无法按预期工作是因为它取决于外部状态( const state = getCurrentState() ),该状态在 login() 时的时间点不同。被调用并且当 login() 返回一个 observable 时已订阅。 AjaxRequest捕获 login() 时的状态被调用,发生在 GENERATE_DEVICE_ID 之前被派往商店。此时还没有执行网络请求,但是 ajax observable 已经基于错误的状态进行了配置。

为了看看会发生什么,让我们稍微简化一下,并以这种方式重写史诗:
const createInnerObservable = submitLoginAction => {
return Observable.of(generateDeviceId()).concat(login());
}

const submitLoginEpic = action$ =>
action$.ofType(SUBMIT_LOGIN).mergeMap(createInnerObservable);

SUBMIT_LOGIN行动来了, mergeMap()第一次来电 createInnerObservable()功能。该函数需要创建一个新的 observable 并且它必须调用 generateDeviceId()login()职能。当 login()被调用时,状态仍然很旧,因为此时尚未创建内部可观察对象,因此没有机会 GENERATE_DEVICE_ID被 dispatch 。正因为如此 login()返回 ajax observable 配置了旧数据,它成为结果内部 observable 的一部分。尽快 createInnerObservable()返回, mergeMap()订阅返回的内部 observable 并开始发出值。 GENERATE_DEVICE_ID首先,被分派(dispatch)到商店并改变状态。之后, ajax observable(现在是内部 observable 的一部分)被订阅并执行网络请求。但是新状态对此没有影响,因为 ajax observable 已经用旧数据初始化。

包装 login进入 Observable.create推迟调用,直到 Observable.create 返回的 observable已订阅,并且此时状态已经是最新的。

一个替代方案可能是引入一个额外的史诗,它将对 GENERATE_DEVICE_ID 使用react。操作(或其他适合您的域的操作)并发送登录请求,例如:
const submitLogin = payload => ({ type: "SUBMIT_LOGIN", payload });

// SUBMIT_LOGIN_REQUESTED is what used to be called SUBMIT_LOGIN
const submitLoginRequestedEpic = action$ =>
action$.ofType(SUBMIT_LOGIN_REQUESTED)
.mergeMap(({ payload }) => Rx.Observable.of(
generateDeviceId(uuidv1()),
submitLogin(payload))
);

const submitLoginEpic = (action$, store) =>
action$.ofType(SUBMIT_LOGIN)
.mergeMap(({ payload }) => {
// explicitly pass all the data required to login
const { token, deviceId } = store.getState().user;
return login(payload.email, payload.password, token, deviceId)
.map(({ response }) => resolveLogin(response.content))
.concat(loadAbout());
});

学习资源

redux-observable基于 RxJS,首先熟悉 Rx 是有意义的。

我强烈推荐观看 "You will learn RxJS"安德烈·斯塔尔茨的谈话。它应该直观地了解什么是可观察对象以及它们如何在幕后工作。

安德烈还撰写了这些关于蛋头的非凡类(class):
  • Creating Observables from scratch
  • Operators in Depth
  • Use Higher Order Observables in RxJS Effectively

  • 杰伊菲尔普斯也给了 a brilliant talkredux-observable ,绝对值得一看。

    关于redux - 发出 Action ,然后使用 redux-observable 和 rxjs 请求并发出另一个 Action ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44706633/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com