【问题标题】:equivalent of redux 'getState' in from an observable in rxjs相当于 rxjs 中可观察到的 redux 'getState'
【发布时间】:2018-08-29 04:16:13
【问题描述】:

我正在玩 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 并在 @987654323 中使用它@动作。

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

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

为清晰起见进行了编辑。

【问题讨论】:

    标签: javascript redux rxjs reactive-programming


    【解决方案1】:

    我不太清楚你想要达到什么目的。

    我有几个可以帮助你的 cmets。

    使用 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 中。然后可以使用 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)
    
    }
    

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

    【讨论】:

    • 谢谢。关于我想要实现的目标,我会尝试访问状态以在 fetchData 调用中使用。这样我就可以调用fetchData(offset, offset + 10) 并从 api 中检索接下来的 10 个条目。在 redux 操作中,我得到 dispatchgetState,我可以调用它们来检索当前状态(或偏移量)。但是,我在这里没有使用 redux。我想访问作为 eventStream 的一部分传递给 reducer 的状态。这有意义吗?
    • 我认为这是我们在这种情况下能做的最好的事情。我上面的代码只是为了掌握 rxJs。我知道在现实生活中这可能不会以这种方式完成。感谢您的意见。
    猜你喜欢
    • 2019-12-10
    • 1970-01-01
    • 2020-06-11
    • 2018-03-16
    • 2021-01-11
    • 2021-08-29
    • 2019-10-18
    • 2016-05-17
    • 2017-06-11
    相关资源
    最近更新 更多