0

我正在学习 Cycle.JS 并遇到了挑战。我有一个组件将从 HTTP 调用中获得结果,我想将此响应保存在 indexDB 中。但是,我觉得持久性的请求是另一个组件的责任。

我的问题是:

  1. 这是将 HTTP 响应持久化到 indexDB 的自定义驱动程序的用例吗?
  2. 另一个组件如何访问它没有发出的请求的响应流?
  3. 当我尝试从 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
  }
}
4

2 回答 2

2

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

sources.HTTP.select('rss').flatten().debug()不记录任何内容的唯一原因是因为这不是debug工作方式。它不会“订阅”流并产生副作用。debug本质上就像一个map使用恒等函数的运算符(总是x作为输入和输出x),但有一个日志操作作为副作用。因此,您需要替换.debug().addListener({next: x => console.log(x)})使用.debug()输出的流,并将其与进入接收器的操作员管道挂钩。换句话说,debug中间的日志记录副作用,而不是目标日志记录的副作用。

于 2018-04-16T08:57:27.023 回答
1

Question #1: Custom HTTP->IDB Driver: It depends on the nature of the project, for a simple example I used a general CycleJS IDB Driver. See example below or codesandbox.io example.

Question #2: Components Sharing Streams: Since components and main share the same source/sink API you can link the output (sink) of one component to the input (source) of another. See example below or codesandbox.io example.

Question #3: debug and Logging: As the authoritative (literally) André Staltz pointed out debug needs to be inserted into a completed stream cycle, I.E., an already subscribed/listened stream.

In your example you can put debug in your RssList component:

const response$ = HTTP
  .select('rss')
  .flatten()
  .map(feedAdapter)
  .debug()

OR add a listener to your main example:

sources.HTTP.select('rss').flatten().debug()
  .addListener({next: x => console.log(x)})

OR, what I like to do, is include a log driver:

run(main, {
    DOM: makeDOMDriver('#app'),
    HTTP: makeHTTPDriver(),
    log: log$ => log$.addListener({next: log => console.log(log)}),
})

Then I'll just duplicate a stream and send it to the log sink:

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$,
}

Here's some example code for sending HTTP response to IndexedDB storage, using two components that share the data and a general IndexedDB driver:

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)}),
})

This is a contrived example, simply to explore the issues raised. Codesandbox.io example.

于 2018-04-17T01:16:31.800 回答