2015-11-19 10 views
8

Jestem nowy w reaktywnym programowaniu i bawieniu się z plikiem cycle.js, próbując zaimplementować pole, z którego należy obserwować od this tutorial. Ale zrozumiałem, że dla prawidłowej implementacji (i celów uczenia się) nie mam jednego kawałka danych: pełnej nazwy użytkownika. Mogę to uzyskać, kolejno pobierając użytkowników, a następnie pełne dane użytkowników z serwera. W stylu imperatywu zrobiłbym coś takiego:Jak przesłać dane sekwencyjnie w Cycle.js?

fetch(`https://api.github.com/users`) 
    .then(data => data.json()) 
    .then(users => fetch(users[0].url)) 
    .then(data => data.json()) 
    .then(/* ... work with data ... */) 

Ale jak zrobić to w cyklu? Używam pobrać sterownik i próbuje coś takiego:

function main({ DOM, HTTP }) { 
    const users = `https://api.github.com/users`; 

    const refresh$ = DOM.select(`.refresh`).events(`click`) 

    const response$ = getJSON({ key: `users` }, HTTP) 

    const userUrl$ = response$ 
    .map(users => ({ 
     url: R.prop(`url`, R.head(users)), 
     key: `user`, 
    })) 
    .startWith(null) 

    const request$ = refresh$ 
    .startWith(`initial`) 
    .map(_ => ({ 
     url: `${users}?since=${random(500)}`, 
     key: `users`, 
    })) 
    .merge(userUrl$) 

    const dom$ = ... 

    return { 
    DOM: dom$, 
    HTTP: request$, 
    }; 
} 

gdzie getJSON jest

function getJSON(by, requests$) { 
    const type = capitalize(firstKey(by)); 

    return requests$ 
    [`by${type}`](firstVal(by)) 
    .mergeAll() 
    .flatMap(res => res.json()); 

A ja zawsze się jakiś tajemniczy (dla mnie) Błąd jak: TypeError: Already read. Co to znaczy i jak sobie z tym poradzić?

Odpowiedz

9

Byłeś bardzo blisko. Musisz tylko usunąć startWith(null) jako żądanie i pobrać drugą odpowiedź (brakowało ci getJSON dla tego).

function main({ DOM, HTTP }) { 
    const usersAPIPath = `https://api.github.com/users`; 

    const refresh$ = DOM.select(`.refresh`).events(`click`); 

    const userResponse$ = getJSON({ key: `user` }, HTTP); 
    const listResponse$ = getJSON({ key: `users` }, HTTP); 

    const userRequest$ = listResponse$ 
    .map(users => ({ 
     url: R.prop(`url`, R.head(users)), 
     key: `user`, 
    })); 

    const listRequest$ = refresh$ 
    .startWith(`initial`) 
    .map(_ => ({ 
     url: `${usersAPIPath}?since=${Math.round(Math.random()*500)}`, 
     key: `users`, 
    })); 

    const dom$ = userResponse$.map(res => h('div', JSON.stringify(res))); 

    return { 
    DOM: dom$, 
    HTTP: listRequest$.merge(userRequest$), 
    }; 
} 
+0

Dzięki, to działa! Ale nadal wydaje mi się to trochę magiczne. Czy rozumiem poprawnie, że 'userRequest $' tylko czeka, aż 'listResponse $' wyemituje cokolwiek, a następnie zrobi to, co będzie dalej w łańcuchu? – kibin

+1

Prawidłowo. Mapuje zdarzenie z 'listResponse $' do nowego żądania. –

+0

Zrozumiano. Jeszcze raz dziękuję, bardzo lubię cycle.js i nadal będę się w to zagłębiał. – kibin

0

Ponieważ dociekliwych umysły chcą wiedzieć ... tutaj jest pełna przykładów pracy:

import Cycle from '@cycle/rx-run'; 
import {div, button, makeDOMDriver} from '@cycle/dom'; 
import {makeFetchDriver} from '@cycle/fetch'; 
import R from 'ramda' 

function main({DOM, HTTP}) { 

    const usersAPIPath = 'https://api.github.com/users'; 
    const refresh$ = DOM.select('button').events('click'); 

    const userResponse$ = getJSON({ key: 'user' }, HTTP); 
    const listResponse$ = getJSON({ key: 'users' }, HTTP); 

    const userRequest$ = listResponse$ 
    .map(users => ({ 
     url: R.prop('url', R.head(users)), 
     key: 'user', 
    })); 

    const listRequest$ = refresh$ 
    .startWith('initial') 
    .map(_ => ({ 
     url: `${usersAPIPath}?since=${Math.round(Math.random()*500)}`, 
     key: 'users', 
    })); 

    const dom$ = userResponse$.map(res => div([ 
    button('Refresh'), 
    div(JSON.stringify(res)) 
    ])); 

    return { 
    DOM: dom$, 
    HTTP: listRequest$.merge(userRequest$) 
    }; 

    function getJSON(by, requests$) { 
    return requests$.byKey(by.key) 
     .mergeAll() 
     .flatMap(res => res.json()); 
    } 
} 

Cycle.run(main, { 
    DOM: makeDOMDriver('#main-container'), 
    HTTP: makeFetchDriver() 
}); 

Zajęło mi trochę czasu, aby dowiedzieć się, HTTP był kierowcą @cycle/fetch, a nie kierowca @cycle/http. Następnie trochę wyszukiwania zmieniło bibliotekę NPM ramda, zapewniając metody prop i head.

Powiązane problemy