提交 085b2f80 编写于 作者: G George Pantazis 提交者: Tim Neutkens

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
上级 a785f303
......@@ -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
......
......@@ -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('#')
......
......@@ -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.
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册