@reause/rxjs 0.1.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/LICENSE +21 -0
- package/dist/index.d.ts +461 -0
- package/dist/index.iife.js +478 -0
- package/dist/index.iife.min.js +1 -0
- package/dist/index.js +471 -0
- package/package.json +31 -0
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
(function(exports, react, rxjs) {
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
//#region toObserver/index.tsx
|
|
4
|
+
/**
|
|
5
|
+
* Sugar function converting a write sink into an RxJS
|
|
6
|
+
* [Observer](https://rxjs.dev/guide/observer).
|
|
7
|
+
*
|
|
8
|
+
* Map from @vueuse/rxjs `toObserver`
|
|
9
|
+
* (`source/vueuse/packages/rxjs/toObserver/index.ts`). Upstream is
|
|
10
|
+
* `toObserver<T>(value: Ref<T>): NextObserver<T>` — it returns an observer
|
|
11
|
+
* whose only method is `next`, which synchronously writes the emission into
|
|
12
|
+
* `value.value`.
|
|
13
|
+
*
|
|
14
|
+
* Adjustment for React: a `useRef` write never schedules a re-render, so a
|
|
15
|
+
* 1:1 mirror accepting only a Vue-style `Ref` would silently pin consumers to
|
|
16
|
+
* non-rendering state. The reause version therefore accepts either a
|
|
17
|
+
* ref-like object (`{ current }`, written through `.current`) or a setter
|
|
18
|
+
* function (`(value: T) => void`, called directly) — pass a `useState` setter
|
|
19
|
+
* when the UI must update, or a `useRef` when the latest value only needs to
|
|
20
|
+
* be read later. Everything else matches upstream: the returned observer has
|
|
21
|
+
* ONLY `next` (no `error`/`complete`), each emission is written synchronously,
|
|
22
|
+
* and `toObserver` itself has no side effects.
|
|
23
|
+
*
|
|
24
|
+
* The parameter is named `target` (upstream `value`) because it is a write
|
|
25
|
+
* sink, not a value — there is no Vue-style ref object exposed to users.
|
|
26
|
+
*
|
|
27
|
+
* @__NO_SIDE_EFFECTS__
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* const [count, setCount] = useState(0)
|
|
31
|
+
* interval(1000).pipe(take(3)).subscribe(toObserver(setCount)) // re-renders
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* const count = useRef(0)
|
|
35
|
+
* interval(1000).pipe(take(3)).subscribe(toObserver(count)) // no re-render
|
|
36
|
+
* count.current // latest emission
|
|
37
|
+
*
|
|
38
|
+
* @param target - A ref-like `{ current }` object or a setter function.
|
|
39
|
+
* @returns An RxJS `NextObserver<T>` whose `next` writes into `target`.
|
|
40
|
+
*/
|
|
41
|
+
function toObserver(target) {
|
|
42
|
+
if (typeof target === "function") return { next: (val) => {
|
|
43
|
+
target(val);
|
|
44
|
+
} };
|
|
45
|
+
return { next: (val) => {
|
|
46
|
+
target.current = val;
|
|
47
|
+
} };
|
|
48
|
+
}
|
|
49
|
+
//#endregion
|
|
50
|
+
//#region useExtractedObservable/index.tsx
|
|
51
|
+
/**
|
|
52
|
+
* Shared empty dependency array — a stable identity so the default `deps`
|
|
53
|
+
* never re-creates the effect dependency list.
|
|
54
|
+
*/
|
|
55
|
+
const EMPTY_DEPS$1 = [];
|
|
56
|
+
/**
|
|
57
|
+
* Use an RxJS [`Observable`](https://rxjs.dev/guide/observable) as extracted
|
|
58
|
+
* from one or more hooks, and automatically unsubscribe from it when the
|
|
59
|
+
* component is unmounted.
|
|
60
|
+
*
|
|
61
|
+
* Map from @vueuse/rxjs `useExtractedObservable`
|
|
62
|
+
* (`source/vueuse/packages/rxjs/useExtractedObservable/`): whenever the
|
|
63
|
+
* resolved source value changes, the previous subscription is unsubscribed
|
|
64
|
+
* and `extractor` derives a new `Observable` from the new value, whose
|
|
65
|
+
* emissions become the hook's value. Unsubscribing happens both on a source
|
|
66
|
+
* change and on unmount.
|
|
67
|
+
*
|
|
68
|
+
* React adaptation (upstream's Vue reactivity graph is replaced):
|
|
69
|
+
*
|
|
70
|
+
* - `value` is a read-only value source and takes a plain
|
|
71
|
+
* `Value | null | undefined` (upstream: `T | WatchSource<T>`, i.e. a
|
|
72
|
+
* reactive object, an array of sources or a getter). Resolve a React ref or
|
|
73
|
+
* getter at the call site; a list of sources is passed as a plain array and
|
|
74
|
+
* re-extracts when a new array identity arrives. There is no reactive graph:
|
|
75
|
+
* the effect re-runs when the value's identity changes **or** when
|
|
76
|
+
* `options.deps` change (upstream re-runs whenever any tracked source
|
|
77
|
+
* mutates), so a source object mutated **in place** needs a new identity or
|
|
78
|
+
* the mutation inputs listed in `deps`.
|
|
79
|
+
* - the extractor is `(value, onCleanup) => Observable<E>`: upstream also
|
|
80
|
+
* passes Vue's `oldValue` as the second argument, which has no React
|
|
81
|
+
* equivalent (React keeps no previous-value tracking) and is dropped.
|
|
82
|
+
* - upstream returns a `DeepReadonly<ShallowRef<E>>`; the React port returns
|
|
83
|
+
* the value directly (a readonly ref holds no writable value, so the
|
|
84
|
+
* structure is mirrored — §2 return rules). `initialValue` narrows the
|
|
85
|
+
* returned type to `E | I` (`I` being the `initialValue` type and
|
|
86
|
+
* defaulting to `undefined`), the same trick as `useObservable`.
|
|
87
|
+
* - upstream's `watch` options (`immediate` / `deep` / `flush`) are dropped:
|
|
88
|
+
* the effect always extracts on mount (`immediate: true`, upstream's
|
|
89
|
+
* default) and re-extracts on identity / `deps` changes — React has no
|
|
90
|
+
* flush scheduler to configure. Pass `initialValue` to seed the value
|
|
91
|
+
* before the first extraction settles.
|
|
92
|
+
* - `initialValue` is the `useState` initial value, so it only applies to the
|
|
93
|
+
* first render — a later source change keeps the last emitted value
|
|
94
|
+
* (upstream's `obsRef` is never reset either), and a nullish source
|
|
95
|
+
* subscribes to nothing and drops the previous subscription.
|
|
96
|
+
* - `onCleanup` parity: callbacks registered through the `onCleanup` argument
|
|
97
|
+
* are collected per run and invoked before the next subscription is created,
|
|
98
|
+
* before the previous subscription is unsubscribed (upstream's Vue `watch`
|
|
99
|
+
* runs the previous cleanup before the watcher body), and on unmount.
|
|
100
|
+
* - `extractor`, `onError` and `onComplete` are read through latest-value refs,
|
|
101
|
+
* so inline identities never re-subscribe; only the resolved source value
|
|
102
|
+
* and `deps` do.
|
|
103
|
+
* - the option values are handed to the observer directly, so an absent
|
|
104
|
+
* `onError` leaves the error slot `undefined` and RxJS reports the error as
|
|
105
|
+
* unhandled instead of swallowing it (upstream parity).
|
|
106
|
+
* - SSR-safe: nothing touches `window` / `document`, and the subscription only
|
|
107
|
+
* exists inside the mount effect.
|
|
108
|
+
*
|
|
109
|
+
* @see https://vueuse.org/rxjs/useExtractedObservable/
|
|
110
|
+
* @example
|
|
111
|
+
* const [start, setStart] = useState(0)
|
|
112
|
+
* const count = useExtractedObservable(start, start => interval(1000).pipe(
|
|
113
|
+
* startWith(start),
|
|
114
|
+
* scan((total, next) => next + total),
|
|
115
|
+
* ), { initialValue: 0 })
|
|
116
|
+
* // count is 0 until the first emission, then keeps accumulating
|
|
117
|
+
*/
|
|
118
|
+
function useExtractedObservable(value, extractor, options) {
|
|
119
|
+
const { initialValue, onError, onComplete, deps = EMPTY_DEPS$1 } = options !== null && options !== void 0 ? options : {};
|
|
120
|
+
const [state, setState] = (0, react.useState)(initialValue);
|
|
121
|
+
const resolvedValue = value;
|
|
122
|
+
const extractorRef = (0, react.useRef)(extractor);
|
|
123
|
+
extractorRef.current = extractor;
|
|
124
|
+
const onErrorRef = (0, react.useRef)(onError);
|
|
125
|
+
onErrorRef.current = onError;
|
|
126
|
+
const onCompleteRef = (0, react.useRef)(onComplete);
|
|
127
|
+
onCompleteRef.current = onComplete;
|
|
128
|
+
const effectDeps = [resolvedValue, ...deps];
|
|
129
|
+
(0, react.useEffect)(() => {
|
|
130
|
+
if (resolvedValue === null || resolvedValue === void 0) return;
|
|
131
|
+
const cleanups = [];
|
|
132
|
+
let closed = false;
|
|
133
|
+
const onCleanup = (cleanupFn) => {
|
|
134
|
+
cleanups.push(cleanupFn);
|
|
135
|
+
};
|
|
136
|
+
const subscription = extractorRef.current(resolvedValue, onCleanup).subscribe({
|
|
137
|
+
next: (val) => setState(() => val),
|
|
138
|
+
error: onErrorRef.current,
|
|
139
|
+
complete: onCompleteRef.current
|
|
140
|
+
});
|
|
141
|
+
return () => {
|
|
142
|
+
if (closed) return;
|
|
143
|
+
closed = true;
|
|
144
|
+
cleanups.splice(0, cleanups.length).forEach((cleanupFn) => cleanupFn());
|
|
145
|
+
subscription.unsubscribe();
|
|
146
|
+
};
|
|
147
|
+
}, effectDeps);
|
|
148
|
+
return state;
|
|
149
|
+
}
|
|
150
|
+
//#endregion
|
|
151
|
+
//#region useFrom/index.tsx
|
|
152
|
+
/**
|
|
153
|
+
* Observable-like: an object or function exposing a `subscribe` method (an
|
|
154
|
+
* rxjs `Observable`, `Subject`, `BehaviorSubject`, ...).
|
|
155
|
+
*/
|
|
156
|
+
function isObservableLike(value) {
|
|
157
|
+
return value !== null && (typeof value === "object" || typeof value === "function") && typeof value.subscribe === "function";
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Promise-like: an object or function exposing a `then` method.
|
|
161
|
+
*/
|
|
162
|
+
function isPromiseLike(value) {
|
|
163
|
+
return value !== null && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Create an [`Observable`](https://rxjs.dev/guide/observable) from either an
|
|
167
|
+
* rxjs `ObservableInput` (forwarded to RxJS's
|
|
168
|
+
* [`from()`](https://rxjs.dev/api/index/function/from) unchanged) or a plain
|
|
169
|
+
* value that re-emits whenever it changes across renders.
|
|
170
|
+
*
|
|
171
|
+
* Map from @vueuse/rxjs `from`
|
|
172
|
+
* (`source/vueuse/packages/rxjs/from/`): upstream branches on Vue's `isRef`
|
|
173
|
+
* and `watch`es the ref; React has no reactive refs, so the port branches on
|
|
174
|
+
* observable-/promise-likeness and pushes plain values through an internal
|
|
175
|
+
* effect instead.
|
|
176
|
+
*
|
|
177
|
+
* Branch discriminator (runtime):
|
|
178
|
+
* - a value with a `subscribe` function (Observable-like) or a `then` function
|
|
179
|
+
* (Promise-like) is passed straight to rxjs `from(value)` — upstream parity.
|
|
180
|
+
* - any other plain value is wrapped in a `BehaviorSubject` seeded with the
|
|
181
|
+
* current render value: subscribing immediately receives the current value
|
|
182
|
+
* (the mapped `immediate` semantics), and the Observable re-emits whenever
|
|
183
|
+
* the value changes across renders.
|
|
184
|
+
*
|
|
185
|
+
* React divergences:
|
|
186
|
+
* - upstream's `Ref<T>` branch becomes the plain-value re-emit branch. The
|
|
187
|
+
* subject and its `asObservable()` wrapper are held in refs, so the returned
|
|
188
|
+
* Observable keeps a stable identity across renders and downstream
|
|
189
|
+
* subscriptions are not rebuilt by re-renders.
|
|
190
|
+
* - upstream's `WatchOptions` (`immediate` / `deep` / `flush`) is dropped —
|
|
191
|
+
* React has no Vue `watch`. `immediate` is covered by the seeded
|
|
192
|
+
* `BehaviorSubject` (subscribing receives the current value immediately);
|
|
193
|
+
* `deep` / `flush` are not mapped — handle extra control at the call site
|
|
194
|
+
* with rxjs operators or effect dependencies.
|
|
195
|
+
* - the value source is a plain `T` only — never a getter, `State<T>` or
|
|
196
|
+
* `RefOrValue` (AGENTS.md §2).
|
|
197
|
+
* - on unmount the subject is completed: subscriptions stop and no further
|
|
198
|
+
* emissions are delivered. In dev, React StrictMode remounts effects and
|
|
199
|
+
* runs that cleanup, which completes the subject; the mount effect detects
|
|
200
|
+
* the stopped subject and reseeds it, so re-emission survives the simulated
|
|
201
|
+
* unmount/remount cycle.
|
|
202
|
+
*
|
|
203
|
+
* @see https://vueuse.org/rxjs/from/
|
|
204
|
+
* @example
|
|
205
|
+
* const [count, setCount] = useState(0)
|
|
206
|
+
* const count$ = useFrom(count)
|
|
207
|
+
* // count$ emits 0 immediately; setCount(1) re-emits 1
|
|
208
|
+
* @example
|
|
209
|
+
* const values$ = useFrom(of(1, 2)) // ObservableInput: rxjs from() passthrough
|
|
210
|
+
*/
|
|
211
|
+
function useFrom(value) {
|
|
212
|
+
const subjectRef = (0, react.useRef)(null);
|
|
213
|
+
if (subjectRef.current === null) subjectRef.current = new rxjs.BehaviorSubject(value);
|
|
214
|
+
const subject = subjectRef.current;
|
|
215
|
+
const observableRef = (0, react.useRef)(null);
|
|
216
|
+
if (observableRef.current === null) observableRef.current = subject.asObservable();
|
|
217
|
+
const observable = observableRef.current;
|
|
218
|
+
const valueRef = (0, react.useRef)(value);
|
|
219
|
+
valueRef.current = value;
|
|
220
|
+
const prevValueRef = (0, react.useRef)(value);
|
|
221
|
+
(0, react.useEffect)(() => {
|
|
222
|
+
const subject = subjectRef.current;
|
|
223
|
+
if (subject === null) return;
|
|
224
|
+
if (subject.isStopped) {
|
|
225
|
+
const nextSubject = new rxjs.BehaviorSubject(valueRef.current);
|
|
226
|
+
subjectRef.current = nextSubject;
|
|
227
|
+
observableRef.current = nextSubject.asObservable();
|
|
228
|
+
return () => nextSubject.complete();
|
|
229
|
+
}
|
|
230
|
+
return () => subject.complete();
|
|
231
|
+
}, []);
|
|
232
|
+
(0, react.useEffect)(() => {
|
|
233
|
+
const subject = subjectRef.current;
|
|
234
|
+
if (subject === null || subject.isStopped) return;
|
|
235
|
+
const previous = prevValueRef.current;
|
|
236
|
+
prevValueRef.current = value;
|
|
237
|
+
if (!Object.is(previous, value)) subject.next(value);
|
|
238
|
+
}, [value]);
|
|
239
|
+
if (isObservableLike(value) || isPromiseLike(value)) return (0, rxjs.from)(value);
|
|
240
|
+
return observable;
|
|
241
|
+
}
|
|
242
|
+
//#endregion
|
|
243
|
+
//#region useObservable/index.tsx
|
|
244
|
+
/**
|
|
245
|
+
* Use an RxJS [`Observable`](https://rxjs.dev/guide/observable), return a
|
|
246
|
+
* controllable state, and automatically unsubscribe from it when the component
|
|
247
|
+
* is unmounted.
|
|
248
|
+
*
|
|
249
|
+
* Map from @vueuse/rxjs `useObservable`
|
|
250
|
+
* (`source/vueuse/packages/rxjs/useObservable/`): every emission is written
|
|
251
|
+
* into the state and `options.initialValue` is used until the first one
|
|
252
|
+
* arrives. A failing `Observable` is forwarded to `options.onError`; without a
|
|
253
|
+
* handler RxJS reports the error as unhandled instead of swallowing it.
|
|
254
|
+
*
|
|
255
|
+
* React divergences:
|
|
256
|
+
* - upstream returns a `Readonly<Ref<H | I>>`; the React port returns a
|
|
257
|
+
* useState-like `[value, setValue]` writable tuple (hairyf/reause#174), so
|
|
258
|
+
* the state can also be set from React code — a later emission overwrites it
|
|
259
|
+
* again. Setting a new value re-renders.
|
|
260
|
+
* - upstream's `tryOnScopeDispose` becomes the effect cleanup: the subscription
|
|
261
|
+
* is created once when the component mounts and unsubscribed on unmount.
|
|
262
|
+
* - the `observable` argument is read through a latest-value ref and is **not**
|
|
263
|
+
* part of the effect dependencies — a new identity on a later render does not
|
|
264
|
+
* re-subscribe, matching upstream (Vue creates the subscription once during
|
|
265
|
+
* `setup`). `useObservable(interval(1000), { initialValue: 0 })` therefore
|
|
266
|
+
* keeps one live interval across re-renders instead of restarting it on every
|
|
267
|
+
* render.
|
|
268
|
+
* - `initialValue` is the `useState` initial value, so it only applies to the
|
|
269
|
+
* first render; `onError` is read when the subscription is created.
|
|
270
|
+
*
|
|
271
|
+
* @see https://vueuse.org/rxjs/useObservable/
|
|
272
|
+
* @example
|
|
273
|
+
* const [count, setCount] = useObservable(interval(1000), { initialValue: 0 })
|
|
274
|
+
* // count is 0 until the first emission
|
|
275
|
+
*/
|
|
276
|
+
function useObservable(observable, options) {
|
|
277
|
+
const { initialValue, onError } = options !== null && options !== void 0 ? options : {};
|
|
278
|
+
const [value, setValue] = (0, react.useState)(initialValue);
|
|
279
|
+
const observableRef = (0, react.useRef)(observable);
|
|
280
|
+
observableRef.current = observable;
|
|
281
|
+
const onErrorRef = (0, react.useRef)(onError);
|
|
282
|
+
onErrorRef.current = onError;
|
|
283
|
+
(0, react.useEffect)(() => {
|
|
284
|
+
const subscription = observableRef.current.subscribe({
|
|
285
|
+
next: (val) => setValue(val),
|
|
286
|
+
error: onErrorRef.current
|
|
287
|
+
});
|
|
288
|
+
return () => subscription.unsubscribe();
|
|
289
|
+
}, []);
|
|
290
|
+
return [value, setValue];
|
|
291
|
+
}
|
|
292
|
+
//#endregion
|
|
293
|
+
//#region useSubject/index.tsx
|
|
294
|
+
/**
|
|
295
|
+
* Runtime counterpart of the `BehaviorSubject` overload: only a
|
|
296
|
+
* `BehaviorSubject` replays a current value, so only it can seed the state.
|
|
297
|
+
*/
|
|
298
|
+
function isBehaviorSubject(subject) {
|
|
299
|
+
return subject instanceof rxjs.BehaviorSubject;
|
|
300
|
+
}
|
|
301
|
+
function useSubject(subject, options) {
|
|
302
|
+
const { onError } = options !== null && options !== void 0 ? options : {};
|
|
303
|
+
const [value, setValue] = (0, react.useState)(() => isBehaviorSubject(subject) ? subject.value : void 0);
|
|
304
|
+
const valueRef = (0, react.useRef)(value);
|
|
305
|
+
const onErrorRef = (0, react.useRef)(onError);
|
|
306
|
+
onErrorRef.current = onError;
|
|
307
|
+
const subjectRef = (0, react.useRef)(subject);
|
|
308
|
+
const writeValue = (0, react.useCallback)((next) => {
|
|
309
|
+
const nextValue = typeof next === "function" ? next(valueRef.current) : next;
|
|
310
|
+
valueRef.current = nextValue;
|
|
311
|
+
subjectRef.current.next(nextValue);
|
|
312
|
+
}, []);
|
|
313
|
+
(0, react.useEffect)(() => {
|
|
314
|
+
const subscription = subjectRef.current.subscribe({
|
|
315
|
+
next: (val) => {
|
|
316
|
+
valueRef.current = val;
|
|
317
|
+
setValue(val);
|
|
318
|
+
},
|
|
319
|
+
error: onErrorRef.current
|
|
320
|
+
});
|
|
321
|
+
return () => subscription.unsubscribe();
|
|
322
|
+
}, []);
|
|
323
|
+
return [value, writeValue];
|
|
324
|
+
}
|
|
325
|
+
//#endregion
|
|
326
|
+
//#region useSubscription/index.tsx
|
|
327
|
+
/**
|
|
328
|
+
* Use an RxJS [`Subscription`](https://rxjs.dev/guide/subscription) without
|
|
329
|
+
* worrying about unsubscribing from it or creating memory leaks.
|
|
330
|
+
*
|
|
331
|
+
* Map from @vueuse/rxjs `useSubscription`
|
|
332
|
+
* (`source/vueuse/packages/rxjs/useSubscription/`): the subscription is handed
|
|
333
|
+
* to the hook and torn down automatically when the component unmounts, so the
|
|
334
|
+
* call site never needs its own cleanup.
|
|
335
|
+
*
|
|
336
|
+
* React divergences:
|
|
337
|
+
* - upstream's `tryOnScopeDispose(() => subscription.unsubscribe())` becomes
|
|
338
|
+
* the effect cleanup: the subscription lives from mount until unmount. There
|
|
339
|
+
* is no state, so the hook returns nothing (upstream parity).
|
|
340
|
+
* - like `useObservable` (`packages/rxjs/useObservable/index.tsx`), the
|
|
341
|
+
* argument is deliberately **not** an effect dependency — a new `subscription`
|
|
342
|
+
* identity on a later render does not re-subscribe (Vue's
|
|
343
|
+
* `tryOnScopeDispose` also registers exactly once, during `setup`). Create
|
|
344
|
+
* the subscription with `useState`'s lazy initializer, `useRef` or a module
|
|
345
|
+
* scope when the surrounding component re-renders.
|
|
346
|
+
* - SSR-safe: nothing touches `window` / `document`, and the cleanup only runs
|
|
347
|
+
* on real unmount.
|
|
348
|
+
*
|
|
349
|
+
* @see https://vueuse.org/rxjs/useSubscription/
|
|
350
|
+
* @example
|
|
351
|
+
* const [count, setCount] = useState(0)
|
|
352
|
+
* useSubscription(interval(1000).subscribe(() => setCount(c => c + 1)))
|
|
353
|
+
* // unsubscribes when the component unmounts
|
|
354
|
+
*/
|
|
355
|
+
function useSubscription(subscription) {
|
|
356
|
+
(0, react.useEffect)(() => {
|
|
357
|
+
return () => subscription.unsubscribe();
|
|
358
|
+
}, []);
|
|
359
|
+
}
|
|
360
|
+
//#endregion
|
|
361
|
+
//#region useWatchExtractedObservable/index.tsx
|
|
362
|
+
/**
|
|
363
|
+
* Shared empty dependency array — a stable identity so the default `deps`
|
|
364
|
+
* never re-creates the effect dependency list.
|
|
365
|
+
*/
|
|
366
|
+
const EMPTY_DEPS = [];
|
|
367
|
+
/**
|
|
368
|
+
* Watch the values of an RxJS [`Observable`](https://rxjs.dev/guide/observable)
|
|
369
|
+
* extracted from a source value — React port of VueUse's
|
|
370
|
+
* `watchExtractedObservable`.
|
|
371
|
+
*
|
|
372
|
+
* Map from @vueuse/rxjs `watchExtractedObservable`
|
|
373
|
+
* (`source/vueuse/packages/rxjs/watchExtractedObservable/`): whenever the
|
|
374
|
+
* resolved source value changes, the previous subscription is unsubscribed
|
|
375
|
+
* and `extractor` derives a new `Observable`, whose emissions are forwarded
|
|
376
|
+
* to `callback`. Automatically unsubscribes when the source changes and when
|
|
377
|
+
* the component unmounts.
|
|
378
|
+
*
|
|
379
|
+
* React adaptation (upstream's Vue reactivity graph is replaced):
|
|
380
|
+
*
|
|
381
|
+
* - `value` is a read-only value source and takes a plain
|
|
382
|
+
* `Value | null | undefined` (upstream: `T | WatchSource<T>`; resolve a
|
|
383
|
+
* React ref or getter at the call site). There
|
|
384
|
+
* is no reactive graph: the effect re-runs when the value's
|
|
385
|
+
* identity changes **or** when `options.deps` change (upstream re-runs
|
|
386
|
+
* whenever the tracked source mutates). A source object mutated **in place**
|
|
387
|
+
* therefore does not re-trigger — pass a new identity or list the mutation
|
|
388
|
+
* inputs in `deps`. `deps` is the React substitute for Vue's reactive
|
|
389
|
+
* tracking, the same convention as `useAsync`'s `options.deps`
|
|
390
|
+
* (`packages/core/useAsync/index.tsx`).
|
|
391
|
+
* - The extractor is `(value, onCleanup) => Observable<E>`: upstream also
|
|
392
|
+
* passes Vue's `oldValue` as the second argument, which has no React
|
|
393
|
+
* equivalent (React keeps no previous-value tracking) and is dropped.
|
|
394
|
+
* - Upstream returns a `WatchHandle` function; the React hook returns
|
|
395
|
+
* `{ stop }` (§2B object return — no state-like writable pair). `stop` is a
|
|
396
|
+
* stable `useCallback`, idempotent, and — like the `WatchHandle` — permanent:
|
|
397
|
+
* it tears down the current subscription, runs the pending `onCleanup`
|
|
398
|
+
* callbacks, and prevents later `deps` / source changes from subscribing
|
|
399
|
+
* again.
|
|
400
|
+
* - `onCleanup` parity: callbacks registered through the `onCleanup` argument
|
|
401
|
+
* are collected per run and invoked before the next subscription is created
|
|
402
|
+
* (upstream's Vue `watch` runs the previous cleanup before the watcher body
|
|
403
|
+
* unsubscribes) and on unmount / `stop()`. They run before the subscription
|
|
404
|
+
* is unsubscribed, mirroring upstream's ordering.
|
|
405
|
+
* - A `null` / `undefined` resolved value subscribes to nothing and drops any
|
|
406
|
+
* previous subscription (upstream parity).
|
|
407
|
+
* - `extractor`, `callback`, `onError` and `onComplete` are read through
|
|
408
|
+
* latest-value refs, so inline identities never re-subscribe; only the
|
|
409
|
+
* resolved source value and `deps` do.
|
|
410
|
+
*
|
|
411
|
+
* @see https://vueuse.org/watchExtractedObservable/
|
|
412
|
+
* @example
|
|
413
|
+
* const player = useRef<AudioPlayer | null>(null)
|
|
414
|
+
* const [progress, setProgress] = useState(0)
|
|
415
|
+
*
|
|
416
|
+
* useWatchExtractedObservable(player, p => p.progress$, (percentage) => {
|
|
417
|
+
* setProgress(percentage * 100)
|
|
418
|
+
* }, { onError: err => console.error(err) })
|
|
419
|
+
*/
|
|
420
|
+
function useWatchExtractedObservable(value, extractor, callback, options) {
|
|
421
|
+
const { deps = EMPTY_DEPS, onError, onComplete } = options !== null && options !== void 0 ? options : {};
|
|
422
|
+
const resolvedValue = value;
|
|
423
|
+
const extractorRef = (0, react.useRef)(extractor);
|
|
424
|
+
extractorRef.current = extractor;
|
|
425
|
+
const callbackRef = (0, react.useRef)(callback);
|
|
426
|
+
callbackRef.current = callback;
|
|
427
|
+
const onErrorRef = (0, react.useRef)(onError);
|
|
428
|
+
onErrorRef.current = onError;
|
|
429
|
+
const onCompleteRef = (0, react.useRef)(onComplete);
|
|
430
|
+
onCompleteRef.current = onComplete;
|
|
431
|
+
const subscriptionRef = (0, react.useRef)(null);
|
|
432
|
+
const teardownRef = (0, react.useRef)(null);
|
|
433
|
+
const stoppedRef = (0, react.useRef)(false);
|
|
434
|
+
const stop = (0, react.useCallback)(() => {
|
|
435
|
+
var _teardownRef$current;
|
|
436
|
+
stoppedRef.current = true;
|
|
437
|
+
(_teardownRef$current = teardownRef.current) === null || _teardownRef$current === void 0 || _teardownRef$current.call(teardownRef);
|
|
438
|
+
teardownRef.current = null;
|
|
439
|
+
}, []);
|
|
440
|
+
const effectDeps = [resolvedValue, ...deps];
|
|
441
|
+
(0, react.useEffect)(() => {
|
|
442
|
+
if (stoppedRef.current) return;
|
|
443
|
+
if (resolvedValue === null || resolvedValue === void 0) return;
|
|
444
|
+
const cleanups = [];
|
|
445
|
+
let closed = false;
|
|
446
|
+
const onCleanup = (cleanupFn) => {
|
|
447
|
+
cleanups.push(cleanupFn);
|
|
448
|
+
};
|
|
449
|
+
const subscription = extractorRef.current(resolvedValue, onCleanup).subscribe({
|
|
450
|
+
next: (snapshot) => callbackRef.current(snapshot),
|
|
451
|
+
error: onErrorRef.current,
|
|
452
|
+
complete: onCompleteRef.current
|
|
453
|
+
});
|
|
454
|
+
subscriptionRef.current = subscription;
|
|
455
|
+
const teardown = () => {
|
|
456
|
+
if (closed) return;
|
|
457
|
+
closed = true;
|
|
458
|
+
cleanups.splice(0, cleanups.length).forEach((cleanupFn) => cleanupFn());
|
|
459
|
+
subscription.unsubscribe();
|
|
460
|
+
if (subscriptionRef.current === subscription) subscriptionRef.current = null;
|
|
461
|
+
};
|
|
462
|
+
teardownRef.current = teardown;
|
|
463
|
+
return () => {
|
|
464
|
+
teardown();
|
|
465
|
+
if (teardownRef.current === teardown) teardownRef.current = null;
|
|
466
|
+
};
|
|
467
|
+
}, effectDeps);
|
|
468
|
+
return { stop };
|
|
469
|
+
}
|
|
470
|
+
//#endregion
|
|
471
|
+
exports.toObserver = toObserver;
|
|
472
|
+
exports.useExtractedObservable = useExtractedObservable;
|
|
473
|
+
exports.useFrom = useFrom;
|
|
474
|
+
exports.useObservable = useObservable;
|
|
475
|
+
exports.useSubject = useSubject;
|
|
476
|
+
exports.useSubscription = useSubscription;
|
|
477
|
+
exports.useWatchExtractedObservable = useWatchExtractedObservable;
|
|
478
|
+
})(this.reause = this.reause || {}, React, rxjs);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(e,t,n){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function r(e){return typeof e==`function`?{next:t=>{e(t)}}:{next:t=>{e.current=t}}}let i=[];function a(e,n,r){let{initialValue:a,onError:o,onComplete:s,deps:c=i}=r==null?{}:r,[l,u]=(0,t.useState)(a),d=e,f=(0,t.useRef)(n);f.current=n;let p=(0,t.useRef)(o);p.current=o;let m=(0,t.useRef)(s);m.current=s;let h=[d,...c];return(0,t.useEffect)(()=>{if(d==null)return;let e=[],t=!1,n=f.current(d,t=>{e.push(t)}).subscribe({next:e=>u(()=>e),error:p.current,complete:m.current});return()=>{t||(t=!0,e.splice(0,e.length).forEach(e=>e()),n.unsubscribe())}},h),l}function o(e){return e!==null&&(typeof e==`object`||typeof e==`function`)&&typeof e.subscribe==`function`}function s(e){return e!==null&&(typeof e==`object`||typeof e==`function`)&&typeof e.then==`function`}function c(e){let r=(0,t.useRef)(null);r.current===null&&(r.current=new n.BehaviorSubject(e));let i=r.current,a=(0,t.useRef)(null);a.current===null&&(a.current=i.asObservable());let c=a.current,l=(0,t.useRef)(e);l.current=e;let u=(0,t.useRef)(e);return(0,t.useEffect)(()=>{let e=r.current;if(e!==null){if(e.isStopped){let e=new n.BehaviorSubject(l.current);return r.current=e,a.current=e.asObservable(),()=>e.complete()}return()=>e.complete()}},[]),(0,t.useEffect)(()=>{let t=r.current;if(t===null||t.isStopped)return;let n=u.current;u.current=e,Object.is(n,e)||t.next(e)},[e]),o(e)||s(e)?(0,n.from)(e):c}function l(e,n){let{initialValue:r,onError:i}=n==null?{}:n,[a,o]=(0,t.useState)(r),s=(0,t.useRef)(e);s.current=e;let c=(0,t.useRef)(i);return c.current=i,(0,t.useEffect)(()=>{let e=s.current.subscribe({next:e=>o(e),error:c.current});return()=>e.unsubscribe()},[]),[a,o]}function u(e){return e instanceof n.BehaviorSubject}function d(e,n){let{onError:r}=n==null?{}:n,[i,a]=(0,t.useState)(()=>u(e)?e.value:void 0),o=(0,t.useRef)(i),s=(0,t.useRef)(r);s.current=r;let c=(0,t.useRef)(e),l=(0,t.useCallback)(e=>{let t=typeof e==`function`?e(o.current):e;o.current=t,c.current.next(t)},[]);return(0,t.useEffect)(()=>{let e=c.current.subscribe({next:e=>{o.current=e,a(e)},error:s.current});return()=>e.unsubscribe()},[]),[i,l]}function f(e){(0,t.useEffect)(()=>()=>e.unsubscribe(),[])}let p=[];function m(e,n,r,i){let{deps:a=p,onError:o,onComplete:s}=i==null?{}:i,c=e,l=(0,t.useRef)(n);l.current=n;let u=(0,t.useRef)(r);u.current=r;let d=(0,t.useRef)(o);d.current=o;let f=(0,t.useRef)(s);f.current=s;let m=(0,t.useRef)(null),h=(0,t.useRef)(null),g=(0,t.useRef)(!1),_=(0,t.useCallback)(()=>{var e;g.current=!0,(e=h.current)==null||e.call(h),h.current=null},[]),v=[c,...a];return(0,t.useEffect)(()=>{if(g.current||c==null)return;let e=[],t=!1,n=l.current(c,t=>{e.push(t)}).subscribe({next:e=>u.current(e),error:d.current,complete:f.current});m.current=n;let r=()=>{t||(t=!0,e.splice(0,e.length).forEach(e=>e()),n.unsubscribe(),m.current===n&&(m.current=null))};return h.current=r,()=>{r(),h.current===r&&(h.current=null)}},v),{stop:_}}e.toObserver=r,e.useExtractedObservable=a,e.useFrom=c,e.useObservable=l,e.useSubject=d,e.useSubscription=f,e.useWatchExtractedObservable=m})(this.reause=this.reause||{},React,rxjs);
|