1
0
Fork 0
mirror of https://github.com/terribleplan/next.js.git synced 2024-01-19 02:48:18 +00:00

Add Router method to execute custom logic before popstate events (#3956)

* Add router method to inject code before popstate events

* Default _beforePopState, return true

* Fix link in README

* Re-order `if` statements per feedback
This commit is contained in:
George Pantazis 2018-03-31 07:21:51 -07:00 committed by Tim Neutkens
parent a785f303f4
commit 085b2f806a
3 changed files with 45 additions and 1 deletions

View file

@ -15,8 +15,8 @@ const SingletonRouter = {
// Create public properties and methods of the router in the SingletonRouter
const propertyFields = ['components', 'pathname', 'route', 'query', 'asPath']
const coreMethodFields = ['push', 'replace', 'reload', 'back', 'prefetch']
const routerEvents = ['routeChangeStart', 'beforeHistoryChange', 'routeChangeComplete', 'routeChangeError']
const coreMethodFields = ['push', 'replace', 'reload', 'back', 'prefetch', 'beforePopState']
propertyFields.forEach((field) => {
// Here we need to use Object.defineProperty because, we need to return

View file

@ -40,6 +40,7 @@ export default class Router {
this.subscriptions = new Set()
this.componentLoadCancel = null
this.onPopState = this.onPopState.bind(this)
this._beforePopState = () => true
if (typeof window !== 'undefined') {
// in order for `e.state` to work on the `onpopstate` event
@ -66,6 +67,12 @@ export default class Router {
return
}
// If the downstream application returns falsy, return.
// They will then be responsible for handling the event.
if (!this._beforePopState(e.state)) {
return
}
const { url, as, options } = e.state
this.replace(url, as, options)
}
@ -265,6 +272,10 @@ export default class Router {
this.notify(data)
}
beforePopState (cb) {
this._beforePopState = cb
}
onlyAHashChange (as) {
if (!this.asPath) return false
const [ oldUrlNoHash, oldHash ] = this.asPath.split('#')

View file

@ -458,6 +458,31 @@ export default () =>
</div>
```
#### Intercepting `popstate`
In some cases (for example, if using a [custom router](#custom-server-and-routing)), you may wish
to listen to `popstate` and react before the router acts on it.
For example, you could use this to manipulate the request, or force an SSR refresh.
```jsx
import Router from 'next/router'
Router.beforePopState(({ url, as, options }) => {
// I only want to allow these two routes!
if (as !== "/" || as !== "/other") {
// Have SSR render bad routes as a 404.
window.location.href = as
return false
}
return true
});
```
If you return a falsy value from `beforePopState`, `Router` will not handle `popstate`;
you'll be responsible for handling it, in that case.
See [Disabling File-System Routing](#disabling-file-system-routing).
Above `Router` object comes with the following API:
- `route` - `String` of the current route
@ -466,6 +491,7 @@ Above `Router` object comes with the following API:
- `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
- `beforePopState(cb=function)` - intercept popstate before router processes the event.
The second `as` parameter for `push` and `replace` is an optional _decoration_ of the URL. Useful if you configured custom routes on the server.
@ -753,6 +779,13 @@ module.exports = {
}
```
Note that `useFileSystemPublicRoutes` simply disables filename routes from SSR; client-side routing
may still access those paths. If using this option, you should guard against navigation to routes
you do not want programmatically.
You may also wish to configure the client-side Router to disallow client-side redirects to filename
routes; please refer to [Intercepting `popstate`](#intercepting-popstate).
#### Dynamic assetPrefix
Sometimes we need to set the `assetPrefix` dynamically. This is useful when changing the `assetPrefix` based on incoming requests.