@based/react 0.7.1 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.ts CHANGED
@@ -1,223 +1,26 @@
1
- import React, {
2
- createContext,
3
- FunctionComponent,
4
- ReactNode,
5
- useContext,
6
- useEffect,
7
- useMemo,
8
- useReducer,
9
- useRef,
10
- useState,
11
- } from 'react'
12
- import based, {
13
- Based,
14
- GenericObject,
1
+ import { useEffect, useMemo, useReducer, useState } from 'react'
2
+
3
+ import {
15
4
  Query,
16
5
  addSubscriber,
17
6
  removeSubscriber,
18
- handleGraphqlVariables,
19
- BasedGraphQL,
20
7
  generateSubscriptionId,
21
8
  BasedOpts,
22
9
  } from '@based/client'
23
10
 
24
- const schemaSubId = generateSubscriptionId({ $subscribe_schema: 'default' })
25
-
26
- const genOptsId = (opts: BasedOpts & { key?: string }): string => {
27
- if (!opts) {
28
- return
29
- }
30
-
31
- if (opts.key) {
32
- return opts.key
33
- }
34
-
35
- return `${opts.env}_${opts.project}_${opts.org}_${opts.cluster || ''}_${
36
- opts.name || ''
37
- }`
38
- }
39
-
40
- export type CreateClient = (
41
- selector: string | (BasedOpts & { key?: string })
42
- ) => Based
43
-
44
- interface BasedContextType {
45
- clients: { [key: string]: Based }
46
- createClient?: CreateClient
47
- removeClient: (
48
- selector: string | (BasedOpts & { key?: string }) | Based
49
- ) => void
50
- }
51
-
52
- export const BasedContext = createContext<BasedContextType>({
53
- clients: {},
54
- // eslint-disable-next-line
55
- removeClient: (...args: any) => {},
56
- })
57
-
58
- const errors = {}
59
- const errorListeners = new Set()
60
- const loadings = new Set()
61
- const loadingListeners = new Set()
62
- const newClientListeners: Set<() => void> = new Set()
63
-
64
- let isLoading = false
65
- let lastError = ''
66
- let errorCnt = 0
67
- let errorKey = errorCnt + lastError
68
-
69
- export const defaultCreateClient: CreateClient = (selector) => {
70
- if (typeof selector === 'object') {
71
- if (
72
- process.env.CLUSTER &&
73
- process.env.CLUSTER.startsWith('local') &&
74
- !selector.cluster
75
- ) {
76
- selector.cluster = process.env.CLUSTER
77
- }
78
- return based(selector)
79
- } else {
80
- // default
81
- console.error('Cannot create client from ' + selector)
82
- }
83
- }
84
-
85
- export const Provider: FunctionComponent<{
86
- client?: Based
87
- clients?: { [key: string]: Based }
88
- children: ReactNode
89
- createClient?: CreateClient
90
- }> = ({ client, children, clients, createClient }) => {
91
- if (!clients && client) {
92
- clients = {
93
- default: client,
94
- }
95
- } else if (clients && client) {
96
- clients.default = client
97
- }
98
-
99
- const ctx = React.createElement(
100
- BasedContext.Provider,
101
- {
102
- value: {
103
- clients,
104
- createClient: createClient || defaultCreateClient,
105
- removeClient: (selector) => {
106
- if (selector instanceof Based) {
107
- for (const cl in clients) {
108
- if (clients[cl] === selector) {
109
- selector = cl
110
- break
111
- }
112
- }
113
- if (typeof selector !== 'string') {
114
- console.error('Cannot find client to remove from ctx', selector)
115
- return
116
- }
117
- } else if (typeof selector !== 'string') {
118
- selector = genOptsId(selector)
119
- }
120
- // @ts-ignore
121
- if (clients[selector]) {
122
- // @ts-ignore
123
- clients[selector].disconnect()
124
- // @ts-ignore
125
- delete clients[selector]
126
- newClientListeners.forEach((fn) => fn())
127
- }
128
- },
129
- },
130
- },
131
- children
132
- )
133
-
134
- return ctx
135
- }
136
-
137
- export const useBasedContext = () => {
138
- return useContext(BasedContext)
139
- }
11
+ import { resultReducer } from './reducer'
140
12
 
141
- function forceUpdate(state: number) {
142
- return state + 1
143
- }
13
+ import { Loading, Data } from './types'
144
14
 
145
- export const useClients = (): Based[] => {
146
- const ctx = useBasedContext()
147
- const [, update] = useReducer(forceUpdate, 0)
15
+ import { genOptsId } from './genOptsId'
148
16
 
149
- useEffect(() => {
150
- let timer
151
- const fn = () => {
152
- timer = setTimeout(update, 0)
153
- }
154
- newClientListeners.add(fn)
155
- return () => {
156
- newClientListeners.delete(fn)
157
- clearTimeout(timer)
158
- }
159
- }, [])
17
+ import { useClient } from './clients'
160
18
 
161
- return Object.values(ctx.clients)
162
- }
19
+ import { updateMeta } from './meta'
163
20
 
164
- export const useClient = (
165
- selector: string | (BasedOpts & { key?: string }) = 'default'
166
- ) => {
167
- const basedCtx = useContext(BasedContext)
168
-
169
- if (typeof selector === 'object') {
170
- if (!(selector.env && selector.project && selector.org)) {
171
- return
172
- }
173
- }
174
-
175
- let key: string
176
-
177
- if (typeof selector === 'string') {
178
- key = selector
179
- } else {
180
- key = selector.key || genOptsId(selector)
181
- }
182
-
183
- let client: Based = basedCtx.clients[key]
184
-
185
- if (!client && basedCtx.createClient) {
186
- client = basedCtx.createClient(selector)
187
-
188
- if (client) {
189
- basedCtx.clients[key] = client
190
- newClientListeners.forEach((fn) => fn())
191
- }
192
- }
193
-
194
- return client
195
- }
196
-
197
- type Data = GenericObject
198
-
199
- type Loading = boolean
200
-
201
- function resultReducer(
202
- state: { data: Data; error?: Error; loading: Loading; checksum: number },
203
- action: { data?: Data; error?: Error; loading?: Loading; checksum?: number }
204
- ) {
205
- if (action.error) {
206
- state.error = action.error
207
- }
208
- if (action.data) {
209
- state.checksum = action.checksum || 0
210
- state.data = action.data
211
- state.loading = false
212
- if (state.error) {
213
- delete state.error
214
- }
215
- }
216
- if (action.loading) {
217
- state.loading = action.loading
218
- }
219
- return { ...state }
220
- }
21
+ export * from './meta'
22
+ export * from './clients'
23
+ export * from './gql'
221
24
 
222
25
  export function useAuth(
223
26
  clientSelector?: string | (BasedOpts & { key?: string })
@@ -379,222 +182,4 @@ export function useData(
379
182
  return result
380
183
  }
381
184
 
382
- export function useLoading() {
383
- const [, setLoading] = useState(isLoading)
384
-
385
- loadingListeners.add(setLoading)
386
-
387
- useEffect(() => {
388
- return () => {
389
- loadingListeners.delete(setLoading)
390
- }
391
- }, [])
392
-
393
- return { loading: isLoading }
394
- }
395
-
396
- export function useError() {
397
- const [, setError] = useState(errorKey)
398
-
399
- errorListeners.add(setError)
400
-
401
- useEffect(() => {
402
- return () => {
403
- errorListeners.delete(setError)
404
- }
405
- }, [])
406
-
407
- return { error: errorCnt ? lastError : null, errors: Object.values(errors) }
408
- }
409
-
410
- function updateMeta(subKey, loading, error) {
411
- if (error) {
412
- lastError = error
413
- if (subKey in errors) {
414
- errors[subKey] = error
415
- } else {
416
- errors[subKey] = error
417
- errorCnt++
418
- }
419
- } else {
420
- if (subKey in errors) {
421
- errorCnt--
422
- delete errors[subKey]
423
- }
424
- }
425
-
426
- const newErrorKey = errorCnt + lastError
427
- if (newErrorKey !== errorKey) {
428
- errorKey = newErrorKey
429
- errorListeners.forEach((fn: Function) => fn(errorKey))
430
- }
431
-
432
- if (loading) {
433
- loadings.add(subKey)
434
- } else {
435
- loadings.delete(subKey)
436
- }
437
-
438
- const newLoading = !!loadings.size
439
- if (newLoading !== isLoading) {
440
- isLoading = newLoading
441
- loadingListeners.forEach((fn: Function) => fn(isLoading))
442
- }
443
- }
444
-
445
- export function useQuery(
446
- query?: string | BasedGraphQL,
447
- variables: Record<string, any> = {},
448
- clientSelector?: string | (BasedOpts & { key?: string })
449
- ): {
450
- data: Data
451
- error?: Error
452
- loading: Loading
453
- } {
454
- const [result, dispatch] = useReducer(resultReducer, {
455
- loading: true,
456
- data: {},
457
- checksum: 0,
458
- })
459
-
460
- const r = useRef({ checksum: 0, schemaChecksum: 0 })
461
-
462
- if (query) {
463
- const selector = clientSelector || 'default'
464
-
465
- const client = useClient(selector)
466
-
467
- const [configState, updateConfigState] = useState(0)
468
-
469
- useEffect(() => {
470
- const [, subscriberId] = addSubscriber(
471
- client.client,
472
- // FIXME
473
- { $subscribe_schema: 'default' },
474
- (d, checksum) => {
475
- console.info('snurp', 'flx', checksum)
476
- if (!client.client.configuration) {
477
- client.client.configuration = { dbs: [], schema: {} }
478
- }
479
- client.client.configuration.schema.default = d
480
- updateConfigState(checksum)
481
- },
482
- (err) => {
483
- if (err) {
484
- console.error(err)
485
- }
486
- },
487
- (err) => {
488
- console.error(err)
489
- },
490
- schemaSubId
491
- )
492
- return () => {
493
- removeSubscriber(client.client, schemaSubId, subscriberId)
494
- }
495
- return () => {}
496
- }, [])
497
-
498
- if (configState) {
499
- let op: BasedGraphQL
500
- if (typeof query === 'string') {
501
- op = client.gql(query)
502
- } else {
503
- op = query
504
- }
505
-
506
- op = handleGraphqlVariables(op, op, variables)
507
-
508
- const fns: { name: string; payload: any }[] = []
509
- const queryObj: any = {}
510
- for (const key in op.ops) {
511
- if (op.ops[key].fnObserve) {
512
- const { name, payload } = op.ops[key].fnObserve
513
- fns.push({ name: <string>name, payload })
514
- continue
515
- }
516
- queryObj[key] = op.ops[key].get
517
- }
518
-
519
- // if (fns?.length) {
520
- // console.log('HELLO OBSERVABLE FUNCTIONS', JSON.stringify(fns, null, 2))
521
-
522
- // // TODO: create observables for these fns and combine with below
523
- // return new Observable(this.client, { $db: op.db, data: queryObj })
524
- // }
525
-
526
- // return new Observable(this.client, { $db: op.db, data: queryObj })
527
- // }
528
-
529
- // op
530
-
531
- const subId = useMemo(() => {
532
- // fn?
533
- return generateSubscriptionId(queryObj)
534
- }, [queryObj])
535
-
536
- const clientKey =
537
- typeof selector === 'string' ? selector : genOptsId(selector)
538
-
539
- if (client) {
540
- const subKey = clientKey + subId
541
-
542
- useEffect(() => {
543
- if (!configState) {
544
- return
545
- }
546
- updateMeta(subKey, true, false)
547
- // dispatch({ error: null, loading: true, data: {} })
548
- const [, subscriberId] = addSubscriber(
549
- client.client,
550
- queryObj,
551
- (d, checksum) => {
552
- // updateMeta(subKey, false, false)
553
-
554
- console.info(r.current.checksum)
555
-
556
- if (r.current.checksum !== checksum) {
557
- r.current.checksum = checksum
558
- dispatch({ data: d, checksum })
559
- }
560
- },
561
- (err) => {
562
- if (err) {
563
- console.error(err)
564
- // updateMeta(subKey, false, err)
565
- dispatch({ error: err, loading: false })
566
- }
567
- },
568
- (err) => {
569
- console.error(err)
570
- // updateMeta(subKey, false, err)
571
- dispatch({ error: err })
572
- },
573
- subId
574
- )
575
- return () => {
576
- // updateMeta(subKey, false, false)
577
- removeSubscriber(client.client, subId, subscriberId)
578
- }
579
- }, [subId, clientKey, configState])
580
- } else {
581
- useEffect(stubFn, [null, null])
582
- }
583
- } else {
584
- useMemo(stubFn, [query])
585
- useEffect(stubFn, [null, null, null])
586
- }
587
- } else {
588
- useClient(clientSelector)
589
- useState(null)
590
- useEffect(stubFn, [null])
591
-
592
- useMemo(stubFn, [query])
593
-
594
- useEffect(stubFn, [null, null, null])
595
- }
596
-
597
- return result
598
- }
599
-
600
185
  function stubFn() {}
package/src/meta.ts ADDED
@@ -0,0 +1,74 @@
1
+ import { useEffect, useState } from 'react'
2
+
3
+ const errors = {}
4
+ const errorListeners = new Set()
5
+ const loadings = new Set()
6
+ const loadingListeners = new Set()
7
+
8
+ let isLoading = false
9
+ let lastError = ''
10
+ let errorCnt = 0
11
+ let errorKey = errorCnt + lastError
12
+
13
+ export function updateMeta(subKey, loading, error) {
14
+ if (error) {
15
+ lastError = error
16
+ if (subKey in errors) {
17
+ errors[subKey] = error
18
+ } else {
19
+ errors[subKey] = error
20
+ errorCnt++
21
+ }
22
+ } else {
23
+ if (subKey in errors) {
24
+ errorCnt--
25
+ delete errors[subKey]
26
+ }
27
+ }
28
+
29
+ const newErrorKey = errorCnt + lastError
30
+ if (newErrorKey !== errorKey) {
31
+ errorKey = newErrorKey
32
+ errorListeners.forEach((fn: Function) => fn(errorKey))
33
+ }
34
+
35
+ if (loading) {
36
+ loadings.add(subKey)
37
+ } else {
38
+ loadings.delete(subKey)
39
+ }
40
+
41
+ const newLoading = !!loadings.size
42
+ if (newLoading !== isLoading) {
43
+ isLoading = newLoading
44
+ loadingListeners.forEach((fn: Function) => fn(isLoading))
45
+ }
46
+ }
47
+
48
+ export function useLoading() {
49
+ const [, setLoading] = useState(isLoading)
50
+
51
+ loadingListeners.add(setLoading)
52
+
53
+ useEffect(() => {
54
+ return () => {
55
+ loadingListeners.delete(setLoading)
56
+ }
57
+ }, [])
58
+
59
+ return { loading: isLoading }
60
+ }
61
+
62
+ export function useError() {
63
+ const [, setError] = useState(errorKey)
64
+
65
+ errorListeners.add(setError)
66
+
67
+ useEffect(() => {
68
+ return () => {
69
+ errorListeners.delete(setError)
70
+ }
71
+ }, [])
72
+
73
+ return { error: errorCnt ? lastError : null, errors: Object.values(errors) }
74
+ }
package/src/reducer.ts ADDED
@@ -0,0 +1,42 @@
1
+ import { Data, Loading } from './types'
2
+
3
+ export function resultReducer(
4
+ state: { data: Data; error?: Error; loading: Loading; checksum: number },
5
+ action: {
6
+ merge?: Data
7
+ data?: Data
8
+ error?: Error
9
+ loading?: Loading
10
+ checksum?: number
11
+ }
12
+ ) {
13
+ if (action.error) {
14
+ state.error = action.error
15
+ }
16
+ if (action.data) {
17
+ state.checksum = action.checksum || 0
18
+ state.data = action.data
19
+ state.loading = false
20
+ if (state.error) {
21
+ delete state.error
22
+ }
23
+ }
24
+
25
+ if (action.merge) {
26
+ state.checksum = action.checksum || 0
27
+ if (!state.data) {
28
+ state.data = action.merge
29
+ } else {
30
+ Object.assign(state.data, action.merge)
31
+ }
32
+ state.loading = false
33
+ if (state.error) {
34
+ delete state.error
35
+ }
36
+ }
37
+
38
+ if (action.loading) {
39
+ state.loading = action.loading
40
+ }
41
+ return { ...state }
42
+ }
package/src/types.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { GenericObject } from '@based/client'
2
+
3
+ export type Data = GenericObject
4
+
5
+ export type Loading = boolean