我正在与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)
}
我无法测试我的建议,因此我不确定是否有错误。