我正在与rxjs一起玩耍,以期掌握JS中的反应式和函数式编程。

我有以下琐碎的应用程序:

import Rx from 'rxjs'

const domContainer = document.querySelector('[data-container]')

const clickElement = document.querySelector('[data-init-fetch]')
const loader = document.querySelector('.loader')

// only emit when all the data is returned
const fetchData = (first, last) => Rx.Observable.range(first, last)
  .flatMap(async n => {
    const res = await fetch(`https://swapi.co/api/people/${n}`)
    return await res.json()
  })
  .reduce((acc, item) => [...acc, item], [])

const createEventStream = ({
  domNode,
  createRequestDataAction,
  createReceiveDataAction,
}) => {

  const handleButtonClick = Rx.Observable.fromEvent(domNode, 'click')

  const request = fetchData(1, 10)
  // i'd like to be be able to 'getState' in here to use correct offset and
  // fetch the next ten entries

  const setIsFetching = handleButtonClick
    .map(createRequestDataAction)

  // fetch data on request
  const requestData = handleButtonClick
    .switchMapTo(request)
    .map(createReceiveDataAction)

  return Rx.Observable.merge(
    requestData,
    setIsFetching,
  )
}

const initState = {
  offset: 1,
  isFetching: false,
  chars: {},
}

const eventStream = createEventStream({
  domNode: clickElement,
  createReceiveDataAction,
  createRequestDataAction,
})
  .scan(reducer, initState)
  .subscribe(
    renderMarkup,
  )

function reducer (
  state = initState,
  {
    type,
    payload,
  }
) {
  // do some reducery stuff in here and return new state
}

function renderMarkup(state) {
  // renders the state to the DOM
}

function createReceiveDataAction(data) {
  return {
    type: 'apiReceive',
    payload: {
      chars: data,
    },
  }
}

function createRequestDataAction() {
  return { type: 'apiRequest' }
  // this action will update offset += 10 in the reducer
}


我的问题是,如果我在调度fetchData操作时使用的是redux(尽管不在这里),我只需调用getState()从当前状态获取offset并在fetchData操作中使用它。

在上面的代码中,有没有办法使用rxjs(并且没有redux)来做到这一点?例如,我想在fetchData调用中使用state中的变量。

我是否需要创建另一个Observable来处理offset?还是我完全以错误的方式来解决这个问题?

为清楚起见进行了编辑。

最佳答案

对我来说,目前还不清楚您要实现什么目标。

我的评论很少,可能对您有帮助。

使用Observable代替Promise

您可以转换此代码

const fetchData = (first, last) => Rx.Observable.range(first, last)
  .flatMap(async n => {
    const res = await fetch(`https://swapi.co/api/people/${n}`)
    return await res.json()
  })
  .reduce((acc, item) => [...acc, item], [])


对此

const fetchData = (first, last) => Rx.Observable.range(first, last)
  .mergeMap(n => {   // mergeMap is the new name of flatMap
    const promiseCall = fetch(`https://swapi.co/api/people/${n}`)
    return Observable.fromPromise(promiseCall).map(res => res.json())
  })
  .reduce((acc, item) => [...acc, item], [])


您也可以查看this question来找到执行许多http请求并仅在所有请求完成后才返回的其他方法。

在eventStream中,您存储一个订阅

如果运行以下代码,最终将在变量eventStream中存储一个Subscription。然后可以使用Subcription实例退订。

const eventStream = createEventStream({
  domNode: clickElement,
  createReceiveDataAction,
  createRequestDataAction,
})
  .scan(reducer, initState)
  .subscribe(
    renderMarkup,
  )


在fetchData调用中使用状态信息

我假设您将状态信息存储在state变量引用的对象中。

如果这是正确的,我将按照这些思路进行一些操作

const createEventStream = ({
  domNode,
  createRequestDataAction,
  createReceiveDataAction,
}) => {

  Rx.Observable.fromEvent(domNode, 'click')
    .map(createRequestDataAction)
    .concat(fetchData(initState.offset, initState.offset + 10))
    .map(createReceiveDataAction)

}


您甚至可以考虑用这样的方法删除reduce函数。

const createEventStream = ({
  domNode
}) => {

  Rx.Observable.fromEvent(domNode, 'click')
    .do(_ => state.fetching = true)
    .concat(fetchData(initState.offset, initState.offset + 10))
    .do(_ => initState.offset = initState.offset + 10)
    .do(_ => initState.fetching = false)

}


我无法测试我的建议,因此我不确定是否有错误。

08-19 15:51