From aaa694da9b8928cda53125598159817c776b5e9c Mon Sep 17 00:00:00 2001 From: Henrik Wenz Date: Wed, 8 Aug 2018 15:13:20 +0200 Subject: [PATCH 1/3] Throw error if getInitialProps is defined as as instance method Omitting the static keyword happens pretty often. Therefore we should trigger a warning in devmode. Closes: #4782 --- lib/utils.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/utils.js b/lib/utils.js index 22faec6a62f2a..2474870175c41 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -54,6 +54,14 @@ export function isResSent (res) { } export async function loadGetInitialProps (Component, ctx) { + if (process.env.NODE_ENV !== 'production') { + if (Component.prototype && Component.prototype.getInitialProps) { + const compName = getDisplayName(Component) + const message = `"${compName}.getInitialProps()" is defined as an instance method.` + throw new Error(message) + } + } + if (!Component.getInitialProps) return {} const props = await Component.getInitialProps(ctx) From 212c44f37f12c2194158d652d47d3bd636f9d458 Mon Sep 17 00:00:00 2001 From: Henrik Wenz Date: Wed, 8 Aug 2018 18:47:22 +0200 Subject: [PATCH 2/3] Document getInitialProps error --- .../get-inital-props-as-an-instance-method.md | 39 +++++++++++++++++++ lib/utils.js | 2 +- 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 errors/get-inital-props-as-an-instance-method.md diff --git a/errors/get-inital-props-as-an-instance-method.md b/errors/get-inital-props-as-an-instance-method.md new file mode 100644 index 0000000000000..d9c58e617f182 --- /dev/null +++ b/errors/get-inital-props-as-an-instance-method.md @@ -0,0 +1,39 @@ +# getInitialProps was defined as an instance method + +#### Why This Error Occurred + +`getInitialProps` must be a static method in order to be called by next.js. + +#### Possible Ways to Fix It + +Use the static keyword. + +```js +export default class YourEntryComponent extends React.Component { + static getInitialProps () { + return {} + } + + render () { + return 'foo' + } +} +``` + +or + +```js +const YourEntryComponent = function () { + return 'foo' +} + +YourEntryComponent.getInitialProps = () => { + return {} +} + +export default YourEntryComponent +``` + +### Useful Links + +- [Fetching data and component lifecycle](https://github.com/zeit/next.js#fetching-data-and-component-lifecycle) diff --git a/lib/utils.js b/lib/utils.js index 2474870175c41..c1daa56f14994 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -57,7 +57,7 @@ export async function loadGetInitialProps (Component, ctx) { if (process.env.NODE_ENV !== 'production') { if (Component.prototype && Component.prototype.getInitialProps) { const compName = getDisplayName(Component) - const message = `"${compName}.getInitialProps()" is defined as an instance method.` + const message = `"${compName}.getInitialProps()" is defined as an instance method - visit https://err.sh/next.js/get-inital-props-as-an-instance-method for more information.` throw new Error(message) } } From 7a0b3d7fa271d5efe1b07dfe66303e83c0ac2dc9 Mon Sep 17 00:00:00 2001 From: Henrik Wenz Date: Thu, 9 Aug 2018 10:10:50 +0200 Subject: [PATCH 3/3] Add unit tests for loadGetInitialProps --- test/unit/loadGetInitialProps.test.js | 52 +++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 test/unit/loadGetInitialProps.test.js diff --git a/test/unit/loadGetInitialProps.test.js b/test/unit/loadGetInitialProps.test.js new file mode 100644 index 0000000000000..860e90406b64c --- /dev/null +++ b/test/unit/loadGetInitialProps.test.js @@ -0,0 +1,52 @@ +/* global describe, it, expect */ +import { loadGetInitialProps } from '../../dist/lib/utils' + +describe('loadGetInitialProps', () => { + it('should throw if getInitialProps is defined as an instance method', () => { + class TestComponent { + getInitialProps () {} + } + const rejectPromise = loadGetInitialProps(TestComponent, {}) + const error = new Error('"TestComponent.getInitialProps()" is defined as an instance method - visit https://err.sh/next.js/get-inital-props-as-an-instance-method for more information.') + return expect(rejectPromise).rejects.toEqual(error) + }) + + it('should resolve to an object if getInitialProps is missing', async () => { + const result = await loadGetInitialProps(() => {}, {}) + expect(result).toEqual({}) + }) + + it('should resolve getInitialProps', async () => { + class TestComponent { + static async getInitialProps () { + return { foo: 1 } + } + } + const result = await loadGetInitialProps(TestComponent, {}) + expect(result).toEqual({ foo: 1 }) + }) + + it('should be able to return an invalid value if the request was already sent', async () => { + class TestComponent { + static async getInitialProps () { + return 'invalidValue' + } + } + const ctx = { + res: { + finished: true + } + } + const result = await loadGetInitialProps(TestComponent, ctx) + expect(result).toBe('invalidValue') + }) + + it('should throw if getInitialProps won\'t return an object ', () => { + class TestComponent { + static async getInitialProps () {} + } + const rejectPromise = loadGetInitialProps(TestComponent, {}) + const error = new Error('"TestComponent.getInitialProps()" should resolve to an object. But found "undefined" instead.') + return expect(rejectPromise).rejects.toEqual(error) + }) +})