├── .all-contributorsrc
├── .editorconfig
├── .gitignore
├── .prettierignore
├── .travis.yml
├── .yarnrc
├── LICENSE
├── README.md
├── babel.config.js
├── components
└── layout.js
├── index.js
├── jest.setup.js
├── package.json
├── pages
├── _app.js
├── async.js
├── index.js
└── sync.js
├── rollup.config.js
├── test
├── __snapshots__
│ ├── client.test.js.snap
│ └── server.test.js.snap
├── client.test.js
├── components
│ ├── async-get-initial-props.js
│ ├── class-component.js
│ ├── functional-component.js
│ └── sync-get-initial-props.js
├── constants.js
├── server.test.js
├── store
│ ├── root-reducer.js
│ ├── root-saga.js
│ └── store-wrapper.js
└── utils
│ ├── create-snapshot.js
│ ├── get-initial-props.js
│ └── get-server-context.js
└── yarn.lock
/.all-contributorsrc:
--------------------------------------------------------------------------------
1 | {
2 | "projectName": "next-redux-saga",
3 | "projectOwner": "bmealhouse",
4 | "repoType": "github",
5 | "repoHost": "https://github.com",
6 | "files": [
7 | "README.md"
8 | ],
9 | "imageSize": 100,
10 | "commit": false,
11 | "contributors": [
12 | {
13 | "login": "bmealhouse",
14 | "name": "Brent Mealhouse",
15 | "avatar_url": "https://avatars3.githubusercontent.com/u/3741255?v=4",
16 | "profile": "https://twitter.com/bmealhouse",
17 | "contributions": [
18 | "code",
19 | "test",
20 | "doc",
21 | "maintenance",
22 | "question"
23 | ]
24 | },
25 | {
26 | "login": "bbortt",
27 | "name": "Timon Borter",
28 | "avatar_url": "https://avatars0.githubusercontent.com/u/12272901?v=4",
29 | "profile": "https://bbortt.github.io",
30 | "contributions": [
31 | "code",
32 | "test",
33 | "doc",
34 | "maintenance",
35 | "question"
36 | ]
37 | },
38 | {
39 | "login": "JerryCauser",
40 | "name": "Artem Abzanov",
41 | "avatar_url": "https://avatars3.githubusercontent.com/u/5141037?v=4",
42 | "profile": "https://abzanov.com",
43 | "contributions": [
44 | "code",
45 | "test",
46 | "doc"
47 | ]
48 | },
49 | {
50 | "login": "RobbinHabermehl",
51 | "name": "Robbin Habermehl",
52 | "avatar_url": "https://avatars1.githubusercontent.com/u/1640272?v=4",
53 | "profile": "https://github.com/RobbinHabermehl",
54 | "contributions": [
55 | "code",
56 | "test",
57 | "doc"
58 | ]
59 | }
60 | ],
61 | "contributorsPerLine": 7
62 | }
63 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | indent_style = space
7 | indent_size = 2
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # build
4 | /.next
5 | /dist
6 |
7 | # dependencies
8 | /node_modules
9 |
10 | # editors
11 | /.idea
12 |
13 | # testing
14 | /coverage
15 |
16 | # misc
17 | .DS_Store
18 | npm-debug.log*
19 | yarn-debug.log*
20 | yarn-error.log*
21 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # See https://prettier.io/docs/en/ignore.html for more about ignoring files.
2 |
3 | # build
4 | /.next
5 | /dist
6 |
7 | # test
8 | /coverage
9 |
10 | # misc
11 | package.json
12 | README.md
13 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | node_js:
4 | - "8"
5 | - "12"
6 | - "13"
7 |
--------------------------------------------------------------------------------
/.yarnrc:
--------------------------------------------------------------------------------
1 | save-prefix ""
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Brent Mealhouse
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | next-redux-saga
2 | =====
3 |
4 | # This project is no longer maintained!
5 |
6 | Because `next.js` has grown massively and other packages with better support have covered the `redux-saga` SSR functionality.
7 | See [#79](https://github.com/bmealhouse/next-redux-saga/issues/79) for more information.
8 |
9 | [](https://npmjs.org/package/next-redux-saga)
10 | [](https://npmjs.org/package/next-redux-saga)
11 | [](https://github.com/sindresorhus/xo)
12 | [](https://github.com/prettier/prettier)
13 | [](https://travis-ci.com/bmealhouse/next-redux-saga)
14 | [](#contributors)
15 |
16 | > `redux-saga` HOC for [Next.js](https://github.com/zeit/next.js/). controlled `redux-saga` execution for server side rendering.
17 |
18 | > **Attention:** Synchronous HOC is no longer supported since version 4.0.0!
19 |
20 | ## Installation
21 |
22 | ```sh
23 | yarn add next-redux-saga
24 | ```
25 |
26 | ## Getting Started
27 |
28 | Check out the official [Next.js example](https://github.com/zeit/next.js/tree/canary/examples/with-redux-saga) or clone this repository and run the local example.
29 |
30 | ### Try the local example
31 |
32 | 1. Clone this repository
33 | 1. Install dependencies: `yarn`
34 | 1. Start the project: `yarn start`
35 | 1. Open [http://localhost:3000](http://localhost:3000)
36 |
37 | ## Usage
38 |
39 | `next-redux-saga` uses the redux store created by [next-redux-wrapper](https://github.com/kirill-konshin/next-redux-wrapper). Please refer to their documentation for more information.
40 |
41 | ### Configure the Store wrapper
42 |
43 | ```js
44 | import {applyMiddleware, createStore} from 'redux'
45 | import createSagaMiddleware from 'redux-saga'
46 | import {createWrapper} from 'next-redux-wrapper'
47 |
48 | import rootReducer from './root-reducer'
49 | import rootSaga from './root-saga'
50 |
51 | const makeStore = context => {
52 | const sagaMiddleware = createSagaMiddleware()
53 | const store = createStore(
54 | rootReducer,
55 | applyMiddleware(sagaMiddleware),
56 | )
57 |
58 | store.sagaTask = sagaMiddleware.run(rootSaga)
59 |
60 | return store
61 | }
62 |
63 | const wrapper = createWrapper(makeStore)
64 |
65 | export default wrapper
66 |
67 | ```
68 |
69 | ### Configure Custom `_app.js` Component
70 |
71 | ```js
72 | import React from 'react'
73 | import App from 'next/app'
74 | import withReduxSaga from 'next-redux-saga'
75 |
76 | import wrapper from './store-wrapper'
77 |
78 | class ExampleApp extends App {
79 | static async getInitialProps({Component, ctx}) {
80 | let pageProps = {}
81 |
82 | if (Component.getInitialProps) {
83 | pageProps = await Component.getInitialProps(ctx)
84 | }
85 |
86 | return {pageProps}
87 | }
88 |
89 | render() {
90 | const {Component, pageProps} = this.props
91 | return (
92 |
93 | )
94 | }
95 | }
96 |
97 | export default wrapper.withRedux(withReduxSaga(ExampleApp))
98 | ```
99 |
100 | ### Connect Page Components
101 |
102 | ```js
103 | import React, {Component} from 'react'
104 | import {connect} from 'react-redux'
105 |
106 | class ExamplePage extends Component {
107 | static async getInitialProps({store}) {
108 | store.dispatch({type: 'SOME_ASYNC_ACTION_REQUEST'})
109 | return {staticData: 'Hello world!'}
110 | }
111 |
112 | render() {
113 | return
{this.props.staticData}
114 | }
115 | }
116 |
117 | export default connect(state => state)(ExamplePage)
118 | ```
119 |
120 | ## Contributors
121 |
122 |
123 |
124 |
125 |
126 |
127 | ## Contributing
128 |
129 | 1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your own GitHub account and then [clone](https://help.github.com/articles/cloning-a-repository/) it to your local device
130 | 1. Install the dependecies: `yarn`
131 | 1. Link the package to the global module directory: `yarn link`
132 | 1. Run `yarn test --watch` and start making your changes
133 | 1. You can use `yarn link next-redux-saga` to test your changes in an actual project
134 |
135 | ## LICENSE
136 |
137 | This project is licensed under the terms of MIT license. See the [license file](https://github.com/bmealhouse/next-redux-saga/blob/master/LICENSE) for more information.
138 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'presets': [
3 | 'next/babel',
4 | ],
5 | 'env': {
6 | 'test': {
7 | 'presets': [
8 | [
9 | 'next/babel',
10 | {
11 | 'preset-env': {
12 | 'modules': 'commonjs',
13 | },
14 | },
15 | ],
16 | ],
17 | },
18 | },
19 | }
20 |
--------------------------------------------------------------------------------
/components/layout.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 |
3 | import {node} from 'prop-types'
4 |
5 | import Router from 'next/router'
6 |
7 | Router.onRouteChangeComplete = () => {
8 | document.querySelectorAll('.spinner').forEach(el => {
9 | el.classList.remove('spinner')
10 | })
11 | }
12 |
13 | class App extends Component {
14 | static propTypes = {
15 | children: node,
16 | }
17 |
18 | componentDidMount() {
19 | if (typeof window === 'undefined') return
20 | Router.prefetch('/sync')
21 | Router.prefetch('/async')
22 | }
23 |
24 | handleClick = target => {
25 | Router.push(target)
26 | }
27 |
28 | render() {
29 | return (
30 |
31 | this.handleClick('/')}>
32 | Home
33 |
34 | this.handleClick('/sync')}>
35 | Sync
36 |
37 | this.handleClick('/async')}>
38 | Async
39 |
40 | {this.props.children}
41 |
76 |
110 |
111 | )
112 | }
113 | }
114 |
115 | export default App
116 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/prop-types */
2 | import React, {Component} from 'react'
3 | import {END} from 'redux-saga'
4 |
5 | function withReduxSaga(BaseComponent) {
6 | class WrappedComponent extends Component {
7 | static displayName = `withReduxSaga(${BaseComponent.displayName ||
8 | BaseComponent.name ||
9 | 'BaseComponent'})`
10 |
11 | static async getInitialProps(props) {
12 | const {store} = props.ctx
13 |
14 | let pageProps = {}
15 | if (BaseComponent.getInitialProps) {
16 | pageProps = await BaseComponent.getInitialProps(props)
17 | }
18 |
19 | // Stop saga on the server
20 | if (typeof window === 'undefined') {
21 | store.dispatch(END)
22 | await store.sagaTask.toPromise()
23 | }
24 |
25 | return pageProps
26 | }
27 |
28 | render() {
29 | return
30 | }
31 | }
32 |
33 | return WrappedComponent
34 | }
35 |
36 | export default withReduxSaga
37 |
--------------------------------------------------------------------------------
/jest.setup.js:
--------------------------------------------------------------------------------
1 | import {configure} from 'enzyme'
2 | import Adapter from 'enzyme-adapter-react-16'
3 |
4 | configure({adapter: new Adapter()})
5 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "next-redux-saga",
3 | "version": "5.0.0-0",
4 | "description": "redux-saga HOC for Next.js",
5 | "repository": "https://github.com/bmealhouse/next-redux-saga.git",
6 | "author": "Brent Mealhouse ",
7 | "contributors": [
8 | "Timon Borter",
9 | "Artem Abzanov",
10 | "Robbin Habermehl"
11 | ],
12 | "license": "MIT",
13 | "main": "dist/next-redux-saga.umd.js",
14 | "jsnext:main": "dist/next-redux-saga.es.js",
15 | "module": "dist/next-redux-saga.es.js",
16 | "files": [
17 | "dist"
18 | ],
19 | "scripts": {
20 | "build": "rollup --config",
21 | "format": "prettier --write",
22 | "prerelease": "yarn build",
23 | "release": "release-it",
24 | "start": "next",
25 | "test": "jest"
26 | },
27 | "jest": {
28 | "collectCoverage": true,
29 | "verbose": true,
30 | "setupFiles": [
31 | "/jest.setup.js"
32 | ],
33 | "coveragePathIgnorePatterns": [
34 | "/node_modules/",
35 | "package.json",
36 | "yarn.lock"
37 | ],
38 | "transformIgnorePatterns": [
39 | "node_modules/(?!(@babel/runtime)/)"
40 | ]
41 | },
42 | "lint-staged": {
43 | "*.js": [
44 | "prettier --write",
45 | "git add"
46 | ]
47 | },
48 | "prettier": {
49 | "useTabs": false,
50 | "semi": false,
51 | "singleQuote": true,
52 | "trailingComma": "all",
53 | "bracketSpacing": false
54 | },
55 | "renovate": {
56 | "automerge": true,
57 | "automergeType": "branch-push",
58 | "pinVersions": true,
59 | "schedule": [
60 | "every friday"
61 | ],
62 | "packageRules": [
63 | {
64 | "packageNames": [
65 | "jest",
66 | "babel-jest"
67 | ],
68 | "groupName": "jest packages"
69 | }
70 | ]
71 | },
72 | "xo": {
73 | "envs": [
74 | "browser",
75 | "jest"
76 | ],
77 | "extends": [
78 | "plugin:react/recommended",
79 | "prettier/react"
80 | ],
81 | "parser": "babel-eslint",
82 | "prettier": true,
83 | "rules": {
84 | "capitalized-comments": 0,
85 | "import/order": 0
86 | },
87 | "settings": {
88 | "react": {
89 | "version": "detect"
90 | }
91 | }
92 | },
93 | "peerDependencies": {
94 | "redux-saga": "1.x"
95 | },
96 | "dependencies": {
97 | "@babel/runtime": "7.3.1"
98 | },
99 | "devDependencies": {
100 | "@babel/core": "7.10.2",
101 | "@rollup/plugin-babel": "5.0.3",
102 | "babel-eslint": "10.1.0",
103 | "babel-jest": "26.0.1",
104 | "enzyme": "3.11.0",
105 | "enzyme-adapter-react-16": "1.15.2",
106 | "enzyme-to-json": "3.5.0",
107 | "eslint": "7.2.0",
108 | "eslint-plugin-prettier": "3.1.4",
109 | "eslint-plugin-react": "7.20.0",
110 | "husky": "4.2.5",
111 | "jest": "26.0.1",
112 | "jest-express": "1.12.0",
113 | "lint-staged": "10.2.10",
114 | "next": "9.4.4",
115 | "next-redux-wrapper": "6.0.2",
116 | "prettier": "2.0.5",
117 | "prop-types": "15.7.2",
118 | "react": "16.13.1",
119 | "react-dom": "16.13.1",
120 | "react-redux": "7.2.0",
121 | "react-test-renderer": "16.13.1",
122 | "redux": "4.0.5",
123 | "redux-saga": "1.1.3",
124 | "release-it": "13.6.3",
125 | "rollup": "2.16.1",
126 | "webpack": "4.43.0",
127 | "xo": "0.32.0"
128 | },
129 | "bugs": {
130 | "url": "https://github.com/bmealhouse/next-redux-saga/issues"
131 | },
132 | "homepage": "https://github.com/bmealhouse/next-redux-saga#readme",
133 | "keywords": [
134 | "next",
135 | "nextjs",
136 | "Next.js",
137 | "next-redux",
138 | "next-redux-wrapper",
139 | "react",
140 | "react-redux",
141 | "redux",
142 | "redux-saga"
143 | ]
144 | }
145 |
--------------------------------------------------------------------------------
/pages/_app.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import App from 'next/app'
4 |
5 | import wrapper from '../test/store/store-wrapper'
6 |
7 | class ExampleApp extends App {
8 | static async getInitialProps({Component, ctx}) {
9 | let pageProps = {}
10 |
11 | if (Component.getInitialProps) {
12 | pageProps = await Component.getInitialProps({ctx})
13 | }
14 |
15 | return {pageProps}
16 | }
17 |
18 | render() {
19 | const {Component, pageProps} = this.props
20 |
21 | return (
22 |
23 | )
24 | }
25 | }
26 |
27 | export default wrapper.withRedux(ExampleApp)
28 |
--------------------------------------------------------------------------------
/pages/async.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 |
3 | import {string} from 'prop-types'
4 |
5 | import {connect} from 'react-redux'
6 |
7 | import withReduxSaga from '..'
8 |
9 | import Layout from '../components/layout'
10 |
11 | import {
12 | GET_SYNC_REDUX_PROP_TYPE,
13 | GET_ASYNC_REDUX_SAGA_PROP_TYPE,
14 | STATIC_PROP_TEXT,
15 | SYNC_REDUX_PROP_TEXT,
16 | } from '../test/constants'
17 |
18 | class AsyncExample extends Component {
19 | static propTypes = {
20 | staticProp: string,
21 | syncReduxProp: string,
22 | asyncReduxSagaProp: string,
23 | }
24 |
25 | static getInitialProps({ctx: {store}}) {
26 | store.dispatch({
27 | type: GET_SYNC_REDUX_PROP_TYPE,
28 | data: SYNC_REDUX_PROP_TEXT,
29 | })
30 |
31 | store.dispatch({type: GET_ASYNC_REDUX_SAGA_PROP_TYPE})
32 | return {staticProp: STATIC_PROP_TEXT}
33 | }
34 |
35 | render() {
36 | const {staticProp, syncReduxProp, asyncReduxSagaProp} = this.props
37 |
38 | return (
39 |
40 |
41 | Received static prop:
42 |
43 | {staticProp}
44 |
45 |
46 |
47 | Received synchronous Redux prop:
48 |
49 | {syncReduxProp}
50 |
51 |
52 |
53 | Received asynchronous Redux-Saga prop:
54 |
55 | {asyncReduxSagaProp || 'loading...'}
56 |
57 |
58 |
59 | )
60 | }
61 | }
62 |
63 | export default withReduxSaga(connect(state => state)(AsyncExample))
64 |
--------------------------------------------------------------------------------
/pages/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import Layout from '../components/layout'
4 |
5 | function Home() {
6 | return
7 | }
8 |
9 | export default Home
10 |
--------------------------------------------------------------------------------
/pages/sync.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 |
3 | import {string} from 'prop-types'
4 |
5 | import {connect} from 'react-redux'
6 |
7 | import withReduxSaga from '..'
8 |
9 | import Layout from '../components/layout'
10 |
11 | import {
12 | GET_SYNC_REDUX_PROP_TYPE,
13 | GET_ASYNC_REDUX_SAGA_PROP_TYPE,
14 | STATIC_PROP_TEXT,
15 | SYNC_REDUX_PROP_TEXT,
16 | } from '../test/constants'
17 |
18 | class SyncExample extends Component {
19 | static propTypes = {
20 | staticProp: string,
21 | syncReduxProp: string,
22 | asyncReduxSagaProp: string,
23 | }
24 |
25 | static getInitialProps({ctx: {store}}) {
26 | store.dispatch({
27 | type: GET_SYNC_REDUX_PROP_TYPE,
28 | data: SYNC_REDUX_PROP_TEXT,
29 | })
30 |
31 | store.dispatch({type: GET_ASYNC_REDUX_SAGA_PROP_TYPE})
32 | return {staticProp: STATIC_PROP_TEXT}
33 | }
34 |
35 | render() {
36 | const {staticProp, syncReduxProp, asyncReduxSagaProp} = this.props
37 |
38 | return (
39 |
40 |
41 | Received static prop:
42 |
43 | {staticProp}
44 |
45 |
46 |
47 | Received synchronous Redux prop:
48 |
49 | {syncReduxProp}
50 |
51 |
52 |
53 | Received asynchronous Redux-Saga prop:
54 |
55 | {asyncReduxSagaProp || 'loading...'}
56 |
57 |
58 |
59 | )
60 | }
61 | }
62 |
63 | export default withReduxSaga(connect(state => state)(SyncExample))
64 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import babel from '@rollup/plugin-babel'
2 | import pkg from './package.json' // eslint-disable-line import/extensions
3 |
4 | export default ['umd', 'es'].map(format => ({
5 | input: 'index.js',
6 | output: {
7 | file: `dist/${pkg.name}.${format}.js`,
8 | name: format === 'umd' ? pkg.name : undefined,
9 | format,
10 | sourcemap: true,
11 | globals: {
12 | react: 'React',
13 | 'redux-saga': 'ReduxSaga',
14 | '@abel/runtime/regenerator': 'regeneratorRuntime',
15 | },
16 | },
17 | plugins: [
18 | babel({
19 | exclude: 'node_modules/**',
20 | babelHelpers: 'runtime',
21 | }),
22 | ],
23 | external: ['react', 'redux-saga', '@babel/runtime/regenerator'],
24 | }))
25 |
--------------------------------------------------------------------------------
/test/__snapshots__/client.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Wrapped component awaits asynchronous getInitialProps 1`] = `
4 |
87 |
90 |
91 | `;
92 |
93 | exports[`Wrapped component awaits synchronous getInitialProps 1`] = `
94 |
177 |
180 |
181 | `;
182 |
183 | exports[`Wrapped component passes along React props 1`] = `
184 |
267 |
268 |
269 | `;
270 |
271 | exports[`Wrapped component skips getInitialProps when it does not exist 1`] = `
272 |
355 |
356 |
357 | `;
358 |
--------------------------------------------------------------------------------
/test/__snapshots__/server.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Wrapped component awaits asynchronous getInitialProps 1`] = `
4 |
87 |
90 |
91 | `;
92 |
93 | exports[`Wrapped component awaits synchronous getInitialProps 1`] = `
94 |
177 |
180 |
181 | `;
182 |
183 | exports[`Wrapped component passes along React props 1`] = `
184 |
267 |
268 |
269 | `;
270 |
271 | exports[`Wrapped component skips getInitialProps when it does not exist 1`] = `
272 |
355 |
356 |
357 | `;
358 |
--------------------------------------------------------------------------------
/test/client.test.js:
--------------------------------------------------------------------------------
1 | import withReduxSaga from '..'
2 |
3 | import ClassComponent from './components/class-component'
4 | import FunctionalComponent from './components/functional-component'
5 | import AsyncGetInitialProps from './components/async-get-initial-props'
6 | import SyncGetInitialProps from './components/sync-get-initial-props'
7 |
8 | import wrapper from './store/store-wrapper'
9 | import createSnapshot from './utils/create-snapshot'
10 | import getInitialProps from './utils/get-initial-props'
11 |
12 | import {STATIC_PROP_TEXT, SYNC_REDUX_PROP_TEXT} from './constants'
13 |
14 | test('Wrapped component passes along React props', () => {
15 | const WrappedComponent = wrapper.withRedux(
16 | withReduxSaga(FunctionalComponent),
17 | )
18 |
19 | createSnapshot(WrappedComponent)
20 | })
21 |
22 | test('Wrapped component skips getInitialProps when it does not exist', async () => {
23 | const WrappedComponent = wrapper.withRedux(
24 | withReduxSaga(ClassComponent),
25 | )
26 |
27 | const props = await getInitialProps(WrappedComponent)
28 |
29 | createSnapshot(WrappedComponent, props)
30 | })
31 |
32 | test('Wrapped component awaits synchronous getInitialProps', async () => {
33 | const WrappedComponent = wrapper.withRedux(
34 | withReduxSaga(SyncGetInitialProps),
35 | )
36 |
37 | const props = await getInitialProps(WrappedComponent)
38 |
39 | expect(props.initialState).toEqual({syncReduxProp: SYNC_REDUX_PROP_TEXT})
40 | expect(props.initialProps).toEqual({staticProp: STATIC_PROP_TEXT})
41 |
42 | createSnapshot(WrappedComponent, props)
43 | })
44 |
45 | test('Wrapped component awaits asynchronous getInitialProps', async () => {
46 | const WrappedComponent = wrapper.withRedux(
47 | withReduxSaga(AsyncGetInitialProps),
48 | )
49 |
50 | const props = await getInitialProps(WrappedComponent)
51 |
52 | expect(props.initialState).toEqual({syncReduxProp: SYNC_REDUX_PROP_TEXT})
53 | expect(props.initialProps).toEqual({staticProp: STATIC_PROP_TEXT})
54 |
55 | createSnapshot(WrappedComponent, props)
56 | })
57 |
--------------------------------------------------------------------------------
/test/components/async-get-initial-props.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/prop-types */
2 | import React, {Component} from 'react'
3 | import {connect} from 'react-redux'
4 |
5 | import {
6 | GET_ASYNC_REDUX_SAGA_PROP_TYPE,
7 | GET_SYNC_REDUX_PROP_TYPE,
8 | STATIC_PROP_TEXT,
9 | SYNC_REDUX_PROP_TEXT,
10 | } from '../constants'
11 |
12 | class AsyncGetInitialProps extends Component {
13 | static async getInitialProps(props) {
14 | const {store} = props.ctx
15 |
16 | store.dispatch({
17 | type: GET_SYNC_REDUX_PROP_TYPE,
18 | data: SYNC_REDUX_PROP_TEXT,
19 | })
20 |
21 | store.dispatch({type: GET_ASYNC_REDUX_SAGA_PROP_TYPE})
22 | return {staticProp: STATIC_PROP_TEXT}
23 | }
24 |
25 | render() {
26 | return AsyncGetInitialProps({JSON.stringify(this.props)})
27 | }
28 | }
29 |
30 | export default connect(state => state)(AsyncGetInitialProps)
31 |
--------------------------------------------------------------------------------
/test/components/class-component.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/prop-types */
2 | import React, {Component} from 'react'
3 | import {connect} from 'react-redux'
4 |
5 | class ClassComponent extends Component {
6 | render() {
7 | const {mode} = this.props
8 | return (
9 |
10 | ClassComponent(
11 | {JSON.stringify({mode})})
12 |
13 | )
14 | }
15 | }
16 |
17 | export default connect(state => state)(ClassComponent)
18 |
--------------------------------------------------------------------------------
/test/components/functional-component.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {connect} from 'react-redux'
3 |
4 | function FunctionalComponent(props) {
5 | return FunctionalComponent({JSON.stringify(props)})
6 | }
7 |
8 | export default connect(state => state)(FunctionalComponent)
9 |
--------------------------------------------------------------------------------
/test/components/sync-get-initial-props.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/prop-types */
2 | import React, {Component} from 'react'
3 | import {connect} from 'react-redux'
4 |
5 | import {
6 | GET_SYNC_REDUX_PROP_TYPE,
7 | STATIC_PROP_TEXT,
8 | SYNC_REDUX_PROP_TEXT,
9 | } from '../constants'
10 |
11 | class SyncGetInitialProps extends Component {
12 | static getInitialProps(props) {
13 | const {store} = props.ctx
14 |
15 | store.dispatch({
16 | type: GET_SYNC_REDUX_PROP_TYPE,
17 | data: SYNC_REDUX_PROP_TEXT,
18 | })
19 |
20 | return {staticProp: STATIC_PROP_TEXT}
21 | }
22 |
23 | render() {
24 | return SyncGetInitialProps({JSON.stringify(this.props)})
25 | }
26 | }
27 |
28 | export default connect(state => state)(SyncGetInitialProps)
29 |
--------------------------------------------------------------------------------
/test/constants.js:
--------------------------------------------------------------------------------
1 | export const GET_SYNC_REDUX_PROP_TYPE = 'GET_SYNC_REDUX_PROP'
2 |
3 | export const GET_ASYNC_REDUX_SAGA_PROP_TYPE = 'GET_ASYNC_REDUX_SAGA_PROP'
4 | export const GET_ASYNC_REDUX_SAGA_PROP_TYPE_SUCCESS =
5 | 'GET_ASYNC_REDUX_SAGA_PROP_SUCCESS'
6 |
7 | export const STATIC_PROP_TEXT = 'Static message from getInitialProps()'
8 | export const SYNC_REDUX_PROP_TEXT = 'Synchronous message from Redux'
9 | export const ASYNC_REDUX_SAGA_PROP_TEXT = 'Asynchronous message from Redux-Saga'
10 |
--------------------------------------------------------------------------------
/test/server.test.js:
--------------------------------------------------------------------------------
1 | /** @jest-environment node */
2 | import withReduxSaga from '..'
3 |
4 | import AsyncGetInitialProps from './components/async-get-initial-props'
5 | import ClassComponent from './components/class-component'
6 | import FunctionalComponent from './components/functional-component'
7 | import SyncGetInitialProps from './components/sync-get-initial-props'
8 |
9 | import wrapper from './store/store-wrapper'
10 | import createSnapshot from './utils/create-snapshot'
11 | import getInitialProps from './utils/get-initial-props'
12 |
13 | import {ASYNC_REDUX_SAGA_PROP_TEXT, STATIC_PROP_TEXT, SYNC_REDUX_PROP_TEXT} from './constants'
14 |
15 | test('Wrapped component passes along React props', () => {
16 | const WrappedComponent = wrapper.withRedux(
17 | withReduxSaga(FunctionalComponent),
18 | )
19 |
20 | createSnapshot(WrappedComponent)
21 | })
22 |
23 | test('Wrapped component skips getInitialProps when it does not exist', async () => {
24 | const WrappedComponent = wrapper.withRedux(
25 | withReduxSaga(ClassComponent),
26 | )
27 |
28 | const props = await getInitialProps(WrappedComponent)
29 |
30 | createSnapshot(WrappedComponent, props)
31 | })
32 |
33 | test('Wrapped component awaits synchronous getInitialProps', async () => {
34 | const WrappedComponent = wrapper.withRedux(
35 | withReduxSaga(SyncGetInitialProps),
36 | )
37 |
38 | const props = await getInitialProps(WrappedComponent)
39 |
40 | expect(props.initialState).toEqual({syncReduxProp: SYNC_REDUX_PROP_TEXT})
41 | expect(props.initialProps).toEqual({staticProp: STATIC_PROP_TEXT})
42 |
43 | createSnapshot(WrappedComponent, props)
44 | })
45 |
46 | test('Wrapped component awaits asynchronous getInitialProps', async () => {
47 | const WrappedComponent = wrapper.withRedux(
48 | withReduxSaga(AsyncGetInitialProps),
49 | )
50 |
51 | const props = await getInitialProps(WrappedComponent)
52 |
53 | console.log('props: ', props)
54 |
55 | expect(props.initialState).toEqual({
56 | syncReduxProp: SYNC_REDUX_PROP_TEXT,
57 | asyncReduxSagaProp: ASYNC_REDUX_SAGA_PROP_TEXT,
58 | })
59 | expect(props.initialProps).toEqual({staticProp: STATIC_PROP_TEXT})
60 |
61 | createSnapshot(WrappedComponent, props)
62 | })
63 |
--------------------------------------------------------------------------------
/test/store/root-reducer.js:
--------------------------------------------------------------------------------
1 | import {
2 | GET_SYNC_REDUX_PROP_TYPE,
3 | GET_ASYNC_REDUX_SAGA_PROP_TYPE_SUCCESS,
4 | } from '../constants'
5 |
6 | const initialState = {}
7 |
8 | function rootReducer(state = initialState, action) {
9 | switch (action.type) {
10 | case GET_SYNC_REDUX_PROP_TYPE:
11 | return {...state, syncReduxProp: action.data}
12 | case GET_ASYNC_REDUX_SAGA_PROP_TYPE_SUCCESS:
13 | return {...state, asyncReduxSagaProp: action.data}
14 | default:
15 | return state
16 | }
17 | }
18 |
19 | export default rootReducer
20 |
--------------------------------------------------------------------------------
/test/store/root-saga.js:
--------------------------------------------------------------------------------
1 | import {delay, put, takeEvery} from 'redux-saga/effects'
2 |
3 | import {
4 | GET_ASYNC_REDUX_SAGA_PROP_TYPE,
5 | GET_ASYNC_REDUX_SAGA_PROP_TYPE_SUCCESS,
6 | ASYNC_REDUX_SAGA_PROP_TEXT,
7 | } from '../constants'
8 |
9 | const TEST = process.env.NODE_ENV === 'test'
10 |
11 | function* getAsyncReduxSagaProp() {
12 | yield delay(TEST ? 100 : 2000)
13 |
14 | yield put({
15 | type: GET_ASYNC_REDUX_SAGA_PROP_TYPE_SUCCESS,
16 | data: ASYNC_REDUX_SAGA_PROP_TEXT,
17 | })
18 | }
19 |
20 | function* rootSaga() {
21 | yield takeEvery(GET_ASYNC_REDUX_SAGA_PROP_TYPE, getAsyncReduxSagaProp)
22 | }
23 |
24 | export default rootSaga
25 |
--------------------------------------------------------------------------------
/test/store/store-wrapper.js:
--------------------------------------------------------------------------------
1 | import {applyMiddleware, createStore} from 'redux'
2 | import createSagaMiddleware from 'redux-saga'
3 | import {createWrapper} from 'next-redux-wrapper'
4 |
5 | import rootReducer from './root-reducer'
6 | import rootSaga from './root-saga'
7 |
8 | const makeStore = context => {
9 | const sagaMiddleware = createSagaMiddleware()
10 | const store = createStore(
11 | rootReducer,
12 | applyMiddleware(sagaMiddleware),
13 | )
14 |
15 | store.sagaTask = sagaMiddleware.run(rootSaga)
16 |
17 | return store
18 | }
19 |
20 | const wrapper = createWrapper(makeStore)
21 |
22 | export default wrapper
23 |
--------------------------------------------------------------------------------
/test/utils/create-snapshot.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {shallow} from 'enzyme'
3 | import toJson from 'enzyme-to-json'
4 |
5 | function createSnapshot(WrappedComponent, props) {
6 | // Render is called by Next.js at runtime.
7 | const component = shallow().dive()
8 |
9 | // Remove store from snapshots
10 | const json = toJson(component)
11 | delete json.props.store
12 |
13 | expect(json).toMatchSnapshot()
14 | }
15 |
16 | export default createSnapshot
17 |
--------------------------------------------------------------------------------
/test/utils/get-initial-props.js:
--------------------------------------------------------------------------------
1 | function getInitialProps(WrappedComponent) {
2 | // getInitialProps is called by Next.js at runtime.
3 | return WrappedComponent.getInitialProps({ctx: {}})
4 | }
5 |
6 | export default getInitialProps
7 |
--------------------------------------------------------------------------------
/test/utils/get-server-context.js:
--------------------------------------------------------------------------------
1 | function getServerContext() {
2 | return {ctx: {req: {}, res: {}}}
3 | }
4 |
5 | export default getServerContext
6 |
--------------------------------------------------------------------------------