From 24f3f143a687ebb365c828a7ee72968a35c23594 Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Wed, 3 May 2017 09:40:09 -0700 Subject: [PATCH] Introduce "asPath" into router and getInitialProps (#1857) * Add asPath to next/router and getInitialProps context. * Add test cases. * Update docs. * Build as-path pages before they use. --- client/index.js | 10 +++-- lib/router/index.js | 2 +- lib/router/router.js | 10 ++--- readme.md | 2 + server/render.js | 3 +- .../basic/pages/nav/as-path-using-router.js | 22 +++++++++++ test/integration/basic/pages/nav/as-path.js | 15 +++++++ test/integration/basic/pages/nav/index.js | 3 ++ .../basic/test/client-navigation.js | 39 +++++++++++++++++++ test/integration/basic/test/index.test.js | 2 + test/integration/basic/test/rendering.js | 9 ++++- test/lib/next-test-utils.js | 3 +- 12 files changed, 106 insertions(+), 14 deletions(-) create mode 100644 test/integration/basic/pages/nav/as-path-using-router.js create mode 100644 test/integration/basic/pages/nav/as-path.js diff --git a/client/index.js b/client/index.js index 55f9e052..0a08779c 100644 --- a/client/index.js +++ b/client/index.js @@ -29,6 +29,8 @@ const { location } = window +const asPath = getURL() + const pageLoader = new PageLoader(buildId, assetPrefix) window.__NEXT_LOADED_PAGES__.forEach(({ route, fn }) => { pageLoader.registerPage(route, fn) @@ -56,7 +58,7 @@ export default async () => { Component = ErrorComponent } - router = createRouter(pathname, query, getURL(), { + router = createRouter(pathname, query, asPath, { pageLoader, Component, ErrorComponent, @@ -107,7 +109,7 @@ export async function renderError (error) { console.error(errorMessage) if (prod) { - const initProps = { err: error, pathname, query } + const initProps = { err: error, pathname, query, asPath } const props = await loadGetInitialProps(ErrorComponent, initProps) ReactDOM.render(createElement(ErrorComponent, props), errorContainer) } else { @@ -120,8 +122,8 @@ async function doRender ({ Component, props, hash, err, emitter }) { Component !== ErrorComponent && lastAppProps.Component === ErrorComponent) { // fetch props if ErrorComponent was replaced with a page component by HMR - const { pathname, query } = router - props = await loadGetInitialProps(Component, { err, pathname, query }) + const { pathname, query, asPath } = router + props = await loadGetInitialProps(Component, { err, pathname, query, asPath }) } if (emitter) { diff --git a/lib/router/index.js b/lib/router/index.js index faec82cc..1efaf3e9 100644 --- a/lib/router/index.js +++ b/lib/router/index.js @@ -13,7 +13,7 @@ const SingletonRouter = { } // Create public properties and methods of the router in the SingletonRouter -const propertyFields = ['components', 'pathname', 'route', 'query'] +const propertyFields = ['components', 'pathname', 'route', 'query', 'asPath'] const coreMethodFields = ['push', 'replace', 'reload', 'back', 'prefetch'] const routerEvents = ['routeChangeStart', 'beforeHistoryChange', 'routeChangeComplete', 'routeChangeError'] diff --git a/lib/router/router.js b/lib/router/router.js index 8b1a4263..3fde1c98 100644 --- a/lib/router/router.js +++ b/lib/router/router.js @@ -27,7 +27,7 @@ export default class Router { this.ErrorComponent = ErrorComponent this.pathname = pathname this.query = query - this.as = as + this.asPath = as this.subscriptions = new Set() this.componentLoadCancel = null this.onPopState = this.onPopState.bind(this) @@ -190,7 +190,7 @@ export default class Router { } const { Component } = routeInfo - const ctx = { pathname, query } + const ctx = { pathname, query, asPath: as } routeInfo.props = await this.getInitialProps(Component, ctx) this.components[route] = routeInfo @@ -229,13 +229,13 @@ export default class Router { this.route = route this.pathname = pathname this.query = query - this.as = as + this.asPath = as this.notify(data) } onlyAHashChange (as) { - if (!this.as) return false - const [ oldUrlNoHash ] = this.as.split('#') + if (!this.asPath) return false + const [ oldUrlNoHash ] = this.asPath.split('#') const [ newUrlNoHash, newHash ] = as.split('#') // If the urls are change, there's more than a hash change diff --git a/readme.md b/readme.md index 8760c6ef..a540ff23 100644 --- a/readme.md +++ b/readme.md @@ -238,6 +238,7 @@ export default Page - `pathname` - path section of URL - `query` - query string section of URL parsed as an object +- `asPath` - the actual url path - `req` - HTTP request object (server only) - `res` - HTTP response object (server only) - `jsonPageRes` - [Fetch Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) object (client only) @@ -283,6 +284,7 @@ Each top-level component receives a `url` property with the following API: - `pathname` - `String` of the current path excluding the query string - `query` - `Object` with the parsed query string. Defaults to `{}` +- `asPath` - `String` of the actual path (including the query) shows in the browser - `push(url, as=url)` - performs a `pushState` call with the given url - `replace(url, as=url)` - performs a `replaceState` call with the given url diff --git a/server/render.js b/server/render.js index 9678993b..75ac75a2 100644 --- a/server/render.js +++ b/server/render.js @@ -54,7 +54,8 @@ async function doRender (req, res, pathname, query, { ]) Component = Component.default || Component Document = Document.default || Document - const ctx = { err, req, res, pathname, query } + const asPath = req.url + const ctx = { err, req, res, pathname, query, asPath } const props = await loadGetInitialProps(Component, ctx) // the response might be finshed on the getinitialprops call diff --git a/test/integration/basic/pages/nav/as-path-using-router.js b/test/integration/basic/pages/nav/as-path-using-router.js new file mode 100644 index 00000000..e56634a1 --- /dev/null +++ b/test/integration/basic/pages/nav/as-path-using-router.js @@ -0,0 +1,22 @@ +import React from 'react' +import Router from 'next/router' + +export default class extends React.Component { + constructor (...args) { + super(...args) + this.state = {} + } + + componentDidMount () { + const asPath = Router.asPath + this.setState({ asPath }) + } + + render () { + return ( +
+ {this.state.asPath} +
+ ) + } +} diff --git a/test/integration/basic/pages/nav/as-path.js b/test/integration/basic/pages/nav/as-path.js new file mode 100644 index 00000000..03328489 --- /dev/null +++ b/test/integration/basic/pages/nav/as-path.js @@ -0,0 +1,15 @@ +import React from 'react' + +export default class extends React.Component { + static getInitialProps ({ asPath, req }) { + return { asPath } + } + + render () { + return ( +
+ {this.props.asPath} +
+ ) + } +} diff --git a/test/integration/basic/pages/nav/index.js b/test/integration/basic/pages/nav/index.js index f92a7cba..0a364ddf 100644 --- a/test/integration/basic/pages/nav/index.js +++ b/test/integration/basic/pages/nav/index.js @@ -35,6 +35,9 @@ export default class extends Component { QueryString Replace state + As Path + As Path (No as) + As Path (Using Router)