Skip to content

Commit

Permalink
Fix async in getServerSideProps
Browse files Browse the repository at this point in the history
Fix #235
Fix #223
  • Loading branch information
kirill-konshin committed Jun 4, 2020
1 parent 6433f8b commit 3470c42
Show file tree
Hide file tree
Showing 15 changed files with 203 additions and 4 deletions.
3 changes: 2 additions & 1 deletion packages/demo-page/src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ const Page: NextPage<ConnectedPageProps> = ({custom}) => {
);
};

export const getServerSideProps = wrapper.getServerSideProps(({store, req}) => {
export const getServerSideProps = wrapper.getServerSideProps(async ({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});
await new Promise(res => setTimeout(res, 1000));
return {props: {custom: 'custom'}};
});

Expand Down
2 changes: 1 addition & 1 deletion packages/demo-page/src/pages/other.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {useSelector, useDispatch} from 'react-redux';
import {State} from '../components/reducer';
import {wrapper} from '../components/store';

export const getStaticProps = wrapper.getStaticProps(({store, previewData}) => {
export const getStaticProps = wrapper.getStaticProps(async ({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})});
});
Expand Down
2 changes: 1 addition & 1 deletion packages/demo-page/src/pages/other2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {useSelector, useDispatch} from 'react-redux';
import {State} from '../components/reducer';
import {wrapper} from '../components/store';

export const getStaticProps = wrapper.getStaticProps(({store, previewData}) => {
export const getStaticProps = wrapper.getStaticProps(async ({store, previewData}) => {
console.log('2. Page.getStaticProps uses the store to dispatch things');
store.dispatch({type: 'PAGE', payload: 'was set in other (SECOND) page ' + JSON.stringify({previewData})});
});
Expand Down
2 changes: 2 additions & 0 deletions packages/demo-saga-page/jest-puppeteer.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module.exports = require('next-redux-wrapper-configs/jest-puppeteer.config');
module.exports.server.port = 5050;
1 change: 1 addition & 0 deletions packages/demo-saga-page/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('next-redux-wrapper-configs/jest.config.puppeteer');
2 changes: 2 additions & 0 deletions packages/demo-saga-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" />
53 changes: 53 additions & 0 deletions packages/demo-saga-page/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"name": "next-redux-wrapper-demo-saga-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=5050",
"build": "next build",
"serve": "next start --port=5050"
},
"dependencies": {
"jsondiffpatch": "0.4.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-logger": "3.0.6",
"redux-saga": "1.1.3"
},
"devDependencies": {
"@types/expect-puppeteer": "4.4.1",
"@types/jest": "25.2.1",
"@types/jest-environment-puppeteer": "4.3.1",
"@types/next-redux-saga": "3.0.2",
"@types/puppeteer": "2.0.1",
"@types/react": "16.9.35",
"@types/react-dom": "16.9.8",
"@types/react-redux": "7.1.8",
"@types/redux-logger": "3.0.7",
"@types/webpack-env": "1.15.2",
"jest": "26.0.1",
"jest-puppeteer": "4.4.0",
"next": "9.4.0",
"next-redux-wrapper-configs": "^6.0.0",
"puppeteer": "3.0.4",
"rimraf": "3.0.2",
"ts-jest": "25.5.1",
"typescript": "3.8.3"
},
"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"
}
22 changes: 22 additions & 0 deletions packages/demo-saga-page/src/components/reducer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {AnyAction} from 'redux';
import {HYDRATE} from 'next-redux-wrapper';
import {SAGA_ACTION_SUCCESS} from './saga';

export interface State {
page: string;
}

const initialState: State = {page: ''};

function rootReducer(state = initialState, action: AnyAction) {
switch (action.type) {
case HYDRATE:
return {...state, ...action.payload};
case SAGA_ACTION_SUCCESS:
return {...state, page: action.data};
default:
return state;
}
}

export default rootReducer;
18 changes: 18 additions & 0 deletions packages/demo-saga-page/src/components/saga.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {delay, put, takeEvery} from 'redux-saga/effects';

export const SAGA_ACTION = 'SAGA_ACTION';
export const SAGA_ACTION_SUCCESS = `${SAGA_ACTION}_SUCCESS`;

function* sagaAction() {
yield delay(100);
yield put({
type: SAGA_ACTION_SUCCESS,
data: 'async text',
});
}

function* rootSaga() {
yield takeEvery(SAGA_ACTION, sagaAction);
}

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

export interface SagaStore extends Store {
sagaTask: Task;
}

const makeStore: MakeStore<State> = (context: Context) => {
// 1: Create the middleware
const sagaMiddleware = createSagaMiddleware();

// 2: Add an extra parameter for applying middleware:
const store = createStore(reducer, applyMiddleware(sagaMiddleware, logger));

// 3: Run your sagas on server
(store as SagaStore).sagaTask = sagaMiddleware.run(rootSaga);

// 4: now return the store:
return store;
};

export const wrapper = createWrapper(makeStore);
7 changes: 7 additions & 0 deletions packages/demo-saga-page/src/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import React, {FC} from 'react';
import {AppProps} from 'next/app';
import {wrapper} from '../components/store';

const WrappedApp: FC<AppProps> = ({Component, pageProps}) => <Component {...pageProps} />;

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

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}: ConnectedPageProps) => {
const {page} = useSelector<State, State>(state => state);
return (
<div className="index">
<pre>{JSON.stringify({page, custom}, null, 2)}</pre>
</div>
);
};

export const getServerSideProps = wrapper.getServerSideProps(async ({store}) => {
store.dispatch({type: SAGA_ACTION});
store.dispatch(END);
await (store as SagaStore).sagaTask.toPromise();

return {props: {custom: 'custom'}};
});

export default Page;
14 changes: 14 additions & 0 deletions packages/demo-saga-page/tests/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
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": "async text"');
await expect(page).toMatch('"custom": "custom"');
});
});
20 changes: 20 additions & 0 deletions packages/demo-saga-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"
],
"compilerOptions": {
"allowJs": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"module": "esnext",
"outDir": "lib",
"isolatedModules": true,
"jsx": "preserve"
},
"exclude": [
"node_modules"
]
}
4 changes: 3 additions & 1 deletion packages/wrapper/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,9 @@ export const createWrapper = <S extends {} = any, A extends Action = AnyAction>(

const getServerSideProps = <P extends {} = any>(
callback: (context: GetServerSidePropsContext & {store: Store<S, A>}) => P | void,
): GetServerSideProps<P> => getStaticProps<P>(callback as any) as any; // just not to repeat myself
): GetServerSideProps<P> => async (context: any) => {
return await getStaticProps(callback as any)(context); // just not to repeat myself
};

const withRedux = (Component: NextComponentType | App | any) => {
const displayName = `withRedux(${Component.displayName || Component.name || 'Component'})`;
Expand Down

0 comments on commit 3470c42

Please sign in to comment.