teamplay 0.1.11 → 0.1.13
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/orm/Doc.js +5 -2
- package/orm/Query.js +8 -3
- package/package.json +8 -8
- package/react/convertToObserver.js +88 -55
- package/react/helpers.js +12 -8
- package/react/trapRender.js +6 -12
- package/react/universal$.js +12 -3
- package/react/universalSub.js +9 -1
- package/react/wrapIntoSuspense.js +27 -41
package/orm/Doc.js
CHANGED
|
@@ -80,7 +80,9 @@ class Doc {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
async unsubscribe () {
|
|
83
|
-
if (!this.subscribed)
|
|
83
|
+
if (!this.subscribed) {
|
|
84
|
+
throw Error('trying to unsubscribe while not subscribed. Doc: ' + [this.collection, this.docId])
|
|
85
|
+
}
|
|
84
86
|
this.subscribed = undefined
|
|
85
87
|
// if we are still handling the subscription, just wait for it to finish and then unsubscribe
|
|
86
88
|
if (this.subscribing) {
|
|
@@ -216,7 +218,8 @@ class DocSubscriptions {
|
|
|
216
218
|
if (!doc) return
|
|
217
219
|
this.subCount.delete(hash)
|
|
218
220
|
this.initialized.delete(hash)
|
|
219
|
-
|
|
221
|
+
// If the document was initialized as part of query and wasn't directly subscribed to, we should not unsubscribe from it.
|
|
222
|
+
if (doc.subscribed) await doc.unsubscribe()
|
|
220
223
|
if (doc.subscribed) return // if we subscribed again while waiting for unsubscribe, we don't delete the doc
|
|
221
224
|
this.docs.delete(hash)
|
|
222
225
|
}
|
package/orm/Query.js
CHANGED
|
@@ -87,7 +87,9 @@ class Query {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
async unsubscribe () {
|
|
90
|
-
if (!this.subscribed)
|
|
90
|
+
if (!this.subscribed) {
|
|
91
|
+
throw Error('trying to unsubscribe while not subscribed. Query: ' + [this.collection, this.params])
|
|
92
|
+
}
|
|
91
93
|
this.subscribed = undefined
|
|
92
94
|
// if we are still handling the subscription, just wait for it to finish and then unsubscribe
|
|
93
95
|
if (this.subscribing) {
|
|
@@ -227,7 +229,7 @@ class QuerySubscriptions {
|
|
|
227
229
|
let count = this.subCount.get(hash) || 0
|
|
228
230
|
count -= 1
|
|
229
231
|
if (count < 0) {
|
|
230
|
-
if (ERROR_ON_EXCESSIVE_UNSUBSCRIBES) throw ERRORS.notSubscribed($query)
|
|
232
|
+
if (ERROR_ON_EXCESSIVE_UNSUBSCRIBES) throw Error(ERRORS.notSubscribed($query))
|
|
231
233
|
return
|
|
232
234
|
}
|
|
233
235
|
if (count > 0) {
|
|
@@ -275,5 +277,8 @@ export function getQuerySignal (segments, params, options) {
|
|
|
275
277
|
}
|
|
276
278
|
|
|
277
279
|
const ERRORS = {
|
|
278
|
-
notSubscribed: $
|
|
280
|
+
notSubscribed: $query => `
|
|
281
|
+
trying to unsubscribe when not subscribed. Query:
|
|
282
|
+
${[$query[SEGMENTS], $query[PARAMS]]}
|
|
283
|
+
`
|
|
279
284
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "teamplay",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"description": "Full-stack signals ORM with multiplayer",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -23,12 +23,12 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@nx-js/observer-util": "^4.1.3",
|
|
26
|
-
"@teamplay/backend": "^0.1.
|
|
27
|
-
"@teamplay/cache": "^0.1.
|
|
28
|
-
"@teamplay/channel": "^0.1.
|
|
29
|
-
"@teamplay/debug": "^0.1.
|
|
30
|
-
"@teamplay/schema": "^0.1.
|
|
31
|
-
"@teamplay/utils": "^0.1.
|
|
26
|
+
"@teamplay/backend": "^0.1.13",
|
|
27
|
+
"@teamplay/cache": "^0.1.13",
|
|
28
|
+
"@teamplay/channel": "^0.1.13",
|
|
29
|
+
"@teamplay/debug": "^0.1.13",
|
|
30
|
+
"@teamplay/schema": "^0.1.13",
|
|
31
|
+
"@teamplay/utils": "^0.1.13",
|
|
32
32
|
"diff-match-patch": "^1.0.5",
|
|
33
33
|
"events": "^3.3.0",
|
|
34
34
|
"json0-ot-diff": "^1.1.2",
|
|
@@ -63,5 +63,5 @@
|
|
|
63
63
|
]
|
|
64
64
|
},
|
|
65
65
|
"license": "MIT",
|
|
66
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "1abacbe2dd704f77dfa20d061edf89490ac65cee"
|
|
67
67
|
}
|
|
@@ -1,93 +1,126 @@
|
|
|
1
1
|
// TODO: rewrite to use useSyncExternalStore like in mobx. This will also help with handling Suspense abandonment better
|
|
2
2
|
// to cleanup the observer() reaction when the component is unmounted or was abandoned and unmounts will never trigger.
|
|
3
3
|
// ref: https://github.com/mobxjs/mobx/blob/94bc4997c14152ff5aefcaac64d982d5c21ba51a/packages/mobx-react-lite/src/useObserver.ts
|
|
4
|
-
import {
|
|
5
|
-
import _throttle from 'lodash/throttle.js'
|
|
4
|
+
import { forwardRef as _forwardRef, useRef, useSyncExternalStore } from 'react'
|
|
6
5
|
import { observe, unobserve } from '@nx-js/observer-util'
|
|
7
|
-
import
|
|
6
|
+
import _throttle from 'lodash/throttle.js'
|
|
7
|
+
import { createCaches, getDummyCache } from '@teamplay/cache'
|
|
8
|
+
import { __increment, __decrement } from '@teamplay/debug'
|
|
9
|
+
import executionContextTracker from './executionContextTracker.js'
|
|
10
|
+
import { pipeComponentMeta, useUnmount, useId } from './helpers.js'
|
|
8
11
|
import trapRender from './trapRender.js'
|
|
9
12
|
|
|
10
13
|
const DEFAULT_THROTTLE_TIMEOUT = 100
|
|
11
14
|
|
|
12
|
-
export default function convertToObserver (BaseComponent,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
export default function convertToObserver (BaseComponent, {
|
|
16
|
+
forwardRef,
|
|
17
|
+
cache: enableCache = true,
|
|
18
|
+
throttle,
|
|
19
|
+
...options
|
|
20
|
+
} = {}) {
|
|
21
|
+
throttle = normalizeThrottle(throttle)
|
|
15
22
|
// MAGIC. This fixes hot-reloading. TODO: figure out WHY it fixes it
|
|
16
|
-
const random = Math.random()
|
|
23
|
+
// const random = Math.random()
|
|
17
24
|
|
|
18
25
|
// memo; we are not intested in deep updates
|
|
19
26
|
// in props; we assume that if deep objects are changed,
|
|
20
27
|
// this is in observables, which would have been tracked anyway
|
|
21
28
|
let Component = (...args) => {
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
29
|
+
const [cache, destroyCache] = useCreateCacheRef(enableCache)
|
|
30
|
+
const componentId = useId()
|
|
31
|
+
|
|
32
|
+
const admRef = useRef()
|
|
33
|
+
if (!admRef.current) {
|
|
34
|
+
const adm = {
|
|
35
|
+
stateVersion: Symbol(), // eslint-disable-line symbol-description
|
|
36
|
+
onStoreChange: undefined,
|
|
37
|
+
subscribe (onStoreChange) {
|
|
38
|
+
adm.onStoreChange = () => {
|
|
39
|
+
adm.stateVersion = Symbol() // eslint-disable-line symbol-description
|
|
40
|
+
onStoreChange()
|
|
41
|
+
}
|
|
42
|
+
return () => {
|
|
43
|
+
adm.onStoreChange = undefined
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
getSnapshot () {
|
|
47
|
+
return adm.stateVersion
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
admRef.current = adm
|
|
51
|
+
}
|
|
52
|
+
const adm = admRef.current
|
|
53
|
+
|
|
54
|
+
useSyncExternalStore(adm.subscribe, adm.getSnapshot, adm.getSnapshot)
|
|
25
55
|
|
|
26
56
|
// wrap the BaseComponent into an observe decorator once.
|
|
27
57
|
// This way it will track any observable changes and will trigger rerender
|
|
28
|
-
const reactionRef =
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
58
|
+
const reactionRef = useRef()
|
|
59
|
+
const destroyRef = useRef()
|
|
60
|
+
if (!reactionRef.current) {
|
|
61
|
+
let update = () => {
|
|
62
|
+
// It's important to block updates caused by rendering itself
|
|
63
|
+
// (when the sync rendering is in progress).
|
|
64
|
+
if (!executionContextTracker.isActive()) adm.onStoreChange?.()
|
|
65
|
+
}
|
|
66
|
+
if (throttle) update = _throttle(update, throttle)
|
|
67
|
+
destroyRef.current = (where) => {
|
|
68
|
+
if (!reactionRef.current) throw Error(`NO REACTION REF - ${where}`)
|
|
69
|
+
unobserve(reactionRef.current)
|
|
70
|
+
reactionRef.current = undefined
|
|
71
|
+
destroyRef.current = undefined
|
|
72
|
+
destroyCache(where)
|
|
37
73
|
}
|
|
38
|
-
const trappedRender = trapRender({
|
|
39
|
-
|
|
74
|
+
const trappedRender = trapRender({
|
|
75
|
+
render: BaseComponent,
|
|
76
|
+
cache,
|
|
77
|
+
destroy: destroyRef.current,
|
|
78
|
+
componentId
|
|
79
|
+
})
|
|
80
|
+
reactionRef.current = observe(trappedRender, {
|
|
40
81
|
scheduler: update,
|
|
41
82
|
lazy: true
|
|
42
83
|
})
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (reactionRef.current !== observedRender) reactionRef.current = observedRender
|
|
84
|
+
}
|
|
46
85
|
|
|
47
86
|
// clean up observer on unmount
|
|
48
87
|
useUnmount(() => {
|
|
49
|
-
|
|
50
|
-
// probably because of throw's of the async hooks.
|
|
51
|
-
// So there probably are memory leaks here. Research this.
|
|
52
|
-
if (observedRender.current) {
|
|
53
|
-
unobserve(observedRender.current)
|
|
54
|
-
observedRender.current = undefined
|
|
55
|
-
}
|
|
88
|
+
destroyRef.current('useUnmount()')
|
|
56
89
|
})
|
|
57
90
|
|
|
58
|
-
return
|
|
91
|
+
return reactionRef.current(...args)
|
|
59
92
|
}
|
|
60
93
|
|
|
61
94
|
if (forwardRef) Component = _forwardRef(Component)
|
|
62
95
|
pipeComponentMeta(BaseComponent, Component)
|
|
63
96
|
|
|
64
|
-
Component
|
|
65
|
-
|
|
66
|
-
return Component
|
|
97
|
+
return { Component, forwardRef, ...options }
|
|
67
98
|
}
|
|
68
99
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
100
|
+
function normalizeThrottle (throttle) {
|
|
101
|
+
if (typeof throttle === 'boolean') {
|
|
102
|
+
if (throttle) return DEFAULT_THROTTLE_TIMEOUT
|
|
103
|
+
else return undefined
|
|
73
104
|
}
|
|
105
|
+
if (typeof throttle === 'number') return throttle
|
|
106
|
+
if (throttle == null) return undefined
|
|
107
|
+
throw Error('observer(): throttle can be either boolean or number (milliseconds)')
|
|
74
108
|
}
|
|
75
109
|
|
|
76
|
-
function
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
} else {
|
|
91
|
-
return () => setTick(Math.random())
|
|
110
|
+
function useCreateCacheRef (enableCache) {
|
|
111
|
+
const cacheRef = useRef()
|
|
112
|
+
const destroyCacheRef = useRef()
|
|
113
|
+
if (!cacheRef.current) {
|
|
114
|
+
__increment('ObserverWrapper.cache')
|
|
115
|
+
const _createCaches = enableCache ? createCaches : getDummyCache
|
|
116
|
+
cacheRef.current = _createCaches(['styles', 'model'])
|
|
117
|
+
destroyCacheRef.current = (where) => {
|
|
118
|
+
if (!cacheRef.current) throw Error(`NO CACHE REF - ${where}`)
|
|
119
|
+
__decrement('ObserverWrapper.cache')
|
|
120
|
+
cacheRef.current.clear()
|
|
121
|
+
cacheRef.current = undefined
|
|
122
|
+
destroyCacheRef.current = undefined
|
|
123
|
+
}
|
|
92
124
|
}
|
|
125
|
+
return [cacheRef.current, destroyCacheRef.current]
|
|
93
126
|
}
|
package/react/helpers.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { CACHE_ACTIVE, getDummyCache } from '@teamplay/cache'
|
|
3
|
-
import useIsomorphicLayoutEffect from '../utils/useIsomorphicLayoutEffect.js'
|
|
1
|
+
import { useContext, createContext, useRef, useEffect } from 'react'
|
|
4
2
|
|
|
5
3
|
export const ComponentMetaContext = createContext({})
|
|
6
4
|
|
|
@@ -24,12 +22,18 @@ export function pipeComponentMeta (SourceComponent, TargetComponent, suffix = ''
|
|
|
24
22
|
return TargetComponent
|
|
25
23
|
}
|
|
26
24
|
|
|
27
|
-
export function
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return cache
|
|
25
|
+
export function useId () {
|
|
26
|
+
const { componentId } = useContext(ComponentMetaContext)
|
|
27
|
+
return componentId
|
|
31
28
|
}
|
|
32
29
|
|
|
33
30
|
export function useUnmount (fn) {
|
|
34
|
-
|
|
31
|
+
const fnRef = useRef()
|
|
32
|
+
if (fnRef.current !== fn) fnRef.current = fn
|
|
33
|
+
useEffect(
|
|
34
|
+
() => () => {
|
|
35
|
+
fnRef.current()
|
|
36
|
+
},
|
|
37
|
+
[]
|
|
38
|
+
)
|
|
35
39
|
}
|
package/react/trapRender.js
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
// trap render function (functional component) to block observer updates and activate cache
|
|
2
2
|
// during synchronous rendering
|
|
3
|
-
import { useId } from 'react'
|
|
4
|
-
import { unobserve } from '@nx-js/observer-util'
|
|
5
3
|
import executionContextTracker from './executionContextTracker.js'
|
|
6
4
|
|
|
7
|
-
export default function trapRender ({ render,
|
|
5
|
+
export default function trapRender ({ render, cache, destroy, componentId }) {
|
|
8
6
|
return (...args) => {
|
|
9
|
-
|
|
10
|
-
executionContextTracker._start(id)
|
|
11
|
-
blockUpdate.value = true
|
|
7
|
+
executionContextTracker._start(componentId)
|
|
12
8
|
cache.activate()
|
|
9
|
+
let destroyed
|
|
13
10
|
try {
|
|
14
11
|
// destroyer.reset() // TODO: this one is for any destructuring logic which might be needed
|
|
15
12
|
// promiseBatcher.reset() // TODO: this is to support useBatch* hooks
|
|
@@ -17,15 +14,12 @@ export default function trapRender ({ render, blockUpdate, cache, reactionRef })
|
|
|
17
14
|
// if (promiseBatcher.isActive()) {
|
|
18
15
|
// throw Error('[react-sharedb] useBatch* hooks were used without a closing useBatch() call.')
|
|
19
16
|
// }
|
|
20
|
-
blockUpdate.value = false // TODO: might want to just put it into finally block
|
|
21
17
|
return res
|
|
22
18
|
} catch (err) {
|
|
23
19
|
// TODO: this might only be needed only if promise is thrown
|
|
24
20
|
// (check if useUnmount in convertToObserver is called if a regular error is thrown)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
reactionRef.current = undefined
|
|
28
|
-
}
|
|
21
|
+
destroy('trapRender.js')
|
|
22
|
+
destroyed = true
|
|
29
23
|
|
|
30
24
|
if (!err.then) throw err
|
|
31
25
|
// If the Promise was thrown, we catch it before Suspense does.
|
|
@@ -38,7 +32,7 @@ export default function trapRender ({ render, blockUpdate, cache, reactionRef })
|
|
|
38
32
|
// throw err.then(destroy)
|
|
39
33
|
throw err
|
|
40
34
|
} finally {
|
|
41
|
-
cache.deactivate()
|
|
35
|
+
if (!destroyed) cache.deactivate()
|
|
42
36
|
executionContextTracker._clear()
|
|
43
37
|
}
|
|
44
38
|
}
|
package/react/universal$.js
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
|
+
import { useRef } from 'react'
|
|
1
2
|
import $ from '../orm/$.js'
|
|
2
3
|
import executionContextTracker from './executionContextTracker.js'
|
|
3
4
|
|
|
4
5
|
// universal versions of $() which work as a plain function or as a react hook
|
|
5
6
|
export default function universal$ ($root, value) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
if (executionContextTracker.isActive()) {
|
|
8
|
+
// within react component
|
|
9
|
+
const id = executionContextTracker.newHookId()
|
|
10
|
+
const $signal = $($root, value, id)
|
|
11
|
+
// save signal into ref to make sure it's not garbage collected while component exists
|
|
12
|
+
const $signalRef = useRef() // eslint-disable-line react-hooks/rules-of-hooks
|
|
13
|
+
if ($signalRef.current !== $signal) $signalRef.current = $signal
|
|
14
|
+
return $signal
|
|
15
|
+
} else {
|
|
16
|
+
return $($root, value)
|
|
17
|
+
}
|
|
9
18
|
}
|
package/react/universalSub.js
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
|
+
import { useRef } from 'react'
|
|
1
2
|
import sub from '../orm/sub.js'
|
|
2
3
|
import executionContextTracker from './executionContextTracker.js'
|
|
3
4
|
|
|
4
5
|
// universal versions of sub() which work as a plain function or as a react hook
|
|
5
6
|
export default function universalSub (...args) {
|
|
6
7
|
const promiseOrSignal = sub(...args)
|
|
7
|
-
if (executionContextTracker.isActive()
|
|
8
|
+
if (executionContextTracker.isActive()) {
|
|
9
|
+
// within react component
|
|
10
|
+
// 1. if it's a promise, throw it so that Suspense can catch it and wait for subscription to finish
|
|
11
|
+
if (promiseOrSignal.then) throw promiseOrSignal
|
|
12
|
+
// 2. if it's a signal, we save it into ref to make sure it's not garbage collected while component exists
|
|
13
|
+
const $signalRef = useRef() // eslint-disable-line react-hooks/rules-of-hooks
|
|
14
|
+
if ($signalRef.current !== promiseOrSignal) $signalRef.current = promiseOrSignal
|
|
15
|
+
}
|
|
8
16
|
return promiseOrSignal
|
|
9
17
|
}
|
|
@@ -1,50 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
) {
|
|
9
|
-
|
|
10
|
-
if (!(suspenseProps && suspenseProps.fallback)) {
|
|
11
|
-
throw Error(
|
|
12
|
-
'[observer()] You must pass at least ' +
|
|
13
|
-
'a fallback parameter to suspenseProps'
|
|
14
|
-
)
|
|
15
|
-
}
|
|
1
|
+
import { forwardRef as _forwardRef, memo, createElement as el, Suspense, useId, useRef } from 'react'
|
|
2
|
+
import { pipeComponentMeta, pipeComponentDisplayName, ComponentMetaContext } from './helpers.js'
|
|
3
|
+
|
|
4
|
+
export default function wrapIntoSuspense ({
|
|
5
|
+
Component,
|
|
6
|
+
forwardRef,
|
|
7
|
+
suspenseProps = DEFAULT_SUSPENSE_PROPS
|
|
8
|
+
} = {}) {
|
|
9
|
+
if (!suspenseProps?.fallback) throw Error(ERRORS.noFallback)
|
|
16
10
|
|
|
17
11
|
let SuspenseWrapper = (props, ref) => {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
// const [componentMeta] = React.useState({
|
|
25
|
-
// componentId: $root.id(),
|
|
26
|
-
// createdAt: Date.now(),
|
|
27
|
-
// cache
|
|
28
|
-
// })
|
|
29
|
-
const componentMeta = useMemo(function () {
|
|
30
|
-
return {
|
|
31
|
-
// componentId: $root.id(), // TODO: implement creating a unique component guid here (if it's needed anymore)
|
|
32
|
-
createdAt: Date.now(),
|
|
33
|
-
cache
|
|
12
|
+
const componentId = useId()
|
|
13
|
+
const componentMetaRef = useRef()
|
|
14
|
+
if (!componentMetaRef.current) {
|
|
15
|
+
componentMetaRef.current = {
|
|
16
|
+
componentId,
|
|
17
|
+
createdAt: Date.now()
|
|
34
18
|
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
useUnmount(() => {
|
|
38
|
-
__decrement('ObserverWrapper.cache')
|
|
39
|
-
cache.clear()
|
|
40
|
-
})
|
|
19
|
+
}
|
|
41
20
|
|
|
42
21
|
if (forwardRef) props = { ...props, ref }
|
|
43
22
|
|
|
44
23
|
return (
|
|
45
|
-
el(ComponentMetaContext.Provider, { value:
|
|
24
|
+
el(ComponentMetaContext.Provider, { value: componentMetaRef.current },
|
|
46
25
|
el(Suspense, suspenseProps,
|
|
47
|
-
el(
|
|
26
|
+
el(Component, props)
|
|
48
27
|
)
|
|
49
28
|
)
|
|
50
29
|
)
|
|
@@ -52,12 +31,19 @@ export default function wrapIntoSuspense (
|
|
|
52
31
|
|
|
53
32
|
// pipe only displayName because forwardRef render function
|
|
54
33
|
// do not support propTypes or defaultProps
|
|
55
|
-
pipeComponentDisplayName(
|
|
34
|
+
pipeComponentDisplayName(Component, SuspenseWrapper, 'StartupjsObserverWrapper')
|
|
56
35
|
|
|
57
36
|
if (forwardRef) SuspenseWrapper = _forwardRef(SuspenseWrapper)
|
|
58
37
|
SuspenseWrapper = memo(SuspenseWrapper)
|
|
59
38
|
|
|
60
|
-
pipeComponentMeta(
|
|
39
|
+
pipeComponentMeta(Component, SuspenseWrapper)
|
|
61
40
|
|
|
62
41
|
return SuspenseWrapper
|
|
63
42
|
}
|
|
43
|
+
|
|
44
|
+
const DEFAULT_SUSPENSE_PROPS = { fallback: el(NullComponent, null, null) }
|
|
45
|
+
function NullComponent () { return null }
|
|
46
|
+
|
|
47
|
+
const ERRORS = {
|
|
48
|
+
noFallback: '[observer()] You must pass at least a fallback parameter to suspenseProps'
|
|
49
|
+
}
|