1
0
Fork 0
mirror of https://github.com/terribleplan/next.js.git synced 2024-01-19 02:48:18 +00:00
next.js/packages/next/client/on-demand-entries-client.js
JJ Kasper af07611a63 Implement websockets based on-demand-entries ping (#4508)
Fixes #4495

Here's my approach for replacing the XHR on-demand-entries pinger #1364 #4495. I'm not sure if this is the way everyone wants to accomplish this since I saw mention of using a separate server and port for the dynamic entries websocket, but thought this would be a fairly clean solution since it doesn't need that.

With this method the only change when using a custom server is you have to listen for the upgrade event and pass it to next.getRequestHandler(). Example: 
```
const server = app.listen(port)
const handleRequest = next.getRequestHandler()

if(dev) {
  server.on('upgrade', handleRequest)
}
```
2018-12-14 12:25:59 +01:00

90 lines
2.2 KiB
JavaScript

/* global location, WebSocket */
import Router from 'next/router'
import fetch from 'unfetch'
const { hostname } = location
const retryTime = 5000
let ws = null
let lastHref = null
export default async ({ assetPrefix }) => {
Router.ready(() => {
Router.events.on('routeChangeComplete', ping)
})
const setup = async (reconnect) => {
if (ws && ws.readyState === ws.OPEN) {
return Promise.resolve()
}
return new Promise(resolve => {
ws = new WebSocket(`ws://${hostname}:${process.env.NEXT_WS_PORT}`)
ws.onopen = () => resolve()
ws.onclose = () => {
setTimeout(async () => {
// check if next restarted and we have to reload to get new port
await fetch(`${assetPrefix}/_next/on-demand-entries-ping`)
.then(res => res.status === 200 && location.reload())
.catch(() => {})
await setup(true)
resolve()
}, retryTime)
}
ws.onmessage = async ({ data }) => {
const payload = JSON.parse(data)
if (payload.invalid && lastHref !== location.href) {
// Payload can be invalid even if the page does not exist.
// So, we need to make sure it exists before reloading.
const pageRes = await fetch(location.href, {
credentials: 'omit'
})
if (pageRes.status === 200) {
location.reload()
} else {
lastHref = location.href
}
}
}
})
}
await setup()
async function ping () {
if (ws.readyState === ws.OPEN) {
ws.send(Router.pathname)
}
}
let pingerTimeout
async function runPinger () {
// Will restart on the visibilitychange API below. For older browsers, this
// will always be true and will always run, but support is fairly prevalent
// at this point.
while (!document.hidden) {
await ping()
await new Promise(resolve => {
pingerTimeout = setTimeout(resolve, 5000)
})
}
}
document.addEventListener(
'visibilitychange',
() => {
if (!document.hidden) {
runPinger()
} else {
clearTimeout(pingerTimeout)
}
},
false
)
setTimeout(() => {
runPinger().catch(err => {
console.error(err)
})
}, 10000)
}