redux-observable + socket.io: действия должны быть простыми объектами. Используйте собственное промежуточное ПО для асинхронных действий

Я играю с 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)
      }
     }) 

person zaebalo    schedule 09.07.2017    source источник


Ответы (1)


Могут быть некоторые фундаментальные недоразумения относительно RxJS, поэтому я бы потратил некоторое время, чтобы действительно получить прочную основу для этого, или рассмотреть возможность использования вместо этого чего-то вроде redux-thunk, если ваши асинхронные потребности не более сложны, чем это.

Итак, вот несколько вещей, которые помогут вам:

  • Ваш map возвращает Observable, что означает, что теперь у вас есть Observable of Observables, также известный как Observable<Observable>, что почти наверняка не то, что вам нужно.
  • Непонятно, что должен делать Observable.of(socket.emit('verify', { token: 'suc' }))), потому что socket.emit() возвращает сам сокет, поэтому вы испускаете, а затем сопоставляете действие с Observable самого сокета?
  • Использование mapTo также, вероятно, не соответствует вашим намерениям. Второй отменяет первый, и вы снова создаете Observable of Observable. Таким образом, ваш эпик испускает (и, следовательно, отправляет) поток Observables, а не действий, поэтому вы получаете ошибку «действия должны быть простыми объектами» от избыточности.

Я не решаюсь дать вам решение, но я попрошу вас по-настоящему понять его, а не просто копировать и вставлять. Может быть, сделать шаг назад, попытаться забыть все свои нынешние убеждения о том, как работает Rx, и начать все заново? Тогда у вас может быть это «ах-ха!» момент :)

Я предполагаю, что вы хотели сделать что-то вроде этого:

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 })
      )
    );
}
person jayphelps    schedule 10.07.2017
comment
Спасибо, все заработало, как я и хотел. Можете ли вы показать пример, как добавить тайм-аут? - person zaebalo; 11.07.2017