使用 Cycle.js 存储对 indexedDB 的 REST 响应

Storing REST response to indexedDB with Cycle.js

我正在学习 Cycle.JS 和 运行 挑战。我有一个将从 HTTP 调用获得结果的组件,我想将此响应保存在 indexDB 中。不过我觉得请求持久化是另外一个组件的职责。

我的问题是:

  1. 这是将 HTTP 响应保存到 indexDB 的自定义驱动程序的用例吗?
  2. 另一个组件如何访问它没有发出的请求的响应流?
  3. 当我尝试 select 来自 HTTP 源的类别时,没有任何内容记录到控制台。我正在使用 xstream,所以流应该很热,我希望调试输出。这是怎么回事?

下面是我进行 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
  }
}

在主(父)组件中选择类别是正确的做法,并且受到支持。

sources.HTTP.select('rss').flatten().debug() 不记录任何内容的唯一原因是 debug 不是这样工作的。它不会 "subscribe" 到流中并产生副作用。 debug 本质上类似于使用恒等函数的 map 运算符(始终将 x 作为输入并输出 x),但具有日志记录操作作为副作用。因此,您要么需要将 .debug() 替换为 .addListener({next: x => console.log(x)}),要么使用 .debug() 输出的流并将其与进入接收器的运算符管道挂钩。换句话说,debugin-between 日志记录副作用,而不是 destination 日志记录副作用。

问题 #1:自定义 HTTP->IDB 驱动程序:这取决于项目的性质,举个简单的例子我使用了一个通用的 CycleJS IDB Driver. See example below or codesandbox.io example.

问题 #2:组件共享流:由于组件和 main 共享相同的 source/sink API 您可以 link 输出 (sink)一个组件到另一个组件的输入 (source)。请参阅下面的示例或 codesandbox.io example.

问题 #3:debug 和日志记录:正如权威人士(literally) André Staltz 指出 debug 需要插入到一个完整的流循环中,即,已经 subscribed/listened流。

在您的示例中,您可以将 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$,
}

下面是一些将 HTTP 响应发送到 IndexedDB 存储的示例代码,使用两个共享数据的组件和一个通用的 IndexedDB 驱动程序:

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.