diff --git a/src/App.js b/src/App.js
index ea8571b..329221e 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,7 +1,15 @@
import React from 'react';
import axios from 'axios';
-import { BehaviorSubject, combineLatest, timer } from 'rxjs';
-import { flatMap, map, debounce, filter } from 'rxjs/operators';
+import { BehaviorSubject, combineLatest } from 'rxjs';
+import {
+ map,
+ filter,
+ startWith,
+ debounceTime,
+ distinctUntilChanged,
+ switchMap,
+ catchError,
+} from 'rxjs/operators';
import withObservableStream from './withObservableStream';
@@ -59,35 +67,34 @@ const query$ = new BehaviorSubject('react');
const subject$ = new BehaviorSubject(SUBJECT.POPULARITY);
const queryForFetch$ = query$.pipe(
- debounce(() => timer(1000)),
- filter(query => query !== ''),
+ debounceTime(1000),
+ distinctUntilChanged(),
+ filter(Boolean),
);
const fetch$ = combineLatest(subject$, queryForFetch$).pipe(
- flatMap(([subject, query]) =>
- axios(`http://hn.algolia.com/api/v1/${subject}?query=${query}`),
+ switchMap(
+ // discard previous requests. Response order not granted.
+ ([subject, query]) =>
+ axios(`http://hn.algolia.com/api/v1/${subject}?query=${query}`),
),
map(result => result.data.hits),
+ startWith([]),
+ catchError(() => []),
);
-export default withObservableStream(
- combineLatest(
- subject$,
- query$,
- fetch$,
- (subject, query, stories) => ({
- subject,
- query,
- stories,
- }),
- ),
- {
- onSelectSubject: subject => subject$.next(subject),
- onChangeQuery: value => query$.next(value),
- },
- {
- query: 'react',
- subject: SUBJECT.POPULARITY,
- stories: [],
- },
-)(App);
+const state$ = combineLatest(
+ subject$,
+ query$,
+ fetch$,
+ (subject, query, stories) => ({
+ subject,
+ query,
+ stories,
+ }),
+);
+
+export default withObservableStream(state$, {
+ onSelectSubject: subject => subject$.next(subject),
+ onChangeQuery: value => query$.next(value),
+})(App);
diff --git a/src/withObservableStream.js b/src/withObservableStream.js
index 6cf9c96..db9b276 100644
--- a/src/withObservableStream.js
+++ b/src/withObservableStream.js
@@ -1,19 +1,26 @@
-import React from 'react';
+import React, { Component } from 'react';
+import { skip, first, shareReplay } from 'rxjs/operators';
-export default (observable, triggers, initialState) => Component => {
- return class extends React.Component {
+export default (observable, triggers) => InnerComponent => {
+ class Decorated extends Component {
constructor(props) {
super(props);
+ this.sharedObservable = observable.pipe(shareReplay(1));
- this.state = {
- ...initialState,
- };
+ const initializationSubscription = this.sharedObservable
+ .pipe(first())
+ .subscribe(initialState => {
+ this.state = initialState;
+ });
+ initializationSubscription.unsubscribe();
}
componentDidMount() {
- this.subscription = observable.subscribe(newState =>
- this.setState({ ...newState }),
- );
+ this.subscription = this.sharedObservable
+ .pipe(skip(1))
+ .subscribe(newState => {
+ this.setState(newState);
+ });
}
componentWillUnmount() {
@@ -22,8 +29,13 @@ export default (observable, triggers, initialState) => Component => {
render() {
return (
-
+
);
}
- };
+ }
+
+ Decorated.displayName = `withObservableStream(${InnerComponent.displayName ||
+ InnerComponent.name})`;
+
+ return Decorated;
};