Skip to content

Instantly share code, notes, and snippets.

@jvanbruegge
Created August 30, 2017 22:19
Show Gist options
  • Save jvanbruegge/0b31b83b9acda1c1ff3e6f36d7f69303 to your computer and use it in GitHub Desktop.
Save jvanbruegge/0b31b83b9acda1c1ff3e6f36d7f69303 to your computer and use it in GitHub Desktop.
onionify-collection, no initial state
import xs, { Stream } from 'xstream';
import { VNode, DOMSource } from '@cycle/dom';
import { StateSource } from 'cycle-onionify';
import { Sources, Sinks } from './interfaces';
import { Counter, AppState as ChildState } from './counter'
export type AppSources = Sources & { onion : StateSource<AppState> };
export type AppSinks = Sinks & { onion : Stream<Reducer> };
export type Reducer = (prev : AppState) => AppState;
export type AppState = {
counters: ChildState[];
};
export function App(sources : AppSources) : AppSinks
{
const action$ : Stream<Reducer> = intent(sources.DOM);
const collection = sources.onion
.select<ChildState[]>('counters')
.toCollection(Counter)
.isolateEach(key => String(key))
.build(sources);
const childVdom$ : Stream<VNode[]> = collection.pickCombine('DOM');
const vdom$ : Stream<VNode> = view(childVdom$);
return {
DOM: vdom$,
onion: xs.merge(action$, collection.pickMerge('onion'))
};
}
function intent(DOM : DOMSource) : Stream<Reducer>
{
const init$ : Stream<Reducer> = xs.of<Reducer>(() => ({ counters: [
{ count: 5 }
] }));
const add$ : Stream<Reducer> = DOM.select('.add').events('click')
.mapTo<Reducer>(state => ({ ...state, counters: state.counters.concat([undefined as any]) }));
const subtract$ : Stream<Reducer> = DOM.select('.subtract').events('click')
.mapTo<Reducer>(state => ({ ...state, counters: state.counters.slice(0, -1) }));
return xs.merge(init$, add$, subtract$);
}
function view(childDOM$: Stream<VNode[]>) : Stream<VNode>
{
return childDOM$
.map(arr =>
<div>
<h2>My Awesome Cycle.js app</h2>
<span>Counters:</span>
{ arr }
<button type='button' className='add'>Add</button>
<button type='button' className='subtract'>Remove</button>
</div>
);
}
import xs, { Stream } from 'xstream';
import { VNode, DOMSource } from '@cycle/dom';
import { StateSource } from 'cycle-onionify';
import { Sources, Sinks } from './interfaces';
export type AppSources = Sources & { onion : StateSource<AppState> };
export type AppSinks = Sinks & { onion : Stream<Reducer> };
export type Reducer = (prev : AppState) => AppState;
export type AppState = {
count : number;
};
export function Counter(sources : AppSources) : AppSinks
{
const action$ : Stream<Reducer> = intent(sources.DOM);
const vdom$ : Stream<VNode> = view(sources.onion.state$);
return {
DOM: vdom$,
onion: action$
};
}
function intent(DOM : DOMSource) : Stream<Reducer>
{
const init$ : Stream<Reducer> = xs.of<Reducer>(prev => prev === undefined ? { count: 0 } : prev);
const add$ : Stream<Reducer> = DOM.select('.add').events('click')
.mapTo<Reducer>(state => ({ ...state, count: state.count + 1 }));
const subtract$ : Stream<Reducer> = DOM.select('.subtract').events('click')
.mapTo<Reducer>(state => ({ ...state, count: state.count - 1 }));
return xs.merge(init$, add$, subtract$);
}
function view(state$ : Stream<AppState>) : Stream<VNode>
{
return state$
.map(s => s.count)
.map(count =>
<div>
<span>{ 'Counter: ' + count }</span>
<button type='button' className='add'>Increase</button>
<button type='button' className='subtract'>Decrease</button>
</div>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment