From 53853d3fa9fb52f1fe13a7a0a8cb66faa048dd40 Mon Sep 17 00:00:00 2001 From: Tomek Date: Sun, 22 Jul 2018 01:37:59 +0200 Subject: [PATCH] update with-redux-observable example (#4818) --- examples/with-redux-observable/README.md | 9 +++--- examples/with-redux-observable/package.json | 12 ++++---- examples/with-redux-observable/pages/_app.js | 28 +++++++++++++++++++ examples/with-redux-observable/pages/index.js | 12 ++++---- examples/with-redux-observable/redux/epics.js | 25 ++++++++--------- examples/with-redux-observable/redux/index.js | 10 ++++--- 6 files changed, 62 insertions(+), 34 deletions(-) create mode 100644 examples/with-redux-observable/pages/_app.js diff --git a/examples/with-redux-observable/README.md b/examples/with-redux-observable/README.md index dddd34b9..e985dfca 100644 --- a/examples/with-redux-observable/README.md +++ b/examples/with-redux-observable/README.md @@ -47,13 +47,14 @@ probably making initial requests on a server. That's because, the `getInitialProps` hook runs on the server-side before epics have been made available by just dispatching actions. However, we can access and execute epics directly. In order to do so, we need to -pass them an Observable of an action and they will return an Observable: +pass them an Observable of an action together with StateObservable and they will return an Observable: ```js static async getInitialProps({ store, isServer }) { + const state$ = new StateObservable(new Subject(), store.getState()); const resultAction = await rootEpic( of(actions.fetchCharacter(isServer)), - store + state$ ).toPromise(); // we need to convert Observable to Promise store.dispatch(resultAction)}; ``` @@ -61,8 +62,8 @@ static async getInitialProps({ store, isServer }) { Note: we are not using `AjaxObservable` from the `rxjs` library; as of rxjs v5.5.6, it will not work on both the server- and client-side. Instead we call the default export from -[universal-rx-request](https://www.npmjs.com/package/universal-rx-request) (as -`ajax`). +[universal-rxjs-ajax](https://www.npmjs.com/package/universal-rxjs-ajax) (as +`request`). We transform the Observable we get from `ajax` into a Promise in order to await its resolution. That resolution should be a action (since the epic returns diff --git a/examples/with-redux-observable/package.json b/examples/with-redux-observable/package.json index 8883d789..4b3b086e 100644 --- a/examples/with-redux-observable/package.json +++ b/examples/with-redux-observable/package.json @@ -9,17 +9,15 @@ "author": "tomaszmularczyk(tomasz.mularczyk89@gmail.com)", "dependencies": { "next": "latest", - "next-redux-wrapper": "^1.0.0", + "next-redux-wrapper": "^2.0.0-beta.6", "react": "^16.0.0", "react-dom": "^16.0.0", "react-redux": "^5.0.1", - "redux": "^3.6.0", + "redux": "^4.0.0", "redux-logger": "^3.0.6", - "redux-observable": "^0.17.0", - "redux-thunk": "^2.1.0", - "rxjs": "^5.5.2", - "superagent": "^3.8.1", - "universal-rx-request": "^1.0.3" + "redux-observable": "^1.0.0", + "rxjs": "^6.2.1", + "universal-rxjs-ajax": "^2.0.0" }, "license": "ISC" } diff --git a/examples/with-redux-observable/pages/_app.js b/examples/with-redux-observable/pages/_app.js new file mode 100644 index 00000000..6b270a88 --- /dev/null +++ b/examples/with-redux-observable/pages/_app.js @@ -0,0 +1,28 @@ +import React from 'react' +import { Provider } from 'react-redux' +import App, { Container } from 'next/app' +import withRedux from 'next-redux-wrapper' +import makeStore from '../redux' + +class MyApp extends App { + static async getInitialProps ({ Component, ctx }) { + const pageProps = Component.getInitialProps + ? await Component.getInitialProps(ctx) + : {} + + return { pageProps } + } + + render () { + const { Component, pageProps, store } = this.props + return ( + + + + + + ) + } +} + +export default withRedux(makeStore)(MyApp) diff --git a/examples/with-redux-observable/pages/index.js b/examples/with-redux-observable/pages/index.js index bb612f1d..1363f344 100644 --- a/examples/with-redux-observable/pages/index.js +++ b/examples/with-redux-observable/pages/index.js @@ -1,17 +1,18 @@ import React from 'react' import Link from 'next/link' -import withRedux from 'next-redux-wrapper' -import initStore from '../redux' +import { of, Subject } from 'rxjs' +import { StateObservable } from 'redux-observable' +import { connect } from 'react-redux' import CharacterInfo from '../components/CharacterInfo' import { rootEpic } from '../redux/epics' import * as actions from '../redux/actions' -import { of } from 'rxjs/observable/of' class Counter extends React.Component { static async getInitialProps ({ store, isServer }) { + const state$ = new StateObservable(new Subject(), store.getState()) const resultAction = await rootEpic( of(actions.fetchCharacter(isServer)), - store + state$ ).toPromise() // we need to convert Observable to Promise store.dispatch(resultAction) @@ -40,8 +41,7 @@ class Counter extends React.Component { } } -export default withRedux( - initStore, +export default connect( null, { startFetchingCharacters: actions.startFetchingCharacters, diff --git a/examples/with-redux-observable/redux/epics.js b/examples/with-redux-observable/redux/epics.js index f2444321..3daaea10 100644 --- a/examples/with-redux-observable/redux/epics.js +++ b/examples/with-redux-observable/redux/epics.js @@ -1,20 +1,19 @@ -import { interval } from 'rxjs/observable/interval' -import { of } from 'rxjs/observable/of' +import { interval, of } from 'rxjs' import { takeUntil, mergeMap, catchError, map } from 'rxjs/operators' import { combineEpics, ofType } from 'redux-observable' -import ajax from 'universal-rx-request' // because standard AjaxObservable only works in browser +import { request } from 'universal-rxjs-ajax' // because standard AjaxObservable only works in browser import * as actions from './actions' import * as types from './actionTypes' -export const fetchUserEpic = (action$, store) => +export const fetchUserEpic = (action$, state$) => action$.pipe( ofType(types.START_FETCHING_CHARACTERS), mergeMap(action => { return interval(3000).pipe( - mergeMap(x => + map(x => actions.fetchCharacter({ - isServer: store.getState().isServer + isServer: state$.value.isServer }) ), takeUntil(action$.ofType(types.STOP_FETCHING_CHARACTERS)) @@ -22,24 +21,24 @@ export const fetchUserEpic = (action$, store) => }) ) -export const fetchCharacterEpic = (action$, store) => +export const fetchCharacterEpic = (action$, state$) => action$.pipe( ofType(types.FETCH_CHARACTER), mergeMap(action => - ajax({ - url: `https://swapi.co/api/people/${store.getState().nextCharacterId}` + request({ + url: `https://swapi.co/api/people/${state$.value.nextCharacterId}` }).pipe( map(response => actions.fetchCharacterSuccess( - response.body, - store.getState().isServer + response.response, + state$.value.isServer ) ), catchError(error => of( actions.fetchCharacterFailure( - error.response.body, - store.getState().isServer + error.xhr.response, + state$.value.isServer ) ) ) diff --git a/examples/with-redux-observable/redux/index.js b/examples/with-redux-observable/redux/index.js index c514dd72..02a72f3c 100644 --- a/examples/with-redux-observable/redux/index.js +++ b/examples/with-redux-observable/redux/index.js @@ -1,14 +1,16 @@ import { createStore, applyMiddleware } from 'redux' -import thunkMiddleware from 'redux-thunk' import { createLogger } from 'redux-logger' import { createEpicMiddleware } from 'redux-observable' import reducer from './reducer' import { rootEpic } from './epics' export default function initStore (initialState) { - const epicMiddleware = createEpicMiddleware(rootEpic) + const epicMiddleware = createEpicMiddleware() const logger = createLogger({ collapsed: true }) // log every action to see what's happening behind the scenes. - const reduxMiddleware = applyMiddleware(thunkMiddleware, epicMiddleware, logger) + const reduxMiddleware = applyMiddleware(epicMiddleware, logger) - return createStore(reducer, initialState, reduxMiddleware) + const store = createStore(reducer, initialState, reduxMiddleware) + epicMiddleware.run(rootEpic) + + return store };