With redux-observable, we have the power of RxJS at our disposal - this means tasks that would otherwise be complicated and imperative, become simple and declarative. In this lesson we’ll respond to an action by queuing up 2 separate Ajax requests that will execute sequentially. Then we’ll group the results from both into an array and produce a single action from our epic that will save the data into the redux store
// action export const FETCH_STORIES = 'FETCH_STORIES'; export const FETCH_STORIES_FULFILLED = 'FETCH_STORIES_FULFILLED'; export function fetchStoriesAction(count = 5) { return { type: FETCH_STORIES, payload: count } } export function fetchStoriesFulfilledAction(stories) { return { type: FETCH_STORIES_FULFILLED, payload: stories } }
// epics import {Observable} from 'rxjs'; import {combineEpics} from 'redux-observable'; import {FETCH_STORIES, fetchStoriesFulfilledAction} from "../actions/index"; const topStories = `https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty`; const url = (id) => `https://hacker-news.firebaseio.com/v0/item/${id}.json?print=pretty`; function fetchStoriesEpic(action$) { return action$.ofType(FETCH_STORIES) .switchMap(({payload}) => { return Observable.ajax.getJSON(topStories) // slice first 5 ids .map(ids => ids.slice(0, 5)) // convert ids -> urls .map(ids => ids.map(url)) // convert urls -> ajax .map(urls => urls.map(url => Observable.ajax.getJSON(url))) // execute 5 ajax requests .mergeMap(reqs => Observable.forkJoin(reqs)) // results -> store .map(stories => fetchStoriesFulfilledAction(stories)) }) } export const rootEpic = combineEpics(fetchStoriesEpic);
import {FETCH_STORIES, FETCH_STORIES_FULFILLED} from "../actions/index"; const initialState = { stories: [], loading: false, }; export function storiesReducer(state = initialState, action) { switch(action.type) { case FETCH_STORIES: return { stories: [], loading: true }; case FETCH_STORIES_FULFILLED: return { stories: action.payload, loading: false }; default: return state; } } export default storiesReducer;
// component import React from 'react'; import {connect} from "react-redux"; import {fetchStoriesAction} from "../actions/index"; export function Stories(props) { if (props.loading) { return ( <p>Please wait...</p> ) } return ( <div> <button type="button" onClick={props.loadStories}>Load top 5 stories</button> <StoryList stories={props.stories} /> </div> ) } function StoryList(props) { return ( <ul> {props.stories.map(story => <li key={story.id}> <a href={story.url}>{story.title}</a> </li> )} </ul> ); } function mapState(state) { return state; } function mapDispatch(dispatch) { return { loadStories: () => dispatch(fetchStoriesAction()) } } export default connect(mapState, mapDispatch)(Stories);