redux-observable + socket.io:操作必须是普通对象。使用自定义中间件进行异步操作
redux-observable + socket.io: Actions must be plain objects. Use custom middleware for async actions
我正在玩弄 redux-observable 和 socket.io 并尝试通过 socket.emits 验证令牌,但是 redux-observable 说了关于操作的事情。然后我尝试使用 switchMaps,但我在方法列表中只有最后一次调度。我尝试了不同的运算符和方法,但也没有用。我哪里错了?提前致谢。
这是代码。首先,我们在服务器上发出令牌(例如它是 suc
,就像成功一样),如果令牌是 ===
到 suc
我用 verifySuccess
发送发射,否则 verifyError
。我已经测试了服务器端,以防出现问题,但事实并非如此。
前面
export default function verify(action$) {
return action$.ofType(TOKEN_VERIFY_REQUEST)
.map(action => Observable.of(socket.emit('verify', { token: 'suc' })))
.mapTo(
Observable.fromEvent(socket, 'verifySuccess')
.mapTo({ type: TOKEN_VERIFY_SUCCESS })
)
.mapTo(
Observable.fromEvent(socket, 'verifyError')
.mapTo({ type: TOKEN_VERIFY_FAILURE })
)
}
返回
socket.on('verify', async (data) => {
console.log(`got verify with`.red)
console.log(data)
const msgs = {
suc: { msg: 'Received data' },
err: { msg: 'Error in request' }
}
console.log(`data.token is ${data.token}`)
if (data.token === 'suc') {
console.log(`sending success`)
socket.emit('verifySuccess', msgs.suc)
} else {
console.log(`sending error`)
socket.emit('verifyError', msgs.err)
}
})
可能对 RxJS 存在一些根本性的误解,所以我会花一些时间真正打下坚实的基础,或者考虑使用 redux-thunk 之类的东西,如果你的异步需求没有比这更复杂的话.
所以这里有一些指导你的事情:
- 你的
map
正在返回一个 Observable,这意味着你现在有一个 Observable of Observables 又名 Observable<Observable>
这几乎肯定不是你想要的。
- 不清楚
Observable.of(socket.emit('verify', { token: 'suc' })))
是什么意思,因为 socket.emit()
returns 套接字本身,所以你发出然后将动作映射到套接字本身的 Observable?
mapTo
用法也可能不是您想要的。第二个取消了第一个,你又在创建一个 Observable of Observable。所以你的史诗正在发出(并因此调度)一个可观察的流,而不是动作,这就是为什么你从 redux 得到 "actions must be plain objects" 错误。
我对给你一个解决方案犹豫不决,但我会要求你尝试真正理解它,而不仅仅是 copy-pasting。也许退后一步,试着忘记你目前对 Rx 工作原理的所有信念并重新开始?然后你可能会有 "ah ha!" 时刻 :)
我猜你是想做这样的事情:
export default function tokenVerifyRequestEpic(action$) {
return action$.ofType(TOKEN_VERIFY_REQUEST)
.do(() => {
socket.emit('verify', { token: 'suc' }));
})
.mergeMap(() =>
Observable.race(
Observable.fromEvent(socket, 'verifySuccess')
.mapTo({ type: TOKEN_VERIFY_SUCCESS }),
Observable.fromEvent(socket, 'verifyError')
.mapTo({ type: TOKEN_VERIFY_FAILURE })
)
);
}
这是详细的内联注释:
export default function tokenVerifyRequestEpic(action$) {
// filter out all actions except TOKEN_VERIFY_REQUEST
return action$.ofType(TOKEN_VERIFY_REQUEST)
// perform the side effect of emitting the 'verify' message
.do(() => {
socket.emit('verify', { token: 'suc' }));
})
// One problem with this code is that it might not properly account for
// multiple concurrent TOKEN_VERIFY_REQUEST requests. e.g. How are those
// handled by the server? How should be they be handled in the UI?
// If it's not supposed to be possible, it might be useful to assert
// against that condition so that if it does accidentally happen you
// throw an error
.mergeMap(() =>
// Race between either a 'verifySuccess' or 'verifyError'
// This assumes one or the other will always happen, if not
// then you might want to add a timeout() or similar
Observable.race(
Observable.fromEvent(socket, 'verifySuccess')
.mapTo({ type: TOKEN_VERIFY_SUCCESS }),
Observable.fromEvent(socket, 'verifyError')
.mapTo({ type: TOKEN_VERIFY_FAILURE })
)
);
}
我正在玩弄 redux-observable 和 socket.io 并尝试通过 socket.emits 验证令牌,但是 redux-observable 说了关于操作的事情。然后我尝试使用 switchMaps,但我在方法列表中只有最后一次调度。我尝试了不同的运算符和方法,但也没有用。我哪里错了?提前致谢。
这是代码。首先,我们在服务器上发出令牌(例如它是 suc
,就像成功一样),如果令牌是 ===
到 suc
我用 verifySuccess
发送发射,否则 verifyError
。我已经测试了服务器端,以防出现问题,但事实并非如此。
前面
export default function verify(action$) {
return action$.ofType(TOKEN_VERIFY_REQUEST)
.map(action => Observable.of(socket.emit('verify', { token: 'suc' })))
.mapTo(
Observable.fromEvent(socket, 'verifySuccess')
.mapTo({ type: TOKEN_VERIFY_SUCCESS })
)
.mapTo(
Observable.fromEvent(socket, 'verifyError')
.mapTo({ type: TOKEN_VERIFY_FAILURE })
)
}
返回
socket.on('verify', async (data) => {
console.log(`got verify with`.red)
console.log(data)
const msgs = {
suc: { msg: 'Received data' },
err: { msg: 'Error in request' }
}
console.log(`data.token is ${data.token}`)
if (data.token === 'suc') {
console.log(`sending success`)
socket.emit('verifySuccess', msgs.suc)
} else {
console.log(`sending error`)
socket.emit('verifyError', msgs.err)
}
})
可能对 RxJS 存在一些根本性的误解,所以我会花一些时间真正打下坚实的基础,或者考虑使用 redux-thunk 之类的东西,如果你的异步需求没有比这更复杂的话.
所以这里有一些指导你的事情:
- 你的
map
正在返回一个 Observable,这意味着你现在有一个 Observable of Observables 又名Observable<Observable>
这几乎肯定不是你想要的。 - 不清楚
Observable.of(socket.emit('verify', { token: 'suc' })))
是什么意思,因为socket.emit()
returns 套接字本身,所以你发出然后将动作映射到套接字本身的 Observable? mapTo
用法也可能不是您想要的。第二个取消了第一个,你又在创建一个 Observable of Observable。所以你的史诗正在发出(并因此调度)一个可观察的流,而不是动作,这就是为什么你从 redux 得到 "actions must be plain objects" 错误。
我对给你一个解决方案犹豫不决,但我会要求你尝试真正理解它,而不仅仅是 copy-pasting。也许退后一步,试着忘记你目前对 Rx 工作原理的所有信念并重新开始?然后你可能会有 "ah ha!" 时刻 :)
我猜你是想做这样的事情:
export default function tokenVerifyRequestEpic(action$) {
return action$.ofType(TOKEN_VERIFY_REQUEST)
.do(() => {
socket.emit('verify', { token: 'suc' }));
})
.mergeMap(() =>
Observable.race(
Observable.fromEvent(socket, 'verifySuccess')
.mapTo({ type: TOKEN_VERIFY_SUCCESS }),
Observable.fromEvent(socket, 'verifyError')
.mapTo({ type: TOKEN_VERIFY_FAILURE })
)
);
}
这是详细的内联注释:
export default function tokenVerifyRequestEpic(action$) {
// filter out all actions except TOKEN_VERIFY_REQUEST
return action$.ofType(TOKEN_VERIFY_REQUEST)
// perform the side effect of emitting the 'verify' message
.do(() => {
socket.emit('verify', { token: 'suc' }));
})
// One problem with this code is that it might not properly account for
// multiple concurrent TOKEN_VERIFY_REQUEST requests. e.g. How are those
// handled by the server? How should be they be handled in the UI?
// If it's not supposed to be possible, it might be useful to assert
// against that condition so that if it does accidentally happen you
// throw an error
.mergeMap(() =>
// Race between either a 'verifySuccess' or 'verifyError'
// This assumes one or the other will always happen, if not
// then you might want to add a timeout() or similar
Observable.race(
Observable.fromEvent(socket, 'verifySuccess')
.mapTo({ type: TOKEN_VERIFY_SUCCESS }),
Observable.fromEvent(socket, 'verifyError')
.mapTo({ type: TOKEN_VERIFY_FAILURE })
)
);
}