Skip to content

Commit

Permalink
#194 Add support of getStaticProps and getServerSideProps
Browse files Browse the repository at this point in the history
  • Loading branch information
kirill-konshin committed Mar 31, 2020
1 parent 32eb92b commit 59b798d
Show file tree
Hide file tree
Showing 34 changed files with 980 additions and 691 deletions.
8 changes: 8 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ deploy:
skip_cleanup: true
on:
tags: true
branch: master
repo: kirill-konshin/next-redux-wrapper
- provider: script
script: yarn publish:release ${TRAVIS_TAG} --yes --dist-tag next
skip_cleanup: true
on:
tags: true
branch: dev
repo: kirill-konshin/next-redux-wrapper

after_success: yarn test:coverage
600 changes: 294 additions & 306 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "5.0.0",
"version": "6.0.0",
"useWorkspaces": true,
"npmClient": "yarn"
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "next-redux-wrapper-mono",
"private": true,
"version": "5.0.0",
"version": "6.0.0",
"description": "Redux wrapper for Next.js",
"scripts": {
"clean": "lerna run clean && lerna clean --yes && rimraf node_modules",
Expand Down
2 changes: 2 additions & 0 deletions packages/demo-page/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
package-lock=false
save-exact=true
8 changes: 8 additions & 0 deletions packages/demo-page/jest-puppeteer.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
server: {
command: 'yarn serve',
debug: true,
port: 4000,
launchTimeout: 30000,
},
};
6 changes: 6 additions & 0 deletions packages/demo-page/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const config = require('next-redux-wrapper-configs/jest.config');
module.exports = {
...config,
preset: 'jest-puppeteer',
coveragePathIgnorePatterns: config.coveragePathIgnorePatterns.concat('./jest-puppeteer.config.cjs'),
};
2 changes: 2 additions & 0 deletions packages/demo-page/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/// <reference types="next" />
/// <reference types="next/types/global" />
50 changes: 50 additions & 0 deletions packages/demo-page/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"name": "next-redux-wrapper-demo-page",
"private": true,
"version": "6.0.0",
"description": "Demo of redux wrapper for Next.js",
"scripts": {
"clean": "rimraf .next coverage",
"test": "jest",
"start": "next --port=4000",
"build": "next build",
"serve": "next start --port=4000"
},
"dependencies": {
"jsondiffpatch": "0.4.1",
"next-redux-wrapper": "^6.0.0",
"react": "16.12.0",
"react-dom": "16.12.0",
"react-redux": "7.2.0",
"redux": "4.0.5"
},
"devDependencies": {
"@types/expect-puppeteer": "4.4.0",
"@types/jest": "25.1.3",
"@types/jest-environment-puppeteer": "4.3.1",
"@types/puppeteer": "2.0.0",
"@types/react": "16.9.21",
"@types/react-dom": "16.9.5",
"@types/react-redux": "7.1.7",
"@types/redux-promise-middleware": "6.0.0",
"@types/webpack-env": "1.15.1",
"jest": "25.1.0",
"jest-puppeteer": "4.4.0",
"next": "9.3.1",
"next-redux-wrapper-configs": "^5.0.0",
"puppeteer": "2.1.1",
"rimraf": "3.0.2",
"ts-jest": "25.2.1",
"typescript": "3.7.5"
},
"author": "Kirill Konshin",
"repository": {
"type": "git",
"url": "git://github.com/kirill-konshin/next-redux-wrapper.git"
},
"bugs": {
"url": "https://github.com/kirill-konshin/next-redux-wrapper/issues"
},
"homepage": "https://github.com/kirill-konshin/next-redux-wrapper",
"license": "MIT"
}
29 changes: 29 additions & 0 deletions packages/demo-page/src/components/reducer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {AnyAction} from 'redux';
import {HYDRATE} from 'next-redux-wrapper';
import {diff} from 'jsondiffpatch';

export interface State {
page: string;
}

