我正在学习Cycle.JS并遇到挑战。我有一个组件将从HTTP调用中获得结果,并且我希望在indexDB中保留此响应。但是,我觉得持久性请求是另一个组件的责任。
我的问题是:
以下是进行HTTP调用的组件:
import { Feed } from './feed'
export function RssList ({HTTP, props}, feedAdapter = x => x) {
const request$ = props.url$
.map(url => ({
url: url,
method: 'GET',
category: 'rss'
}))
const response$ = HTTP
.select('rss')
.flatten()
.map(feedAdapter)
const vDom$ = response$
.map(Feed)
.startWith('')
return {
DOM: vDom$,
HTTP: request$
}
}
以下是我在应用级别访问响应的尝试:
export function main (sources) {
const urlSource = url$(sources)
const rssSink = rss$(sources, urlSource.value)
const vDom$ = xs.combine(urlSource.DOM, rssSink.DOM)
.map(([urlInput, rssList]) =>
<div>
{urlInput}
{rssList}
</div>
)
sources.HTTP.select('rss').flatten().debug() // nothing happens here
return {
DOM: vDom$,
HTTP: rssSink.HTTP
}
}
答案 0 :(得分:2)
选择主(父)组件中的类别是正确的方法,并且受支持。
sources.HTTP.select('rss').flatten().debug()
没有记录任何内容的唯一原因是因为debug
的工作原理并非如此。它不会“订阅”流并创建副作用。 debug
基本上类似于map
运算符,它使用标识函数(始终将x
作为输入并输出x
),但将日志记录操作作为副作用。因此,您需要将.debug()
替换为.addListener({next: x => console.log(x)})
,或者使用.debug()
输出的流,并将其与用于接收器的运算符管道挂钩。换句话说,debug
是中间日志记录副作用,而不是目标日志记录副作用。
答案 1 :(得分:1)
问题#1:自定义HTTP-&gt; IDB驱动程序:这取决于项目的性质,对于一个简单的例子,我使用了一般CycleJS IDB Driver。请参阅下面的示例或codesandbox.io example.
问题2:组件共享流:由于组件和main
共享相同的源/接收器API,您可以将一个组件的输出(sink
)链接到输入(source
)另一个。请参阅下面的示例或codesandbox.io example.
问题#3:debug
和记录:由于权威的literally)André Staltz指出debug
需要插入已完成的流中循环,IE,已经订阅/收听的流。
在您的示例中,您可以将debug
放入RssList
组件中:
const response$ = HTTP
.select('rss')
.flatten()
.map(feedAdapter)
.debug()
或者为main
示例添加一个监听器:
sources.HTTP.select('rss').flatten().debug()
.addListener({next: x => console.log(x)})
或者,我喜欢做的是,包括一个日志驱动程序:
run(main, {
DOM: makeDOMDriver('#app'),
HTTP: makeHTTPDriver(),
log: log$ => log$.addListener({next: log => console.log(log)}),
})
然后,我只是复制一个流并将其发送到log
接收器:
const url$ = props.url
const http$ = url$.map(url => ({url: url, method: 'GET', category: 'rss'}))
const log$ = url$
return {
DOM: vdom$,
HTTP: http$,
log: log$,
}
这里有一些示例代码,用于使用两个共享数据的组件和一般的IndexedDB驱动程序向IndexedDB存储发送HTTP响应:
function main(sources) {
const header$ = xs.of(div('RSS Feed:'))
const rssSink = RssList(sources) // input HTTP select and props
// output VDOM and data for IDB storage
const vDom$ = xs.combine(header$, rssSink.DOM) // build VDOM
.map(([header, rssList]) => div([header, rssList])
)
const idbSink = IdbSink(sources, rssSink.IDB) // output store and put HTTP response
return {
DOM: vDom$,
HTTP: rssSink.HTTP, // send HTTP request
IDB: idbSink.put, // send response to IDB store
log: idbSink.get, // get and log data stored in IDB
}
}
function RssList({ HTTP, props }, feedAdapter = x => x) {
const request$ = props.url$
.map(url => ({url: url, method: 'GET', category: 'rss'}))
const response$ = HTTP.select('rss').flatten().map(feedAdapter)
const idb$ = response$
const vDom$ = response$
.map(Feed)
.startWith(div('','...loading'))
return {
DOM: vDom$,
HTTP: request$,
IDB: { response: idb$ },
}
}
function Feed (feed) {
return div('> ' + feed)
}
function IdbSink(sources, idb) {
return {
get: sources.IDB.store('rss').getAll()
.map(obj => (obj['0'] && obj['0'].feed) || 'unknown'),
put: idb.response
.map(feedinfo => $put('rss', { feed: feedinfo }))
}
}
run(main, {
props: () => ({ url$: xs.of('http://lorem-rss.herokuapp.com/feed') }),
DOM: makeDOMDriver('#root'),
HTTP: makeHTTPDriver(),
IDB: makeIdbDriver('rss-db', 1, upgradeDb => {
upgradeDb.createObjectStore('rss', { keyPath: 'feed' })
}),
log: log$ => log$.addListener({next: log => console.log(log)}),
})
这是一个人为的例子,只是为了探讨所提出的问题。 Codesandbox.io example.