mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
Introducing Shallow Routing (#1357)
* Simplify route info handling. * Add basic resolve=false support. * Make sure to render getInitialProps always if it's the first render. * Change resolve=false to shallow routing. * Add test cases for shallow routing. * Update README for shallow routing docs. * Update docs. * Update docs. * Update docs.
This commit is contained in:
parent
76698eaa08
commit
f8f3fa7dce
30
examples/with-shallow-routing/README.md
Normal file
30
examples/with-shallow-routing/README.md
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
|
||||||
|
# Shallow Routing Example
|
||||||
|
|
||||||
|
## How to use
|
||||||
|
|
||||||
|
Download the example [or clone the repo](https://github.com/zeit/next.js):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl https://codeload.github.com/zeit/next.js/tar.gz/master | tar -xz --strip=2 next.js-master/examples/hello-world
|
||||||
|
cd hello-world
|
||||||
|
```
|
||||||
|
|
||||||
|
Install it and run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Deploy it to the cloud with [now](https://zeit.co/now) ([download](https://zeit.co/download))
|
||||||
|
|
||||||
|
```bash
|
||||||
|
now
|
||||||
|
```
|
||||||
|
|
||||||
|
## The idea behind the example
|
||||||
|
|
||||||
|
With shallow routing, we could change the URL without actually running the `getInitialProps` every time you change the URL.
|
||||||
|
|
||||||
|
We do this passing the `shallow: true` option to `Router.push` or `Router.replace`.
|
16
examples/with-shallow-routing/package.json
Normal file
16
examples/with-shallow-routing/package.json
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"name": "with-shallow-routing",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "next",
|
||||||
|
"build": "next build",
|
||||||
|
"start": "next start"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"next": "next@beta",
|
||||||
|
"react": "^15.4.2",
|
||||||
|
"react-dom": "^15.4.2"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC"
|
||||||
|
}
|
3
examples/with-shallow-routing/pages/about.js
Normal file
3
examples/with-shallow-routing/pages/about.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export default () => (
|
||||||
|
<div>About us</div>
|
||||||
|
)
|
46
examples/with-shallow-routing/pages/index.js
Normal file
46
examples/with-shallow-routing/pages/index.js
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import React from 'react'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import Router from 'next/router'
|
||||||
|
import { format } from 'url'
|
||||||
|
|
||||||
|
let counter = 1
|
||||||
|
|
||||||
|
export default class Index extends React.Component {
|
||||||
|
static getInitialProps ({ res }) {
|
||||||
|
if (res) {
|
||||||
|
return { initialPropsCounter: 1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
counter++
|
||||||
|
return {
|
||||||
|
initialPropsCounter: counter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reload () {
|
||||||
|
const { pathname, query } = Router
|
||||||
|
Router.push(format({ pathname, query }))
|
||||||
|
}
|
||||||
|
|
||||||
|
incrementStateCounter () {
|
||||||
|
const { url } = this.props
|
||||||
|
const currentCounter = url.query.counter ? parseInt(url.query.counter) : 0
|
||||||
|
const href = `/?counter=${currentCounter + 1}`
|
||||||
|
Router.push(href, href, { shallow: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { initialPropsCounter, url } = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h2>This is the Home Page</h2>
|
||||||
|
<Link href='/about'><a>About</a></Link>
|
||||||
|
<button onClick={() => this.reload()}>Reload</button>
|
||||||
|
<button onClick={() => this.incrementStateCounter()}>Change State Counter</button>
|
||||||
|
<p>"getInitialProps" ran for "{initialPropsCounter}" times.</p>
|
||||||
|
<p>Counter: "{url.query.counter || 0}".</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
26
lib/app.js
26
lib/app.js
|
@ -1,7 +1,6 @@
|
||||||
import React, { Component, PropTypes } from 'react'
|
import React, { Component, PropTypes } from 'react'
|
||||||
import { AppContainer } from 'react-hot-loader'
|
import { AppContainer } from 'react-hot-loader'
|
||||||
import shallowEquals from './shallow-equals'
|
import shallowEquals from './shallow-equals'
|
||||||
import { warn } from './utils'
|
|
||||||
|
|
||||||
const ErrorDebug = process.env.NODE_ENV === 'production'
|
const ErrorDebug = process.env.NODE_ENV === 'production'
|
||||||
? null : require('./error-debug').default
|
? null : require('./error-debug').default
|
||||||
|
@ -18,7 +17,8 @@ export default class App extends Component {
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { Component, props, hash, err, router } = this.props
|
const { Component, props, hash, err, router } = this.props
|
||||||
const containerProps = { Component, props, hash, router }
|
const url = createUrl(router)
|
||||||
|
const containerProps = { Component, props, hash, router, url }
|
||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
<Container {...containerProps} />
|
<Container {...containerProps} />
|
||||||
|
@ -52,8 +52,7 @@ class Container extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { Component, props, router } = this.props
|
const { Component, props, url } = this.props
|
||||||
const url = createUrl(router)
|
|
||||||
|
|
||||||
// includes AppContainer which bypasses shouldComponentUpdate method
|
// includes AppContainer which bypasses shouldComponentUpdate method
|
||||||
// https://github.com/gaearon/react-hot-loader/issues/442
|
// https://github.com/gaearon/react-hot-loader/issues/442
|
||||||
|
@ -66,23 +65,6 @@ class Container extends Component {
|
||||||
function createUrl (router) {
|
function createUrl (router) {
|
||||||
return {
|
return {
|
||||||
query: router.query,
|
query: router.query,
|
||||||
pathname: router.pathname,
|
pathname: router.pathname
|
||||||
back: () => router.back(),
|
|
||||||
push: (url, as) => router.push(url, as),
|
|
||||||
pushTo: (href, as) => {
|
|
||||||
warn(`Warning: 'url.pushTo()' is deprecated. Please use 'url.push()' instead.`)
|
|
||||||
const pushRoute = as ? href : null
|
|
||||||
const pushUrl = as || href
|
|
||||||
|
|
||||||
return router.push(pushRoute, pushUrl)
|
|
||||||
},
|
|
||||||
replace: (url, as) => router.replace(url, as),
|
|
||||||
replaceTo: (href, as) => {
|
|
||||||
warn(`Warning: 'url.replaceTo()' is deprecated. Please use 'url.replace()' instead.`)
|
|
||||||
const replaceRoute = as ? href : null
|
|
||||||
const replaceUrl = as || href
|
|
||||||
|
|
||||||
return router.replace(replaceRoute, replaceUrl)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,12 +71,16 @@ export default class Router extends EventEmitter {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const { url, as } = e.state
|
const { url, as, options } = e.state
|
||||||
this.replace(url, as)
|
this.replace(url, as, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
update (route, Component) {
|
update (route, Component) {
|
||||||
const data = this.components[route] || {}
|
const data = this.components[route]
|
||||||
|
if (!data) {
|
||||||
|
throw new Error(`Cannot update unavailable route: ${route}`)
|
||||||
|
}
|
||||||
|
|
||||||
const newData = { ...data, Component }
|
const newData = { ...data, Component }
|
||||||
this.components[route] = newData
|
this.components[route] = newData
|
||||||
|
|
||||||
|
@ -95,17 +99,14 @@ export default class Router extends EventEmitter {
|
||||||
const { pathname, query } = parse(url, true)
|
const { pathname, query } = parse(url, true)
|
||||||
|
|
||||||
this.emit('routeChangeStart', url)
|
this.emit('routeChangeStart', url)
|
||||||
const {
|
const routeInfo = await this.getRouteInfo(route, pathname, query, url)
|
||||||
data,
|
const { error } = routeInfo
|
||||||
props,
|
|
||||||
error
|
|
||||||
} = await this.getRouteInfo(route, pathname, query, url)
|
|
||||||
|
|
||||||
if (error && error.cancelled) {
|
if (error && error.cancelled) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.notify({ ...data, props })
|
this.notify(routeInfo)
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
this.emit('routeChangeError', error, url)
|
this.emit('routeChangeError', error, url)
|
||||||
|
@ -119,15 +120,15 @@ export default class Router extends EventEmitter {
|
||||||
window.history.back()
|
window.history.back()
|
||||||
}
|
}
|
||||||
|
|
||||||
push (url, as = url) {
|
push (url, as = url, options = {}) {
|
||||||
return this.change('pushState', url, as)
|
return this.change('pushState', url, as, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
replace (url, as = url) {
|
replace (url, as = url, options = {}) {
|
||||||
return this.change('replaceState', url, as)
|
return this.change('replaceState', url, as, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
async change (method, url, as) {
|
async change (method, url, as, options) {
|
||||||
this.abortComponentLoad(as)
|
this.abortComponentLoad(as)
|
||||||
const { pathname, query } = parse(url, true)
|
const { pathname, query } = parse(url, true)
|
||||||
|
|
||||||
|
@ -147,21 +148,30 @@ export default class Router extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
const route = toRoute(pathname)
|
const route = toRoute(pathname)
|
||||||
|
const { shallow = false } = options
|
||||||
|
let routeInfo = null
|
||||||
|
|
||||||
this.emit('routeChangeStart', as)
|
this.emit('routeChangeStart', as)
|
||||||
const {
|
|
||||||
data, props, error
|
// If shallow === false and other conditions met, we reuse the
|
||||||
} = await this.getRouteInfo(route, pathname, query, as)
|
// existing routeInfo for this route.
|
||||||
|
// Because of this, getInitialProps would not run.
|
||||||
|
if (shallow && this.isShallowRoutingPossible(route)) {
|
||||||
|
routeInfo = this.components[route]
|
||||||
|
} else {
|
||||||
|
routeInfo = await this.getRouteInfo(route, pathname, query, as)
|
||||||
|
}
|
||||||
|
|
||||||
|
const { error } = routeInfo
|
||||||
|
|
||||||
if (error && error.cancelled) {
|
if (error && error.cancelled) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
this.changeState(method, url, as)
|
this.changeState(method, url, as, options)
|
||||||
const hash = window.location.hash.substring(1)
|
const hash = window.location.hash.substring(1)
|
||||||
|
|
||||||
this.route = route
|
this.set(route, pathname, query, as, { ...routeInfo, hash })
|
||||||
this.set(pathname, query, as, { ...data, props, hash })
|
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
this.emit('routeChangeError', error, as)
|
this.emit('routeChangeError', error, as)
|
||||||
|
@ -172,31 +182,33 @@ export default class Router extends EventEmitter {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
changeState (method, url, as) {
|
changeState (method, url, as, options = {}) {
|
||||||
if (method !== 'pushState' || getURL() !== as) {
|
if (method !== 'pushState' || getURL() !== as) {
|
||||||
window.history[method]({ url, as }, null, as)
|
window.history[method]({ url, as, options }, null, as)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getRouteInfo (route, pathname, query, as) {
|
async getRouteInfo (route, pathname, query, as) {
|
||||||
const routeInfo = {}
|
let routeInfo = null
|
||||||
|
|
||||||
try {
|
try {
|
||||||
routeInfo.data = await this.fetchComponent(route, as)
|
routeInfo = this.components[route]
|
||||||
if (!routeInfo.data) {
|
if (!routeInfo) {
|
||||||
return null
|
routeInfo = await this.fetchComponent(route, as)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { Component, err, jsonPageRes } = routeInfo.data
|
const { Component, err, jsonPageRes } = routeInfo
|
||||||
const ctx = { err, pathname, query, jsonPageRes }
|
const ctx = { err, pathname, query, jsonPageRes }
|
||||||
routeInfo.props = await this.getInitialProps(Component, ctx)
|
routeInfo.props = await this.getInitialProps(Component, ctx)
|
||||||
|
|
||||||
|
this.components[route] = routeInfo
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.cancelled) {
|
if (err.cancelled) {
|
||||||
return { error: err }
|
return { error: err }
|
||||||
}
|
}
|
||||||
|
|
||||||
const Component = this.ErrorComponent
|
const Component = this.ErrorComponent
|
||||||
routeInfo.data = { Component, err }
|
routeInfo = { Component, err }
|
||||||
const ctx = { err, pathname, query }
|
const ctx = { err, pathname, query }
|
||||||
routeInfo.props = await this.getInitialProps(Component, ctx)
|
routeInfo.props = await this.getInitialProps(Component, ctx)
|
||||||
|
|
||||||
|
@ -207,7 +219,8 @@ export default class Router extends EventEmitter {
|
||||||
return routeInfo
|
return routeInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
set (pathname, query, as, data) {
|
set (route, pathname, query, as, data) {
|
||||||
|
this.route = route
|
||||||
this.pathname = pathname
|
this.pathname = pathname
|
||||||
this.query = query
|
this.query = query
|
||||||
this.as = as
|
this.as = as
|
||||||
|
@ -238,6 +251,15 @@ export default class Router extends EventEmitter {
|
||||||
return this.pathname !== pathname || !shallowEquals(query, this.query)
|
return this.pathname !== pathname || !shallowEquals(query, this.query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isShallowRoutingPossible (route) {
|
||||||
|
return (
|
||||||
|
// If there's cached routeInfo for the route.
|
||||||
|
Boolean(this.components[route]) &&
|
||||||
|
// If the route is already rendered on the screen.
|
||||||
|
this.route === route
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
async prefetch (url) {
|
async prefetch (url) {
|
||||||
// We don't add support for prefetch in the development mode.
|
// We don't add support for prefetch in the development mode.
|
||||||
// If we do that, our on-demand-entries optimization won't performs better
|
// If we do that, our on-demand-entries optimization won't performs better
|
||||||
|
@ -249,9 +271,6 @@ export default class Router extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchComponent (route, as) {
|
async fetchComponent (route, as) {
|
||||||
let data = this.components[route]
|
|
||||||
if (data) return data
|
|
||||||
|
|
||||||
let cancelled = false
|
let cancelled = false
|
||||||
const cancel = this.componentLoadCancel = function () {
|
const cancel = this.componentLoadCancel = function () {
|
||||||
cancelled = true
|
cancelled = true
|
||||||
|
@ -283,7 +302,6 @@ export default class Router extends EventEmitter {
|
||||||
this.componentLoadCancel = null
|
this.componentLoadCancel = null
|
||||||
}
|
}
|
||||||
|
|
||||||
this.components[route] = newData
|
|
||||||
return newData
|
return newData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
45
readme.md
45
readme.md
|
@ -25,6 +25,7 @@ _**NOTE! the README on the `master` branch might not match that of the [latest s
|
||||||
- [With `<Link>`](#with-link)
|
- [With `<Link>`](#with-link)
|
||||||
- [Imperatively](#imperatively)
|
- [Imperatively](#imperatively)
|
||||||
- [Router Events](#router-events)
|
- [Router Events](#router-events)
|
||||||
|
- [Shallow Routing](#shallow-routing)
|
||||||
- [Prefetching Pages](#prefetching-pages)
|
- [Prefetching Pages](#prefetching-pages)
|
||||||
- [With `<Link>`](#with-link-1)
|
- [With `<Link>`](#with-link-1)
|
||||||
- [Imperatively](#imperatively-1)
|
- [Imperatively](#imperatively-1)
|
||||||
|
@ -349,6 +350,50 @@ Router.onAppUpdated = (nextUrl) => {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
##### Shallow Routing
|
||||||
|
|
||||||
|
<p><details>
|
||||||
|
<summary><b>Examples</b></summary>
|
||||||
|
<ul>
|
||||||
|
<li><a href="./examples/with-shallow-routing">Shallow Routing</a></li>
|
||||||
|
</ul>
|
||||||
|
</details></p>
|
||||||
|
|
||||||
|
With shallow routing you could chnage the URL without running `getInitialProps` of the page. You'll receive the updated "pathname" and the "query" via the `url` prop of the page.
|
||||||
|
|
||||||
|
You can do this by invoking the eith `Router.push` or `Router.replace` with `shallow: true` option. Here's an example:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Current URL is "/"
|
||||||
|
const href = '/?counter=10'
|
||||||
|
const as = href
|
||||||
|
Router.push(href, as, { shallow: true })
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, the URL is updated to "/?counter=10" and page is re-rendered.
|
||||||
|
You can see the updated URL with `this.props.url` inside the Component.
|
||||||
|
|
||||||
|
You can also watch for URL changes via [`componentWillReceiveProps`](https://facebook.github.io/react/docs/react-component.html#componentwillreceiveprops) hook as shown below:
|
||||||
|
|
||||||
|
```
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
const { pathname, query } = nextProps.url
|
||||||
|
// fetch data based on the new query
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> NOTES:
|
||||||
|
>
|
||||||
|
> Shallow routing works **only** for same page URL changes.
|
||||||
|
>
|
||||||
|
> For an example, let's assume we've another page called "about".
|
||||||
|
> Now you are changing a URL like this:
|
||||||
|
> ```js
|
||||||
|
> Router.push('/about?counter=10', '/about?counter=10', { shallow: true })
|
||||||
|
> ```
|
||||||
|
> Since that's a new page, it'll run "getInitialProps" of the "about" page even we asked to do shallow routing.
|
||||||
|
|
||||||
|
|
||||||
### Prefetching Pages
|
### Prefetching Pages
|
||||||
|
|
||||||
(This is a production only feature)
|
(This is a production only feature)
|
||||||
|
|
|
@ -18,7 +18,8 @@ export default class extends Component {
|
||||||
<div className='nav-home'>
|
<div className='nav-home'>
|
||||||
<Link href='/nav/about'><a id='about-link' style={linkStyle}>About</a></Link>
|
<Link href='/nav/about'><a id='about-link' style={linkStyle}>About</a></Link>
|
||||||
<Link href='/empty-get-initial-props'><a id='empty-props' style={linkStyle}>Empty Props</a></Link>
|
<Link href='/empty-get-initial-props'><a id='empty-props' style={linkStyle}>Empty Props</a></Link>
|
||||||
<Link href='/nav/self-reload'><a id='self-reload-link'>Self Reload</a></Link>
|
<Link href='/nav/self-reload'><a id='self-reload-link' style={linkStyle}>Self Reload</a></Link>
|
||||||
|
<Link href='/nav/shallow-routing'><a id='shallow-routing-link' style={linkStyle}>Shallow Routing</a></Link>
|
||||||
<p>This is the home.</p>
|
<p>This is the home.</p>
|
||||||
<div id='counter'>
|
<div id='counter'>
|
||||||
Counter: {counter}
|
Counter: {counter}
|
||||||
|
|
44
test/integration/basic/pages/nav/shallow-routing.js
Normal file
44
test/integration/basic/pages/nav/shallow-routing.js
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import { Component } from 'react'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import Router from 'next/router'
|
||||||
|
|
||||||
|
let getInitialPropsRunCount = 1
|
||||||
|
|
||||||
|
const linkStyle = {
|
||||||
|
marginRight: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class extends Component {
|
||||||
|
static getInitialProps ({ res }) {
|
||||||
|
if (res) return { getInitialPropsRunCount: 1 }
|
||||||
|
getInitialPropsRunCount++
|
||||||
|
|
||||||
|
return { getInitialPropsRunCount }
|
||||||
|
}
|
||||||
|
|
||||||
|
increase () {
|
||||||
|
const counter = this.getCurrentCounter()
|
||||||
|
const href = `/nav/shallow-routing?counter=${counter + 1}`
|
||||||
|
Router.push(href, href, { shallow: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentCounter () {
|
||||||
|
const { url } = this.props
|
||||||
|
return url.query.counter ? parseInt(url.query.counter) : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<div className='shallow-routing'>
|
||||||
|
<Link href='/nav'><a id='home-link' style={linkStyle}>Home</a></Link>
|
||||||
|
<div id='counter'>
|
||||||
|
Counter: {this.getCurrentCounter()}
|
||||||
|
</div>
|
||||||
|
<div id='get-initial-props-run-count'>
|
||||||
|
getInitialProps run count: {this.props.getInitialPropsRunCount}
|
||||||
|
</div>
|
||||||
|
<button id='increase' onClick={() => this.increase()}>Increase</button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -179,5 +179,62 @@ export default (context, render) => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('with shallow routing', () => {
|
||||||
|
it('should not update the url without running getInitialProps', async () => {
|
||||||
|
const browser = await webdriver(context.appPort, '/nav/shallow-routing')
|
||||||
|
const counter = await browser
|
||||||
|
.elementByCss('#increase').click()
|
||||||
|
.elementByCss('#increase').click()
|
||||||
|
.elementByCss('#counter').text()
|
||||||
|
expect(counter).toBe('Counter: 2')
|
||||||
|
|
||||||
|
const getInitialPropsRunCount = await browser
|
||||||
|
.elementByCss('#get-initial-props-run-count').text()
|
||||||
|
expect(getInitialPropsRunCount).toBe('getInitialProps run count: 1')
|
||||||
|
|
||||||
|
await browser.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle back button and should not run getInitialProps', async () => {
|
||||||
|
const browser = await webdriver(context.appPort, '/nav/shallow-routing')
|
||||||
|
let counter = await browser
|
||||||
|
.elementByCss('#increase').click()
|
||||||
|
.elementByCss('#increase').click()
|
||||||
|
.elementByCss('#counter').text()
|
||||||
|
expect(counter).toBe('Counter: 2')
|
||||||
|
|
||||||
|
counter = await browser
|
||||||
|
.back()
|
||||||
|
.elementByCss('#counter').text()
|
||||||
|
expect(counter).toBe('Counter: 1')
|
||||||
|
|
||||||
|
const getInitialPropsRunCount = await browser
|
||||||
|
.elementByCss('#get-initial-props-run-count').text()
|
||||||
|
expect(getInitialPropsRunCount).toBe('getInitialProps run count: 1')
|
||||||
|
|
||||||
|
await browser.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should run getInitialProps always when rending the page to the screen', async () => {
|
||||||
|
const browser = await webdriver(context.appPort, '/nav/shallow-routing')
|
||||||
|
|
||||||
|
const counter = await browser
|
||||||
|
.elementByCss('#increase').click()
|
||||||
|
.elementByCss('#increase').click()
|
||||||
|
.elementByCss('#home-link').click()
|
||||||
|
.waitForElementByCss('.nav-home')
|
||||||
|
.back()
|
||||||
|
.waitForElementByCss('.shallow-routing')
|
||||||
|
.elementByCss('#counter').text()
|
||||||
|
expect(counter).toBe('Counter: 2')
|
||||||
|
|
||||||
|
const getInitialPropsRunCount = await browser
|
||||||
|
.elementByCss('#get-initial-props-run-count').text()
|
||||||
|
expect(getInitialPropsRunCount).toBe('getInitialProps run count: 2')
|
||||||
|
|
||||||
|
await browser.close()
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,8 @@ describe('Basic Features', () => {
|
||||||
renderViaHTTP(context.appPort, '/nav/about'),
|
renderViaHTTP(context.appPort, '/nav/about'),
|
||||||
renderViaHTTP(context.appPort, '/nav/querystring'),
|
renderViaHTTP(context.appPort, '/nav/querystring'),
|
||||||
renderViaHTTP(context.appPort, '/nav/self-reload'),
|
renderViaHTTP(context.appPort, '/nav/self-reload'),
|
||||||
renderViaHTTP(context.appPort, '/nav/hash-changes')
|
renderViaHTTP(context.appPort, '/nav/hash-changes'),
|
||||||
|
renderViaHTTP(context.appPort, '/nav/shallow-routing')
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
afterAll(() => stopApp(context.server))
|
afterAll(() => stopApp(context.server))
|
||||||
|
|
Loading…
Reference in a new issue