const reducer = (state: State = {page: 'init'}, action: AnyAction) => {
switch (action.type) {
case HYDRATE:
const stateDiff = diff(state, action.payload) as any;
const wasBumpedOnClient = stateDiff.page[0].endsWith('X');
console.log('HYDRATE action handler', {stateDiff, wasBumpedOnClient});
return {
...state,
...action.payload,
page: wasBumpedOnClient ? state.page : action.payload.page,
};
case 'PAGE':
return {...state, page: action.payload};
case 'BUMP':
return {...state, page: state.page + 'X'};
default:
return state;
}
};

export default reducer;
18 changes: 18 additions & 0 deletions packages/demo-page/src/components/store.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {createStore} from 'redux';
import {MakeStore, createWrapper, Context} from 'next-redux-wrapper';
import reducer, {State} from './reducer';

export const makeStore: MakeStore<State> = (context: Context) => {
const store = createStore(reducer);

if (module.hot) {
module.hot.accept('./reducer', () => {
console.log('Replacing reducer');
store.replaceReducer(require('./reducer').default);
});
}

return store;
};

export const wrapper = createWrapper<State>(makeStore, {debug: true});
30 changes: 30 additions & 0 deletions packages/demo-page/src/pages/_error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React, {Component} from 'react';
import Link from 'next/link';
import {connect} from 'react-redux';
import {wrapper} from '../components/store';
import {State} from '../components/reducer';

class ErrorPage extends Component<State> {
public static getInitialProps = wrapper.getInitialPageProps(({store, pathname}) => {
console.log('2. Page.getInitialProps uses the store to dispatch things');
store.dispatch({type: 'PAGE', payload: 'was set in error page ' + pathname});
});

render() {
const {page} = this.props;
return (
<>
<p>
This is an error page, it also has access to store: <code>{page}</code>
</p>
<nav>
<Link href="/">
<a>Navigate to index</a>
</Link>
</nav>
</>
);
}
}

export default wrapper.withRedux(connect((state: State) => state)(ErrorPage));
35 changes: 35 additions & 0 deletions packages/demo-page/src/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from 'react';
import {NextPage} from 'next';
import Link from 'next/link';
import {useSelector} from 'react-redux';
import {wrapper} from '../components/store';
import {State} from '../components/reducer';

export interface ConnectedPageProps {
custom: string;
}

// Page itself is not connected to Redux Store, it has to render Provider to allow child components to connect to Redux Store
const Page: NextPage<ConnectedPageProps> = ({custom}) => {
const {page} = useSelector<State, State>(state => state);
return (
<div className="index">
<pre>{JSON.stringify({page, custom}, null, 2)}</pre>
<Link href="/other">
<a>Navigate</a>
</Link>
{' | '}
<Link href="/error">
<a>Navigate to error</a>
</Link>
</div>
);
};

export const getServerSideProps = wrapper.getServerSideProps(({store, req}) => {
console.log('2. Page.getServerSideProps uses the store to dispatch things');
store.dispatch({type: 'PAGE', payload: 'was set in index page ' + req.url});
return {props: {custom: 'custom'}};
});

export default wrapper.withRedux(Page);
31 changes: 31 additions & 0 deletions packages/demo-page/src/pages/other.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react';
import {NextPage} from 'next';
import Link from 'next/link';
import {useSelector, useDispatch} from 'react-redux';
import {State} from '../components/reducer';
import {wrapper} from '../components/store';

export const getStaticProps = wrapper.getStaticProps(({store, previewData}) => {
console.log('2. Page.getStaticProps uses the store to dispatch things');
store.dispatch({type: 'PAGE', payload: 'was set in other page ' + JSON.stringify({previewData})});
});

const OtherPage: NextPage<State> = () => {
const {page} = useSelector<State, State>(state => state);
const dispatch = useDispatch();
const bump = () => dispatch({type: 'BUMP'});
return (
<div className="other">
<p>Using Next.js default prop in a wrapped component.</p>
<pre>{JSON.stringify({page}, null, 2)}</pre>
<nav>
<button onClick={bump}>bump</button>
<Link href="/">
<a>Navigate to index</a>
</Link>
</nav>
</div>
);
};

