rsnext/packages/next/lib/data.ts

76 lines
2.2 KiB
TypeScript
Raw Normal View History

import { useContext } from 'react'
import { DataManagerContext } from '../next-server/lib/data-manager-context'
import { RouterContext } from '../next-server/lib/router-context'
2019-04-02 16:09:34 +02:00
import fetch from 'unfetch'
import { stringify } from 'querystring'
2019-04-02 16:09:34 +02:00
type Args = string | number | Array<string | number>
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()
}, '')
}
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.')
}
return function useData(...args: Array<string | number>) {
const router: import('../next-server/lib/router/router').NextRouter = 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)}`
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') {
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 (typeof window !== 'undefined') {
const res = fetch(router.route + '?' + stringify(router.query), {
headers: {
accept: 'application/amp.bind+json',
},
2019-04-02 16:09:34 +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 {
const res = fetcher(...args).then(result => {
dataManager.set(key, {
2019-04-02 16:09:34 +02:00
status: 'resolved',
result,
})
})
throw res
}
}
}