2019-05-29 13:57:26 +02:00
|
|
|
import { useContext } from 'react'
|
2019-04-02 16:09:34 +02:00
|
|
|
import { DataManagerContext } from 'next-server/dist/lib/data-manager-context'
|
|
|
|
import { RouterContext } from 'next-server/dist/lib/router-context'
|
|
|
|
import fetch from 'unfetch'
|
2019-05-29 13:57:26 +02:00
|
|
|
import { stringify } from 'querystring'
|
2019-04-02 16:09:34 +02:00
|
|
|
|
2019-05-29 13:57:26 +02:00
|
|
|
type Args = string | number | Array<string | number>
|
2019-04-03 20:43:20 +02:00
|
|
|
|
|
|
|
function generateArgsKey(args: Args[]) {
|
|
|
|
return args.reduce((a: string, b: Args): string => {
|
|
|
|
if (Array.isArray(b)) {
|
|
|
|
return a + generateArgsKey(b)
|
|
|
|
}
|
|
|
|
if (typeof b !== 'string' && typeof b !== 'number') {
|
|
|
|
throw new Error('arguments can only be string or number')
|
|
|
|
}
|
|
|
|
return a + b.toString()
|
|
|
|
}, '')
|
|
|
|
}
|
|
|
|
|
2019-05-29 13:57:26 +02:00
|
|
|
export function createHook(
|
|
|
|
fetcher: (...args: Args[]) => Promise<any>,
|
|
|
|
options: { key: string }
|
|
|
|
) {
|
2019-04-02 16:09:34 +02:00
|
|
|
if (!options.key) {
|
|
|
|
throw new Error('key not provided to createHook options.')
|
|
|
|
}
|
2019-05-29 13:57:26 +02:00
|
|
|
return function useData(...args: Array<string | number>) {
|
|
|
|
const router: import('next-server/lib/router/router').default = useContext(
|
|
|
|
RouterContext
|
|
|
|
)
|
|
|
|
const dataManager: import('next-server/lib/data-manager').DataManager = useContext(
|
|
|
|
DataManagerContext
|
|
|
|
)
|
2019-04-06 21:05:55 +02:00
|
|
|
const key = `${options.key}${generateArgsKey(args)}`
|
2019-04-03 20:43:20 +02:00
|
|
|
const existing = dataManager.get(key)
|
2019-04-09 17:05:42 +02:00
|
|
|
|
|
|
|
if (existing) {
|
|
|
|
if (existing.status === 'resolved') {
|
|
|
|
return existing.result
|
|
|
|
}
|
|
|
|
if (existing === 'mismatched-key') {
|
2019-05-29 13:57:26 +02:00
|
|
|
throw new Error(
|
|
|
|
'matching key was missing from returned data. make sure arguments match between the client and server'
|
|
|
|
)
|
2019-04-09 17:05:42 +02:00
|
|
|
}
|
2019-04-02 16:09:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// @ts-ignore webpack optimization
|
|
|
|
if (process.browser) {
|
2019-04-04 21:55:47 +02:00
|
|
|
const res = fetch(router.route + '?' + stringify(router.query), {
|
|
|
|
headers: {
|
|
|
|
accept: 'application/amp.bind+json',
|
|
|
|
},
|
2019-04-02 16:09:34 +02:00
|
|
|
})
|
2019-05-29 13:57:26 +02:00
|
|
|
.then((res: any) => res.json())
|
|
|
|
.then((result: any) => {
|
|
|
|
const hasKey = result.some((pair: [string, any]) => pair[0] === key)
|
|
|
|
if (!hasKey) {
|
|
|
|
result = [[key, 'mismatched-key']]
|
|
|
|
}
|
|
|
|
dataManager.overwrite(result)
|
|
|
|
})
|
2019-04-02 16:09:34 +02:00
|
|
|
throw res
|
|
|
|
} else {
|
2019-05-29 13:57:26 +02:00
|
|
|
const res = fetcher(...args).then(result => {
|
2019-04-03 20:43:20 +02:00
|
|
|
dataManager.set(key, {
|
2019-04-02 16:09:34 +02:00
|
|
|
status: 'resolved',
|
|
|
|
result,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
throw res
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|