From 8454438c20b8021a3fc0247fea0f5905a667e9a6 Mon Sep 17 00:00:00 2001 From: TodorTotev <51530311+TodorTotev@users.noreply.github.com> Date: Mon, 25 May 2020 19:21:35 +0300 Subject: [PATCH] Refactor with redux saga (#13342) Related to [11014](https://github.com/zeit/next.js/issues/11014) Upgraded all the packages from package.json, removed the saga-wrapper package since it is totally outdated to today's use. I have refactored the whole example using the new API of the next-redux-wrapper package, using their new support of "getStaticProps". All of the class components were converted to functional, using the new redux hooks API. If you want me to change anything or you are not satisfied with any given change, I'm open to suggestions. --- examples/with-redux-saga/actions.js | 1 + .../with-redux-saga/components/counter.js | 54 +++++++------------ examples/with-redux-saga/components/page.js | 18 +++---- examples/with-redux-saga/package.json | 13 +++-- examples/with-redux-saga/pages/_app.js | 28 +++++----- examples/with-redux-saga/pages/index.js | 43 ++++++++------- examples/with-redux-saga/pages/other.js | 31 +++++------ examples/with-redux-saga/reducer.js | 4 ++ examples/with-redux-saga/store.js | 6 ++- 9 files changed, 92 insertions(+), 106 deletions(-) diff --git a/examples/with-redux-saga/actions.js b/examples/with-redux-saga/actions.js index b7edc5240eb33..8e168150db208 100644 --- a/examples/with-redux-saga/actions.js +++ b/examples/with-redux-saga/actions.js @@ -7,6 +7,7 @@ export const actionTypes = { LOAD_DATA_SUCCESS: 'LOAD_DATA_SUCCESS', START_CLOCK: 'START_CLOCK', TICK_CLOCK: 'TICK_CLOCK', + HYDRATE: 'HYDRATE', } export function failure(error) { diff --git a/examples/with-redux-saga/components/counter.js b/examples/with-redux-saga/components/counter.js index ead3739277d9a..114077e28dc8a 100644 --- a/examples/with-redux-saga/components/counter.js +++ b/examples/with-redux-saga/components/counter.js @@ -1,40 +1,26 @@ -import { Component } from 'react' -import { connect } from 'react-redux' +import { useSelector, useDispatch } from 'react-redux' import { increment, decrement, reset } from '../actions' -class Counter extends Component { - increment = () => { - this.props.dispatch(increment()) - } +const Counter = () => { + const count = useSelector((state) => state.count) + const dispatch = useDispatch() - decrement = () => { - this.props.dispatch(decrement()) - } - - reset = () => { - this.props.dispatch(reset()) - } - - render() { - const { count } = this.props - return ( -
- -

- Count: {count} -

- - - -
- ) - } + return ( +
+ +

+ Count: {count} +

+ + + +
+ ) } -const mapStateToProps = ({ count }) => ({ count }) -export default connect(mapStateToProps)(Counter) +export default Counter diff --git a/examples/with-redux-saga/components/page.js b/examples/with-redux-saga/components/page.js index bcc5f3bb6b4b9..72cb187723abe 100644 --- a/examples/with-redux-saga/components/page.js +++ b/examples/with-redux-saga/components/page.js @@ -1,18 +1,14 @@ import Link from 'next/link' -import { connect } from 'react-redux' +import { useSelector } from 'react-redux' import Counter from './counter' import Clock from './clock' -function Page({ - error, - lastUpdate, - light, - linkTo, - NavigateTo, - placeholderData, - title, -}) { +function Page({ linkTo, NavigateTo, title }) { + const placeholderData = useSelector((state) => state.placeholderData) + const error = useSelector((state) => state.error) + const light = useSelector((state) => state.light) + const lastUpdate = useSelector((state) => state.lastUpdate) return (

{title}

@@ -33,4 +29,4 @@ function Page({ ) } -export default connect((state) => state)(Page) +export default Page diff --git a/examples/with-redux-saga/package.json b/examples/with-redux-saga/package.json index 112871ce7ff98..112162b3ca244 100644 --- a/examples/with-redux-saga/package.json +++ b/examples/with-redux-saga/package.json @@ -10,13 +10,12 @@ "dependencies": { "es6-promise": "4.1.1", "next": "latest", - "next-redux-saga": "4.0.0", - "next-redux-wrapper": "2.0.0", - "react": "^16.0.0", - "react-dom": "^16.0.0", - "react-redux": "5.0.7", - "redux": "4.0.1", - "redux-saga": "1.0.1" + "next-redux-wrapper": "6.0.0", + "react": "16.13.1", + "react-dom": "16.13.1", + "react-redux": "7.2.0", + "redux": "4.0.5", + "redux-saga": "1.1.3" }, "devDependencies": { "redux-devtools-extension": "2.13.2" diff --git a/examples/with-redux-saga/pages/_app.js b/examples/with-redux-saga/pages/_app.js index 130e85fe05ca3..356e395f87997 100644 --- a/examples/with-redux-saga/pages/_app.js +++ b/examples/with-redux-saga/pages/_app.js @@ -1,29 +1,27 @@ import App from 'next/app' -import { Provider } from 'react-redux' -import withRedux from 'next-redux-wrapper' -import withReduxSaga from 'next-redux-saga' - -import createStore from '../store' +import { END } from 'redux-saga' +import { wrapper } from '../store' class MyApp extends App { static async getInitialProps({ Component, ctx }) { - let pageProps = {} + const pageProps = { + ...(Component.getInitialProps + ? await Component.getInitialProps(ctx) + : {}), + } - if (Component.getInitialProps) { - pageProps = await Component.getInitialProps({ ctx }) + if (ctx.req) { + ctx.store.dispatch(END) + await ctx.store.sagaTask.toPromise() } return { pageProps } } render() { - const { Component, pageProps, store } = this.props - return ( - - - - ) + const { Component, pageProps } = this.props + return } } -export default withRedux(createStore)(withReduxSaga(MyApp)) +export default wrapper.withRedux(MyApp) diff --git a/examples/with-redux-saga/pages/index.js b/examples/with-redux-saga/pages/index.js index 8464903ca6cfc..135170df5ef84 100644 --- a/examples/with-redux-saga/pages/index.js +++ b/examples/with-redux-saga/pages/index.js @@ -1,28 +1,31 @@ -import { Component } from 'react' -import { connect } from 'react-redux' - +import { useEffect } from 'react' +import { useDispatch } from 'react-redux' +import { END } from 'redux-saga' +import { wrapper } from '../store' import { loadData, startClock, tickClock } from '../actions' import Page from '../components/page' -class Index extends Component { - static async getInitialProps(props) { - const { store, isServer } = props.ctx - store.dispatch(tickClock(isServer)) - - if (!store.getState().placeholderData) { - store.dispatch(loadData()) - } +const Index = () => { + const dispatch = useDispatch() - return { isServer } - } + useEffect(() => { + dispatch(startClock()) + }, [dispatch]) + return ( + <> + + + ) +} - componentDidMount() { - this.props.dispatch(startClock()) - } +export const getStaticProps = wrapper.getStaticProps(async ({ store }) => { + store.dispatch(tickClock(false)) - render() { - return + if (!store.getState().placeholderData) { + store.dispatch(loadData()) + store.dispatch(END) } -} + await store.sagaTask.toPromise() +}) -export default connect()(Index) +export default Index diff --git a/examples/with-redux-saga/pages/other.js b/examples/with-redux-saga/pages/other.js index 17ad54be40fb8..db5176a1690da 100644 --- a/examples/with-redux-saga/pages/other.js +++ b/examples/with-redux-saga/pages/other.js @@ -1,23 +1,20 @@ -import { Component } from 'react' -import { connect } from 'react-redux' - +import { useEffect } from 'react' +import { useDispatch } from 'react-redux' +import { wrapper } from '../store' import { startClock, tickClock } from '../actions' import Page from '../components/page' -class Other extends Component { - static async getInitialProps(props) { - const { store, isServer } = props.ctx - store.dispatch(tickClock(isServer)) - return { isServer } - } - - componentDidMount() { - this.props.dispatch(startClock()) - } +const Other = () => { + const dispatch = useDispatch() + useEffect(() => { + dispatch(startClock()) + }, [dispatch]) - render() { - return - } + return } -export default connect()(Other) +export const getStaticProps = wrapper.getStaticProps(async ({ store }) => { + store.dispatch(tickClock(false)) +}) + +export default Other diff --git a/examples/with-redux-saga/reducer.js b/examples/with-redux-saga/reducer.js index 57418e73ce1a2..bdfd6329f688c 100644 --- a/examples/with-redux-saga/reducer.js +++ b/examples/with-redux-saga/reducer.js @@ -10,6 +10,10 @@ export const exampleInitialState = { function reducer(state = exampleInitialState, action) { switch (action.type) { + case '__NEXT_REDUX_WRAPPER_HYDRATE__': { + return { ...state, ...action.payload } + } + case actionTypes.FAILURE: return { ...state, diff --git a/examples/with-redux-saga/store.js b/examples/with-redux-saga/store.js index 4ac635321ab5e..c440320f15cc5 100644 --- a/examples/with-redux-saga/store.js +++ b/examples/with-redux-saga/store.js @@ -1,5 +1,6 @@ import { applyMiddleware, createStore } from 'redux' import createSagaMiddleware from 'redux-saga' +import { createWrapper } from 'next-redux-wrapper' import rootReducer, { exampleInitialState } from './reducer' import rootSaga from './saga' @@ -12,8 +13,9 @@ const bindMiddleware = (middleware) => { return applyMiddleware(...middleware) } -function configureStore(initialState = exampleInitialState) { +export const makeStore = (context, initialState = exampleInitialState) => { const sagaMiddleware = createSagaMiddleware() + const store = createStore( rootReducer, initialState, @@ -25,4 +27,4 @@ function configureStore(initialState = exampleInitialState) { return store } -export default configureStore +export const wrapper = createWrapper(makeStore, { debug: true })