-
Notifications
You must be signed in to change notification settings - Fork 3
/
index.js
141 lines (125 loc) · 4.32 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import { useState, useEffect, useMemo } from 'react'
import { prepare } from '@hasura-ws/prepare'
import { buildModel } from '@hasura-ws/model'
export const ERR = Symbol('error')
export const PENDING = Symbol('pending')
export const RELOADING = Symbol('reloading')
const genInputs = variables => (variables ? Object.values(variables) : [])
const assertHookParams = (run, variables, inputs) => {
if (typeof run !== 'function') {
throw Error(`Hooks first arguement must be the prepare query
Hooked query always return all the values, so ensure you didn't pass .all`)
}
if (inputs && !Array.isArray(inputs)) {
throw Error(
'Hooks inputs must be an array like you would give to useEffect',
)
}
if (variables && typeof variables !== 'object') {
throw Error('Hooks variables must be a javascript object')
}
}
const reloadState = (state, setState) => {
if (!state || typeof state !== 'object') return
if (state[PENDING] || state[RELOADING]) return
const copy = Array.isArray(state) ? [...state] : { ...state }
copy[RELOADING] = true
setState(copy)
}
export const useQuery = (run, variables, inputs) => {
assertHookParams(run, variables, inputs)
const [state, setState] = useState({ [PENDING]: true })
useEffect(() => {
reloadState(state, setState)
if (variables === null) return
let abort = false
run(variables).then(
newState => abort || setState(newState),
error => abort || setState({ [ERR]: error }),
)
return () => abort = true
}, inputs || genInputs(variables))
return state
}
export const useMutation = mutate => {
assertHookParams(mutate)
const [pending, setPending] = useState(false)
const run = useMemo(
() => async variables => {
setPending(true)
try {
return await mutate(variables)
} finally {
setPending(false)
}
},
[],
)
return { pending, run }
}
export const useSubscribe = (subscribe, variables, inputs) => {
assertHookParams(subscribe, variables, inputs)
const [state, setState] = useState({ [PENDING]: true })
useEffect(() => {
reloadState(state, setState)
if (variables === null) return
const handle = subscribe(setState, variables)
handle.execution.catch(error => setState({ [ERR]: error }))
return handle.unsubscribe
}, inputs || genInputs(variables))
return state
}
const guessHook = query => {
const match = query.match(/(query|mutation|subscription)/)
switch (match && match[0]) {
case 'mutation':
return useMutation
case 'subscription':
return useSubscribe
default:
return useQuery
}
}
export const prepareWithHooks = (client, query) => {
const prep = prepare(client, query)
const hook = guessHook(query)
const map = exec => (variables, inputs) => hook(exec, variables, inputs)
prep.use = map(prep)
prep.useAll = map(prep.all)
prep.useOne = map(prep.one)
prep.useMap = mapper => map(prep.map(mapper))
return prep
}
export const initPrepareWithHooks = client => query =>
prepareWithHooks(client, query)
const _hasError = data => Boolean(data && data[ERR])
const _getError = data => data && data[ERR]
const _isPending = data => data && data[PENDING]
const _isReloading = data => data && data[RELOADING]
export const hasError = (...data) => data.some(_hasError)
export const getError = (...data) => _getError(data.find(_hasError))
export const isPending = (...data) => data.some(_isPending)
export const isReloading = (...data) => data.some(_isReloading)
export const buildModelWithHooks = prepare => {
const prepModel = buildModel(prepare)
return (...args) => fields => {
const model = prepModel(...args)(fields)
return {
...model,
useRemove: () => useMutation(model.remove),
useUpdate: () => useMutation(model.update),
useAdd: () => useMutation(model.add),
useGet: _ => Array.isArray(_)
? useQuery(model.selectQueryAll, { [model.list]: _ }, _)
: useQuery(model.selectQuery.one, _ ? { [model.key]: _ } : null, [_]),
useSubscribe: _ => Array.isArray(_)
? useSubscribe(model.subscribeQueryAll, { [model.list]: _ }, _)
: useSubscribe(model.subscribeQuery.one, _ ? { [model.key]:_ } : null, [_]),
}
}
}
export const initAll = client => {
const prepare = initPrepareWithHooks(client)
const model = buildModelWithHooks(prepare)
return { client, prepare, model }
}