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 })
      )
    );
}