Switchオペレーターのドキュメントにはこのような説明がある。
convert an Observable that emits Observables into a single Observable that emits the items emitted by the most-recently-emitted of those Observables
絵で表すとわかりやすくなる
import { TestScheduler } from 'rxjs';
const scheduler = new TestScheduler(null);
const observable
= scheduler.createHotObservable('---a---b---c---|');
observable.subscribe(x => console.log(x));
scheduler.flush();
import { TestScheduler } from 'rxjs';
const scheduler = new TestScheduler(null);
const observable
= scheduler.createHotObservable('---a---b---c---|', { a: 10, b: 20, c: 30 });
observable.subscribe(x => console.log(x)); // 10 20 30
scheduler.flush();
import { TestScheduler } from 'rxjs';
const scheduler = new TestScheduler(null);
const observable
= scheduler.createHotObservable('---#', null, 'hoge');
observable.subscribe(null, err => console.log(err))
scheduler.flush(); // hoge
- "-" 何も発火せずに10フレームを過ぎる
- "|" completeが発火される
- "#" エラーを出す
- "a-z" nextが発火される
- "(ab)" 同じフレームに発火される
- "^" subscribeされたフレーム(Hot Observableのみ)
import test from 'ava';
import { TestScheduler } from 'rxjs';
test('merge', (t) => {
const scheduler = new TestScheduler(t.deepEqual.bind(t));
const e1 = scheduler.createHotObservable('-a----b|');
const e2 = scheduler.createHotObservable('-c-----d|');
const expected = '-(ac)-bd|';
scheduler.expectObservable(e1.merge(e2)).toBe(expected);
scheduler.flush();
});
test('map', (t) => {
const scheduler = new TestScheduler(t.deepEqual.bind(t));
const values = {
a: { userId: 'a' },
b: { userId: 'b' },
c: { userId: 'c' },
x: 'a',
y: 'b',
z: 'c',
};
const e1 = scheduler.createHotObservable('-a--b--c|', values);
const expected = '-x--y--z|';
scheduler.expectObservable(e1.map(v => v.userId)).toBe(expected, values);
scheduler.flush();
});
test('filter', (t) => {
const scheduler = new TestScheduler(t.deepEqual.bind(t));
const e1 = scheduler.createHotObservable('-a--b--aa-b|');
const expected = '----b-----b|';
scheduler.expectObservable(e1.filter(v => v !== 'a')).toBe(expected);
scheduler.flush();
});
function model(query$, COUNTRIES) {
return query$
.map((query) => {
if (query.length < 2) {
return { countries: [] };
}
const countries = COUNTRIES.filter(
country => country.toLowerCase().indexOf(query.toLowerCase()) === 0,
);
return {
countries,
};
})
.startWith({ countries: [] });
}
export default model;
シンプルなアプリケーションなので、一つのObservableでステートを管理できる。
componentDidMount() {
if (this.textInput) {
const query$ = Observable
.fromEvent(this.textInput, 'keyup')
.debounceTime(300)
.map(e => e.target.value);
this.state$ = model(
query$,
COUNTRIES,
);
this.state$.subscribe(this.setState.bind(this));
}
}
import test from 'ava';
import { TestScheduler } from 'rxjs';
import model from './model';
const COUNTRIES = [
'Japan',
'China',
'Sweden',
'Switzerland',
'United States',
'Germany',
'Egypt',
];
test('model', (t) => {
const scheduler = new TestScheduler(t.deepEqual.bind(t));
const values = {
a: '',
b: 'hoge',
c: 's',
d: 'sw',
e: 'ja',
f: { countries: [] },
g: { countries: ['Sweden', 'Switzerland'] },
h: { countries: ['Japan'] },
};
const e1 = scheduler.createHotObservable('--a--b-c--d--e--|', values);
const expected = 'f-f--f-f--g--h--|';
scheduler.expectObservable(model(e1, COUNTRIES)).toBe(expected, values);
scheduler.flush();
});
ご清聴ありがとうございました!