export default wrapper.withRedux(OtherPage);
29 changes: 29 additions & 0 deletions packages/demo-page/tests/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import config from '../jest-puppeteer.config';

const openPage = (url = '/') => page.goto(`http://localhost:${config.server.port}${url}`);

describe('Basic integration', () => {
it('shows the page', async () => {
await openPage();

await page.waitForSelector('div.index');

await expect(page).toMatch('"page": "was set in index page /"');
await expect(page).toMatch('"custom": "custom"');
});

it('clicks the button', async () => {
await openPage('/other');

await page.waitForSelector('div.other');

await expect(page).toMatch('"page": "was set in other page {}"');

await expect(page).toClick('a', {text: 'Navigate to index'});

await page.waitForSelector('div.index');

await expect(page).toMatch('"page": "was set in index page /"');
await expect(page).toMatch('"custom": "custom"');
});
});
20 changes: 20 additions & 0 deletions packages/demo-page/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"extends": "../configs/tsconfig.json",
"include": [
"tests",
"src/pages",
"src/components"
],
"compilerOptions": {
"allowJs": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"module": "esnext",
"isolatedModules": true,
"jsx": "preserve"
},
"exclude": [
"node_modules"
]
}
6 changes: 3 additions & 3 deletions packages/demo/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "next-redux-wrapper-demo",
"private": true,
"version": "5.0.0",
"version": "6.0.0",
"description": "Demo of redux wrapper for Next.js",
"scripts": {
"clean": "rimraf .next coverage",
Expand All @@ -11,7 +11,7 @@
"serve": "next start"
},
"dependencies": {
"next-redux-wrapper": "^5.0.0",
"next-redux-wrapper": "^6.0.0",
"react": "16.12.0",
"react-dom": "16.12.0",
"react-redux": "7.2.0",
Expand All @@ -29,7 +29,7 @@
"@types/webpack-env": "1.15.1",
"jest": "25.1.0",
"jest-puppeteer": "4.4.0",
"next": "9.2.2",
"next": "9.3.1",
"next-redux-wrapper-configs": "^5.0.0",
"puppeteer": "2.1.1",
"rimraf": "3.0.2",
Expand Down
12 changes: 0 additions & 12 deletions packages/demo/src/components/layout.tsx

This file was deleted.

20 changes: 10 additions & 10 deletions packages/demo/src/components/reducer.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import {AnyAction} from 'redux';
import {HYDRATE} from 'next-redux-wrapper';

export interface State {
tick: string;
tack: string;
toe: string;
app: string;
page: string;
}

const reducer = (state: State = {tick: 'init', tack: 'init', toe: 'init'}, action: AnyAction) => {
const reducer = (state: State = {app: 'init', page: 'init'}, action: AnyAction) => {
switch (action.type) {
case 'TICK':
return {...state, tick: action.payload};
case 'TACK':
return {...state, tack: action.payload};
case 'TOE':
return {...state, toe: action.payload};
case HYDRATE:
return {...state, ...action.payload};
case 'APP':
return {...state, app: action.payload};
case 'PAGE':
return {...state, page: action.payload};
default:
return state;
}
Expand Down
8 changes: 5 additions & 3 deletions packages/demo/src/components/store.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {createStore} from 'redux';
import {MakeStore} from 'next-redux-wrapper';
import {MakeStore, createWrapper, Context} from 'next-redux-wrapper';
import reducer, {State} from './reducer';

export const makeStore: MakeStore = (initialState: State) => {
const store = createStore(reducer, initialState);
export const makeStore: MakeStore<State> = (context: Context) => {
const store = createStore(reducer);

if (module.hot) {
module.hot.accept('./reducer', () => {
Expand All @@ -14,3 +14,5 @@ export const makeStore: MakeStore = (initialState: State) => {

return store;
};

export const wrapper = createWrapper<State>(makeStore, {debug: true});
Loading

0 comments on commit 59b798d

Please sign in to comment.