diff --git a/lib/router/index.js b/lib/router/index.js index 1df0ef8a..550ef110 100644 --- a/lib/router/index.js +++ b/lib/router/index.js @@ -1,3 +1,4 @@ +/* global window, location */ import _Router from './router' const SingletonRouter = { @@ -75,3 +76,11 @@ export const createRouter = function (...args) { // Export the actual Router class, which is usually used inside the server export const Router = _Router + +export function _notifyBuildIdMismatch (nextRoute) { + if (SingletonRouter.onAppUpdated) { + SingletonRouter.onAppUpdated(nextRoute) + } else { + location.href = nextRoute + } +} diff --git a/lib/router/router.js b/lib/router/router.js index 30f7ab5f..e3141a35 100644 --- a/lib/router/router.js +++ b/lib/router/router.js @@ -6,6 +6,7 @@ import evalScript from '../eval-script' import shallowEquals from '../shallow-equals' import PQueue from '../p-queue' import { loadGetInitialProps, getLocationOrigin } from '../utils' +import { _notifyBuildIdMismatch } from './' // Add "fetch" polyfill for older browsers if (typeof window !== 'undefined') { @@ -75,7 +76,7 @@ export default class Router extends EventEmitter { data, props, error - } = await this.getRouteInfo(route, pathname, query) + } = await this.getRouteInfo(route, pathname, query, as) if (error && error.cancelled) { this.emit('routeChangeError', error, as) @@ -116,7 +117,7 @@ export default class Router extends EventEmitter { data, props, error - } = await this.getRouteInfo(route, pathname, query) + } = await this.getRouteInfo(route, pathname, query, url) if (error && error.cancelled) { this.emit('routeChangeError', error, url) @@ -162,7 +163,7 @@ export default class Router extends EventEmitter { this.emit('routeChangeStart', as) const { data, props, error - } = await this.getRouteInfo(route, pathname, query) + } = await this.getRouteInfo(route, pathname, query, as) if (error && error.cancelled) { this.emit('routeChangeError', error, as) @@ -189,11 +190,16 @@ export default class Router extends EventEmitter { } } - async getRouteInfo (route, pathname, query) { + async getRouteInfo (route, pathname, query, as) { const routeInfo = {} try { - const { Component, err, jsonPageRes } = routeInfo.data = await this.fetchComponent(route) + routeInfo.data = await this.fetchComponent(route, as) + if (!routeInfo.data) { + return null + } + + const { Component, err, jsonPageRes } = routeInfo.data const ctx = { err, pathname, query, jsonPageRes } routeInfo.props = await this.getInitialProps(Component, ctx) } catch (err) { @@ -229,7 +235,7 @@ export default class Router extends EventEmitter { return this.prefetchQueue.add(() => this.fetchRoute(route)) } - async fetchComponent (route) { + async fetchComponent (route, as) { let data = this.components[route] if (data) return data @@ -240,6 +246,15 @@ export default class Router extends EventEmitter { const jsonPageRes = await this.fetchRoute(route) const jsonData = await jsonPageRes.json() + + if (jsonData.buildIdMismatch) { + _notifyBuildIdMismatch(as) + + const error = Error('Abort due to BUILD_ID mismatch') + error.cancelled = true + throw error + } + const newData = { ...loadComponent(jsonData), jsonPageRes diff --git a/readme.md b/readme.md index f6604d22..4b6e27c1 100644 --- a/readme.md +++ b/readme.md @@ -310,6 +310,7 @@ Here's a list of supported events: - `routeChangeStart(url)` - Fires when a route starts to change - `routeChangeComplete(url)` - Fires when a route changed completely - `routeChangeError(err, url)` - Fires when there's an error when changing routes +- `appUpdated(nextRoute)` - Fires when switching pages and there's a new version of the app > Here `url` is the URL shown in the browser. If you call `Router.push(url, as)` (or similar), then the value of `url` will be `as`. @@ -337,6 +338,17 @@ Router.onRouteChangeError = (err, url) => { } ``` +If you change a route while in between a new deployment, we can't navigate the app via client side. We need to do a full browser navigation. We do it automatically for you. + +But you can customize that via `Route.onAppUpdated` event like this: + +```js +Router.onAppUpdated = (nextUrl) => { + // persist the local state + location.href = nextUrl +} +``` + ### Prefetching Pages