Сохранение ответа REST для indexedDB с помощью Cycle.js
Я в процессе изучения Cycle.JS и столкнулся с проблемой. У меня есть компонент, который получит результат от HTTP-вызова, и я хотел бы сохранить этот ответ в indexDB. Однако я чувствую, что запрос на постоянство является обязанностью другого компонента.
У меня есть следующие вопросы:
- Это вариант использования для пользовательского драйвера, который сохраняет HTTP-ответы на indexDB?
- Как другой компонент получает доступ к потоку ответов на запрос, который он не сделал?
- Когда я пытаюсь выбрать категорию из источника 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
}
}
2 ответа
Выбор категории в основном (родительском) компоненте является правильным подходом и поддерживается.
Единственная причина, почему sources.HTTP.select('rss').flatten().debug()
ничего не регистрируется, потому что это не так debug
работает. Он не "подписывается" на поток и создает побочные эффекты. debug
по сути, как map
оператор, который использует функцию тождества (всегда принимает x
в качестве входа и выхода x
), но с регистрацией в качестве побочного эффекта. Так что вам либо нужно заменить .debug()
с .addListener({next: x => console.log(x)})
или использовать поток, который .debug()
выводит и подключает его с конвейером оператора, который идет в приемники. Другими словами, debug
является промежуточным побочным эффектом, а не целевым побочным эффектом.
Вопрос № 1: Пользовательский драйвер HTTP->IDB: Это зависит от характера проекта, для простого примера я использовал общий драйвер CycleJS IDB. См. Пример ниже или код codeandbox.io.
Вопрос № 2: потоки совместного использования компонентов: поскольку компоненты и main
использовать тот же API источника / приемника, с которым вы можете связать вывод (sink
) одного компонента на вход (source
) другого. См. Пример ниже или код codeandbox.io.
Вопрос № 3: debug
и ведение журнала: как отметил авторитетный ( буквально) Андре Штальц 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$,
}
Вот пример кода для отправки ответа 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.