diff --git a/lib/app.js b/lib/app.js
index ebee26d13970b..7cecba362e36a 100644
--- a/lib/app.js
+++ b/lib/app.js
@@ -93,18 +93,20 @@ const warnUrl = execOnce(() => {
})
export function createUrl (router) {
+ // This is to make sure we don't references the router object at call time
+ const {pathname, asPath, query} = router
return {
get query () {
warnUrl()
- return router.query
+ return query
},
get pathname () {
warnUrl()
- return router.pathname
+ return pathname
},
get asPath () {
warnUrl()
- return router.asPath
+ return asPath
},
back: () => {
warnUrl()
diff --git a/test/integration/basic/pages/nav/url-prop-change.js b/test/integration/basic/pages/nav/url-prop-change.js
new file mode 100644
index 0000000000000..79715a087db61
--- /dev/null
+++ b/test/integration/basic/pages/nav/url-prop-change.js
@@ -0,0 +1,37 @@
+import React from 'react'
+import Link from 'next/link'
+
+export default class UrlPropChange extends React.Component {
+ constructor (props) {
+ super(props)
+ this.state = {
+ previousUrl: {},
+ url: props.url
+ }
+ }
+
+ componentWillReceiveProps (nextProps) {
+ this.setState(() => {
+ return {
+ previousUrl: this.props.url,
+ url: nextProps.url
+ }
+ })
+ }
+
+ render () {
+ const {previousUrl, url} = this.state
+ return
+ Current:
+
+ {JSON.stringify(url)}
+
+
+ Previous:
+
+ {JSON.stringify(previousUrl)}
+
+
Add querystring
+
+ }
+}
diff --git a/test/integration/basic/test/client-navigation.js b/test/integration/basic/test/client-navigation.js
index 192cc523f845d..55b0d3b5218a4 100644
--- a/test/integration/basic/test/client-navigation.js
+++ b/test/integration/basic/test/client-navigation.js
@@ -33,6 +33,20 @@ export default (context, render) => {
})
})
+ describe('With url property', () => {
+ it('Should keep immutable pathname, asPath and query', async () => {
+ const browser = await webdriver(context.appPort, '/nav/url-prop-change')
+ await browser.elementByCss('#add-query').click()
+ const urlResult = await browser.elementByCss('#url-result').text()
+ const previousUrlResult = await browser.elementByCss('#previous-url-result').text()
+
+ expect(JSON.parse(urlResult)).toMatchObject({'query': {'added': 'yes'}, 'pathname': '/nav/url-prop-change', 'asPath': '/nav/url-prop-change?added=yes'})
+ expect(JSON.parse(previousUrlResult)).toMatchObject({'query': {}, 'pathname': '/nav/url-prop-change', 'asPath': '/nav/url-prop-change'})
+
+ browser.close()
+ })
+ })
+
describe('with tag inside the ', () => {
it('should navigate the page', async () => {
const browser = await webdriver(context.appPort, '/nav/about')
diff --git a/test/integration/basic/test/index.test.js b/test/integration/basic/test/index.test.js
index 751618175da90..d79a2684a4935 100644
--- a/test/integration/basic/test/index.test.js
+++ b/test/integration/basic/test/index.test.js
@@ -52,6 +52,7 @@ describe('Basic Features', () => {
renderViaHTTP(context.appPort, '/nav/redirect'),
renderViaHTTP(context.appPort, '/nav/as-path'),
renderViaHTTP(context.appPort, '/nav/as-path-using-router'),
+ renderViaHTTP(context.appPort, '/nav/url-prop-change'),
renderViaHTTP(context.appPort, '/nested-cdm/index'),