react-rx 3.0.0-0 → 3.0.0-1
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/dist/cjs/__tests__/strictmode.test.js +22 -62
- package/dist/cjs/__tests__/useObservable.test.js +32 -33
- package/dist/cjs/index.d.ts +2 -7
- package/dist/cjs/index.js +16 -26
- package/dist/cjs/useObservable.d.ts +0 -3
- package/dist/cjs/useObservable.js +58 -20
- package/dist/cjs/{useObservableCallback.js → useObservableEvent.js} +1 -2
- package/dist/es2015/__tests__/strictmode.test.js +6 -42
- package/dist/es2015/__tests__/useObservable.test.js +2 -3
- package/dist/es2015/index.d.ts +2 -7
- package/dist/es2015/index.js +2 -9
- package/dist/es2015/useObservable.d.ts +0 -3
- package/dist/es2015/useObservable.js +58 -19
- package/dist/es2015/{useObservableCallback.js → useObservableEvent.js} +1 -2
- package/dist/esm/__tests__/strictmode.test.js +8 -48
- package/dist/esm/__tests__/useObservable.test.js +2 -3
- package/dist/esm/index.d.ts +2 -7
- package/dist/esm/index.js +2 -9
- package/dist/esm/useObservable.d.ts +0 -3
- package/dist/esm/useObservable.js +58 -19
- package/dist/esm/{useObservableCallback.js → useObservableEvent.js} +1 -2
- package/package.json +35 -28
- package/src/__tests__/strictmode.test.ts +7 -52
- package/src/__tests__/useObservable.test.tsx +3 -3
- package/src/index.ts +2 -15
- package/src/useObservable.ts +75 -33
- package/src/{useObservableCallback.ts → useObservableEvent.ts} +1 -2
- package/dist/cjs/WithObservable.d.ts +0 -12
- package/dist/cjs/WithObservable.js +0 -16
- package/dist/cjs/__tests__/useAsObservable.test.d.ts +0 -1
- package/dist/cjs/__tests__/useAsObservable.test.js +0 -78
- package/dist/cjs/displayName.d.ts +0 -1
- package/dist/cjs/displayName.js +0 -14
- package/dist/cjs/reactiveComponent.d.ts +0 -8
- package/dist/cjs/reactiveComponent.js +0 -35
- package/dist/cjs/useAsObservable.d.ts +0 -10
- package/dist/cjs/useAsObservable.js +0 -47
- package/dist/cjs/useWithObservable.d.ts +0 -9
- package/dist/cjs/useWithObservable.js +0 -11
- package/dist/cjs/utils.d.ts +0 -7
- package/dist/cjs/utils.js +0 -38
- package/dist/es2015/WithObservable.d.ts +0 -12
- package/dist/es2015/WithObservable.js +0 -9
- package/dist/es2015/__tests__/useAsObservable.test.d.ts +0 -1
- package/dist/es2015/__tests__/useAsObservable.test.js +0 -73
- package/dist/es2015/displayName.d.ts +0 -1
- package/dist/es2015/displayName.js +0 -8
- package/dist/es2015/reactiveComponent.d.ts +0 -8
- package/dist/es2015/reactiveComponent.js +0 -30
- package/dist/es2015/useAsObservable.d.ts +0 -10
- package/dist/es2015/useAsObservable.js +0 -43
- package/dist/es2015/useWithObservable.d.ts +0 -9
- package/dist/es2015/useWithObservable.js +0 -7
- package/dist/es2015/utils.d.ts +0 -7
- package/dist/es2015/utils.js +0 -31
- package/dist/esm/WithObservable.d.ts +0 -12
- package/dist/esm/WithObservable.js +0 -13
- package/dist/esm/__tests__/useAsObservable.test.d.ts +0 -1
- package/dist/esm/__tests__/useAsObservable.test.js +0 -73
- package/dist/esm/displayName.d.ts +0 -1
- package/dist/esm/displayName.js +0 -10
- package/dist/esm/reactiveComponent.d.ts +0 -8
- package/dist/esm/reactiveComponent.js +0 -30
- package/dist/esm/useAsObservable.d.ts +0 -10
- package/dist/esm/useAsObservable.js +0 -43
- package/dist/esm/useWithObservable.d.ts +0 -9
- package/dist/esm/useWithObservable.js +0 -7
- package/dist/esm/utils.d.ts +0 -7
- package/dist/esm/utils.js +0 -31
- package/src/WithObservable.tsx +0 -29
- package/src/__tests__/useAsObservable.test.tsx +0 -95
- package/src/displayName.ts +0 -11
- package/src/reactiveComponent.ts +0 -63
- package/src/useAsObservable.ts +0 -66
- package/src/useWithObservable.ts +0 -24
- package/src/utils.ts +0 -48
- /package/dist/cjs/{useObservableCallback.d.ts → useObservableEvent.d.ts} +0 -0
- /package/dist/es2015/{useObservableCallback.d.ts → useObservableEvent.d.ts} +0 -0
- /package/dist/esm/{useObservableCallback.d.ts → useObservableEvent.d.ts} +0 -0
|
@@ -1,26 +1,65 @@
|
|
|
1
|
-
import { useEffect, useRef,
|
|
2
|
-
import { tap } from 'rxjs/operators';
|
|
1
|
+
import { useEffect, useMemo, useRef, useSyncExternalStore } from 'react';
|
|
2
|
+
import { shareReplay, tap } from 'rxjs/operators';
|
|
3
3
|
function getValue(value) {
|
|
4
4
|
return typeof value === 'function' ? value() : value;
|
|
5
5
|
}
|
|
6
|
+
const cache = new WeakMap();
|
|
7
|
+
function getOrCreateStore(inputObservable, initialValue) {
|
|
8
|
+
if (!cache.has(inputObservable)) {
|
|
9
|
+
const entry = { currentValue: initialValue };
|
|
10
|
+
entry.observable = inputObservable.pipe(shareReplay({ refCount: true, bufferSize: 1 }), tap(value => (entry.currentValue = value)));
|
|
11
|
+
// Eagerly subscribe to sync set `entry.currentValue` to what the observable returns
|
|
12
|
+
entry.subscription = entry.observable.subscribe();
|
|
13
|
+
cache.set(inputObservable, entry);
|
|
14
|
+
}
|
|
15
|
+
return cache.get(inputObservable);
|
|
16
|
+
}
|
|
6
17
|
export function useObservable(observable, initialValue) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Store the initialValue in a ref, as we don't want a changed `initialValue` to trigger a re-subscription.
|
|
20
|
+
* But we also don't want the initialValue to be stale if the observable changes.
|
|
21
|
+
*/
|
|
22
|
+
const initialValueRef = useRef(getValue(initialValue));
|
|
23
|
+
/**
|
|
24
|
+
* Ensures that the initialValue is always up-to-date in case the observable changes.
|
|
25
|
+
*/
|
|
10
26
|
useEffect(() => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
27
|
+
initialValueRef.current = getValue(initialValue);
|
|
28
|
+
}, [initialValue]);
|
|
29
|
+
const [getSnapshot, subscribe] = useMemo(() => {
|
|
30
|
+
const store = getOrCreateStore(observable, initialValueRef.current);
|
|
31
|
+
if (store.subscription.closed) {
|
|
32
|
+
store.subscription = store.observable.subscribe();
|
|
33
|
+
}
|
|
34
|
+
return [
|
|
35
|
+
function getSnapshot() {
|
|
36
|
+
// @TODO: perf opt opportunity: we could do `store.subscription.unsubscribe()` here to clear up some memory, as this subscription is only needed to provide a sync initialValue.
|
|
37
|
+
return store.currentValue;
|
|
38
|
+
},
|
|
39
|
+
function subscribe(callback) {
|
|
40
|
+
// @TODO: perf opt opportunity: we could do `store.subscription.unsubscribe()` here as we only need 1 subscription active to keep the observer alive
|
|
41
|
+
const sub = store.observable.subscribe(callback);
|
|
42
|
+
return () => {
|
|
43
|
+
sub.unsubscribe();
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
];
|
|
47
|
+
}, [observable]);
|
|
48
|
+
const shouldRestoreSubscriptionRef = useRef(false);
|
|
19
49
|
useEffect(() => {
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
50
|
+
const store = getOrCreateStore(observable, initialValueRef.current);
|
|
51
|
+
if (shouldRestoreSubscriptionRef.current) {
|
|
52
|
+
if (store.subscription.closed) {
|
|
53
|
+
store.subscription = store.observable.subscribe();
|
|
54
|
+
}
|
|
55
|
+
shouldRestoreSubscriptionRef.current = false;
|
|
56
|
+
}
|
|
57
|
+
return () => {
|
|
58
|
+
// React StrictMode will call effects as `setup + teardown + setup` thus we can't trust this callback as "react is about to unmount"
|
|
59
|
+
// Tracking this ref lets us set the subscription back up on the next `setup` call if needed, and if it really did unmounted then all is well
|
|
60
|
+
shouldRestoreSubscriptionRef.current = !store.subscription.closed;
|
|
61
|
+
store.subscription.unsubscribe();
|
|
62
|
+
};
|
|
63
|
+
}, [observable]);
|
|
64
|
+
return useSyncExternalStore(subscribe, getSnapshot);
|
|
26
65
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef } from 'react';
|
|
2
1
|
import { observableCallback } from 'observable-callback';
|
|
2
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
3
3
|
const EMPTY_DEPS = [];
|
|
4
4
|
export function useObservableCallback(fn, dependencies = EMPTY_DEPS) {
|
|
5
5
|
const callbackRef = useRef();
|
|
@@ -7,7 +7,6 @@ export function useObservableCallback(fn, dependencies = EMPTY_DEPS) {
|
|
|
7
7
|
callbackRef.current = observableCallback();
|
|
8
8
|
}
|
|
9
9
|
const [calls$, call] = callbackRef.current;
|
|
10
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
11
10
|
const callback = useCallback(fn, dependencies);
|
|
12
11
|
useEffect(() => {
|
|
13
12
|
const subscription = calls$.pipe(callback).subscribe();
|
|
@@ -13,7 +13,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
13
13
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
14
14
|
function step(op) {
|
|
15
15
|
if (f) throw new TypeError("Generator is already executing.");
|
|
16
|
-
while (
|
|
16
|
+
while (_) try {
|
|
17
17
|
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
18
18
|
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
19
19
|
switch (op[0]) {
|
|
@@ -34,24 +34,22 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
34
34
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
35
35
|
}
|
|
36
36
|
};
|
|
37
|
-
import { BehaviorSubject, Observable } from 'rxjs';
|
|
38
|
-
import { useMemoObservable, useObservable } from '../useObservable';
|
|
39
|
-
import { createElement, Fragment, StrictMode, useEffect } from 'react';
|
|
40
37
|
import { act, render } from '@testing-library/react';
|
|
41
|
-
import {
|
|
42
|
-
import {
|
|
38
|
+
import { createElement, Fragment, StrictMode, useEffect } from 'react';
|
|
39
|
+
import { BehaviorSubject, Observable } from 'rxjs';
|
|
40
|
+
import { useObservable } from '../useObservable';
|
|
43
41
|
var wait = function (ms) { return new Promise(function (resolve) { return setTimeout(resolve, ms); }); };
|
|
44
42
|
// NOTE: Jest runs NODE_ENV=test by default, which enables development flags for React
|
|
45
43
|
test('Strict mode should trigger double mount effects and re-renders', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
46
44
|
function ObservableComponent() {
|
|
47
|
-
var observedValue = useObservable(observable, 0);
|
|
48
45
|
useEffect(function () {
|
|
49
46
|
mountCount++;
|
|
50
47
|
}, []);
|
|
48
|
+
var observedValue = useObservable(observable);
|
|
51
49
|
returnedValues.push(observedValue);
|
|
52
50
|
return createElement(Fragment, null, observedValue);
|
|
53
51
|
}
|
|
54
|
-
var subject, observable, returnedValues, mountCount
|
|
52
|
+
var subject, observable, returnedValues, mountCount;
|
|
55
53
|
return __generator(this, function (_a) {
|
|
56
54
|
switch (_a.label) {
|
|
57
55
|
case 0:
|
|
@@ -59,8 +57,8 @@ test('Strict mode should trigger double mount effects and re-renders', function
|
|
|
59
57
|
observable = subject.asObservable();
|
|
60
58
|
returnedValues = [];
|
|
61
59
|
mountCount = 0;
|
|
62
|
-
|
|
63
|
-
expect(mountCount).
|
|
60
|
+
render(createElement(StrictMode, null, createElement(ObservableComponent)));
|
|
61
|
+
expect(mountCount).toEqual(2);
|
|
64
62
|
expect(returnedValues).toEqual([0, 0]);
|
|
65
63
|
return [4 /*yield*/, wait(10)];
|
|
66
64
|
case 1:
|
|
@@ -94,41 +92,3 @@ test('Strict mode should unsubscribe the source observable on unmount', function
|
|
|
94
92
|
rerender(createElement(StrictMode, null, createElement('div')));
|
|
95
93
|
expect(unsubscribed).toEqual([0, 1]);
|
|
96
94
|
});
|
|
97
|
-
test('useMemoObservable should unsubscribe the source observable on unmount', function () {
|
|
98
|
-
var subscribed = [];
|
|
99
|
-
var unsubscribed = [];
|
|
100
|
-
var nextId = 0;
|
|
101
|
-
function ObservableComponent() {
|
|
102
|
-
useMemoObservable(function () {
|
|
103
|
-
return new Observable(function () {
|
|
104
|
-
var id = nextId++;
|
|
105
|
-
subscribed.push(id);
|
|
106
|
-
return function () {
|
|
107
|
-
unsubscribed.push(id);
|
|
108
|
-
};
|
|
109
|
-
});
|
|
110
|
-
}, []);
|
|
111
|
-
return createElement(Fragment, null);
|
|
112
|
-
}
|
|
113
|
-
var rerender = render(createElement(StrictMode, null, createElement(ObservableComponent))).rerender;
|
|
114
|
-
expect(subscribed).toEqual([0, 1]);
|
|
115
|
-
rerender(createElement(StrictMode, null, createElement('div')));
|
|
116
|
-
expect(unsubscribed).toEqual([0, 1]);
|
|
117
|
-
});
|
|
118
|
-
test('useAsObservable should work in strict mode', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
119
|
-
function ObservableComponent(props) {
|
|
120
|
-
var count$ = useAsObservable(props.count);
|
|
121
|
-
var count = useObservable(count$, props.count);
|
|
122
|
-
returnedValues.push(count);
|
|
123
|
-
return createElement(Fragment, null, 'ok');
|
|
124
|
-
}
|
|
125
|
-
var returnedValues, rerender;
|
|
126
|
-
return __generator(this, function (_a) {
|
|
127
|
-
returnedValues = [];
|
|
128
|
-
rerender = render(createElement(StrictMode, null, createElement(ObservableComponent, { count: 0 }))).rerender;
|
|
129
|
-
expect(returnedValues).toEqual([0, 0]);
|
|
130
|
-
rerender(createElement(StrictMode, null, createElement(ObservableComponent, { count: 1 })));
|
|
131
|
-
expect(returnedValues).toEqual([0, 0, 0, 0, 1, 1]);
|
|
132
|
-
return [2 /*return*/];
|
|
133
|
-
});
|
|
134
|
-
}); });
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { act, render, renderHook } from '@testing-library/react';
|
|
2
|
-
import {
|
|
2
|
+
import { createElement, Fragment } from 'react';
|
|
3
3
|
import { asyncScheduler, Observable, of, scheduled, Subject, timer } from 'rxjs';
|
|
4
4
|
import { mapTo } from 'rxjs/operators';
|
|
5
|
-
import {
|
|
6
|
-
import { test, expect } from 'vitest';
|
|
5
|
+
import { useObservable } from '../useObservable';
|
|
7
6
|
test('should subscribe immediately on component mount and unsubscribe on component unmount', function () {
|
|
8
7
|
var subscribed = false;
|
|
9
8
|
var observable = new Observable(function () {
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,7 +1,2 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export { observeState as state, observeCallback as handler, observeContext as context, observeElement as elementRef, } from './utils';
|
|
4
|
-
export { observableCallback } from 'observable-callback';
|
|
5
|
-
export { useObservable, useMemoObservable } from './useObservable';
|
|
6
|
-
export { useAsObservable } from './useAsObservable';
|
|
7
|
-
export { useObservableCallback } from './useObservableCallback';
|
|
1
|
+
export * from './useObservable';
|
|
2
|
+
export * from './useObservableEvent';
|
package/dist/esm/index.js
CHANGED
|
@@ -1,9 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
export { observeState, observeCallback, observeContext, observeElement } from './utils';
|
|
4
|
-
export { observeState as state, observeCallback as handler, observeContext as context, observeElement as elementRef, } from './utils';
|
|
5
|
-
export { observableCallback } from 'observable-callback';
|
|
6
|
-
// hooks
|
|
7
|
-
export { useObservable, useMemoObservable } from './useObservable';
|
|
8
|
-
export { useAsObservable } from './useAsObservable';
|
|
9
|
-
export { useObservableCallback } from './useObservableCallback';
|
|
1
|
+
export * from './useObservable';
|
|
2
|
+
export * from './useObservableEvent';
|
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
import { Observable } from 'rxjs';
|
|
2
|
-
import { DependencyList } from 'react';
|
|
3
2
|
export declare function useObservable<T>(observable: Observable<T>): T | undefined;
|
|
4
3
|
export declare function useObservable<T>(observable: Observable<T>, initialValue: T): T;
|
|
5
4
|
export declare function useObservable<T>(observable: Observable<T>, initialValue: () => T): T;
|
|
6
|
-
export declare function useMemoObservable<T>(observableOrFactory: Observable<T> | (() => Observable<T>), deps: DependencyList): T | undefined;
|
|
7
|
-
export declare function useMemoObservable<T>(observableOrFactory: Observable<T> | (() => Observable<T>), deps: DependencyList, initialValue: T | (() => T)): T;
|
|
@@ -1,26 +1,65 @@
|
|
|
1
|
-
import { useEffect, useRef,
|
|
2
|
-
import { tap } from 'rxjs/operators';
|
|
1
|
+
import { useEffect, useMemo, useRef, useSyncExternalStore } from 'react';
|
|
2
|
+
import { shareReplay, tap } from 'rxjs/operators';
|
|
3
3
|
function getValue(value) {
|
|
4
4
|
return typeof value === 'function' ? value() : value;
|
|
5
5
|
}
|
|
6
|
+
var cache = new WeakMap();
|
|
7
|
+
function getOrCreateStore(inputObservable, initialValue) {
|
|
8
|
+
if (!cache.has(inputObservable)) {
|
|
9
|
+
var entry_1 = { currentValue: initialValue };
|
|
10
|
+
entry_1.observable = inputObservable.pipe(shareReplay({ refCount: true, bufferSize: 1 }), tap(function (value) { return (entry_1.currentValue = value); }));
|
|
11
|
+
// Eagerly subscribe to sync set `entry.currentValue` to what the observable returns
|
|
12
|
+
entry_1.subscription = entry_1.observable.subscribe();
|
|
13
|
+
cache.set(inputObservable, entry_1);
|
|
14
|
+
}
|
|
15
|
+
return cache.get(inputObservable);
|
|
16
|
+
}
|
|
6
17
|
export function useObservable(observable, initialValue) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Store the initialValue in a ref, as we don't want a changed `initialValue` to trigger a re-subscription.
|
|
20
|
+
* But we also don't want the initialValue to be stale if the observable changes.
|
|
21
|
+
*/
|
|
22
|
+
var initialValueRef = useRef(getValue(initialValue));
|
|
23
|
+
/**
|
|
24
|
+
* Ensures that the initialValue is always up-to-date in case the observable changes.
|
|
25
|
+
*/
|
|
10
26
|
useEffect(function () {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
27
|
+
initialValueRef.current = getValue(initialValue);
|
|
28
|
+
}, [initialValue]);
|
|
29
|
+
var _a = useMemo(function () {
|
|
30
|
+
var store = getOrCreateStore(observable, initialValueRef.current);
|
|
31
|
+
if (store.subscription.closed) {
|
|
32
|
+
store.subscription = store.observable.subscribe();
|
|
33
|
+
}
|
|
34
|
+
return [
|
|
35
|
+
function getSnapshot() {
|
|
36
|
+
// @TODO: perf opt opportunity: we could do `store.subscription.unsubscribe()` here to clear up some memory, as this subscription is only needed to provide a sync initialValue.
|
|
37
|
+
return store.currentValue;
|
|
38
|
+
},
|
|
39
|
+
function subscribe(callback) {
|
|
40
|
+
// @TODO: perf opt opportunity: we could do `store.subscription.unsubscribe()` here as we only need 1 subscription active to keep the observer alive
|
|
41
|
+
var sub = store.observable.subscribe(callback);
|
|
42
|
+
return function () {
|
|
43
|
+
sub.unsubscribe();
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
];
|
|
47
|
+
}, [observable]), getSnapshot = _a[0], subscribe = _a[1];
|
|
48
|
+
var shouldRestoreSubscriptionRef = useRef(false);
|
|
19
49
|
useEffect(function () {
|
|
20
|
-
var
|
|
21
|
-
|
|
22
|
-
.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
50
|
+
var store = getOrCreateStore(observable, initialValueRef.current);
|
|
51
|
+
if (shouldRestoreSubscriptionRef.current) {
|
|
52
|
+
if (store.subscription.closed) {
|
|
53
|
+
store.subscription = store.observable.subscribe();
|
|
54
|
+
}
|
|
55
|
+
shouldRestoreSubscriptionRef.current = false;
|
|
56
|
+
}
|
|
57
|
+
return function () {
|
|
58
|
+
// React StrictMode will call effects as `setup + teardown + setup` thus we can't trust this callback as "react is about to unmount"
|
|
59
|
+
// Tracking this ref lets us set the subscription back up on the next `setup` call if needed, and if it really did unmounted then all is well
|
|
60
|
+
shouldRestoreSubscriptionRef.current = !store.subscription.closed;
|
|
61
|
+
store.subscription.unsubscribe();
|
|
62
|
+
};
|
|
63
|
+
}, [observable]);
|
|
64
|
+
return useSyncExternalStore(subscribe, getSnapshot);
|
|
26
65
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef } from 'react';
|
|
2
1
|
import { observableCallback } from 'observable-callback';
|
|
2
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
3
3
|
var EMPTY_DEPS = [];
|
|
4
4
|
export function useObservableCallback(fn, dependencies) {
|
|
5
5
|
if (dependencies === void 0) { dependencies = EMPTY_DEPS; }
|
|
@@ -8,7 +8,6 @@ export function useObservableCallback(fn, dependencies) {
|
|
|
8
8
|
callbackRef.current = observableCallback();
|
|
9
9
|
}
|
|
10
10
|
var _a = callbackRef.current, calls$ = _a[0], call = _a[1];
|
|
11
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
12
11
|
var callback = useCallback(fn, dependencies);
|
|
13
12
|
useEffect(function () {
|
|
14
13
|
var subscription = calls$.pipe(callback).subscribe();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-rx",
|
|
3
|
-
"version": "3.0.0-
|
|
3
|
+
"version": "3.0.0-1",
|
|
4
4
|
"description": "React + RxJS = <3",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"action",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"sync",
|
|
29
29
|
"typesafe",
|
|
30
30
|
"typescript",
|
|
31
|
+
"use-sync-external-store",
|
|
31
32
|
"use"
|
|
32
33
|
],
|
|
33
34
|
"homepage": "https://react-rx.dev",
|
|
@@ -44,6 +45,7 @@
|
|
|
44
45
|
"Bjørge Næss <bjoerge@gmail.com>",
|
|
45
46
|
"Cody Olsen <stipsan@gmail.com> (https://github.com/stipsan)"
|
|
46
47
|
],
|
|
48
|
+
"sideEffects": false,
|
|
47
49
|
"main": "dist/cjs/index.js",
|
|
48
50
|
"module": "dist/esm/index.js",
|
|
49
51
|
"types": "dist/cjs/index.d.ts",
|
|
@@ -64,41 +66,46 @@
|
|
|
64
66
|
"dev": "cd website && npm run dev",
|
|
65
67
|
"prepublishOnly": "npm run clean && npm run build",
|
|
66
68
|
"watch": "run-p \"build:* -- --watch\"",
|
|
67
|
-
"test": "
|
|
69
|
+
"test": "jest",
|
|
68
70
|
"lint": "eslint --cache ."
|
|
69
71
|
},
|
|
70
72
|
"dependencies": {
|
|
71
|
-
"observable-callback": "^1.0.
|
|
73
|
+
"observable-callback": "^1.0.3"
|
|
72
74
|
},
|
|
73
75
|
"devDependencies": {
|
|
74
|
-
"@sanity/semantic-release-preset": "^
|
|
75
|
-
"@testing-library/dom": "^
|
|
76
|
-
"@testing-library/react": "^
|
|
77
|
-
"@types/
|
|
78
|
-
"@types/
|
|
79
|
-
"@types/react
|
|
80
|
-
"@
|
|
81
|
-
"@typescript-eslint/
|
|
82
|
-
"eslint": "
|
|
83
|
-
"eslint
|
|
84
|
-
"eslint-
|
|
85
|
-
"eslint-plugin-
|
|
86
|
-
"eslint-plugin-react
|
|
87
|
-
"
|
|
76
|
+
"@sanity/semantic-release-preset": "^2.0.5",
|
|
77
|
+
"@testing-library/dom": "^8.20.1",
|
|
78
|
+
"@testing-library/react": "^13.4.0",
|
|
79
|
+
"@types/jest": "^29.5.3",
|
|
80
|
+
"@types/node": "^18.17.5",
|
|
81
|
+
"@types/react": "^18.3.3",
|
|
82
|
+
"@types/react-dom": "^18.3.0",
|
|
83
|
+
"@typescript-eslint/eslint-plugin": "7.12.0",
|
|
84
|
+
"@typescript-eslint/parser": "7.12.0",
|
|
85
|
+
"eslint": "8.57.0",
|
|
86
|
+
"eslint-config-prettier": "9.1.0",
|
|
87
|
+
"eslint-plugin-prettier": "5.1.3",
|
|
88
|
+
"eslint-plugin-react": "7.34.2",
|
|
89
|
+
"eslint-plugin-react-compiler": "^0.0.0-experimental-51a85ea-20240601",
|
|
90
|
+
"eslint-plugin-react-hooks": "4.6.2",
|
|
91
|
+
"eslint-plugin-simple-import-sort": "^12.1.0",
|
|
92
|
+
"jest": "^29.6.2",
|
|
93
|
+
"jest-environment-jsdom": "^29.6.2",
|
|
94
|
+
"jsdom": "^20.0.3",
|
|
88
95
|
"npm-run-all": "^4.1.5",
|
|
89
|
-
"prettier": "^2.8.
|
|
90
|
-
"prettier-plugin-packagejson": "^2.4.
|
|
91
|
-
"react": "^18.
|
|
92
|
-
"react-dom": "^18.
|
|
93
|
-
"react-test-renderer": "^18.
|
|
94
|
-
"rimraf": "^
|
|
95
|
-
"rxjs": "^7.8.
|
|
96
|
-
"
|
|
97
|
-
"
|
|
96
|
+
"prettier": "^2.8.8",
|
|
97
|
+
"prettier-plugin-packagejson": "^2.4.5",
|
|
98
|
+
"react": "^18.3.1",
|
|
99
|
+
"react-dom": "^18.3.1",
|
|
100
|
+
"react-test-renderer": "^18.3.1",
|
|
101
|
+
"rimraf": "^3.0.2",
|
|
102
|
+
"rxjs": "^7.8.1",
|
|
103
|
+
"ts-jest": "^29.1.1",
|
|
104
|
+
"typescript": "4.7.4"
|
|
98
105
|
},
|
|
99
106
|
"peerDependencies": {
|
|
100
|
-
"react": "^
|
|
101
|
-
"rxjs": "^
|
|
107
|
+
"react": "^18.3 || >=19.0.0-rc",
|
|
108
|
+
"rxjs": "^7"
|
|
102
109
|
},
|
|
103
110
|
"es2015": "dist/es2015/index.js"
|
|
104
111
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import {BehaviorSubject, Observable, of} from 'rxjs'
|
|
2
|
-
import {useMemoObservable, useObservable} from '../useObservable'
|
|
3
|
-
import {createElement, Fragment, StrictMode, useEffect} from 'react'
|
|
4
1
|
import {act, render} from '@testing-library/react'
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
2
|
+
import {createElement, Fragment, StrictMode, useEffect} from 'react'
|
|
3
|
+
import {BehaviorSubject, Observable} from 'rxjs'
|
|
4
|
+
|
|
5
|
+
import {useObservable} from '../useObservable'
|
|
7
6
|
|
|
8
7
|
const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
|
|
9
8
|
|
|
@@ -16,17 +15,16 @@ test('Strict mode should trigger double mount effects and re-renders', async ()
|
|
|
16
15
|
const returnedValues: unknown[] = []
|
|
17
16
|
let mountCount = 0
|
|
18
17
|
function ObservableComponent() {
|
|
19
|
-
const observedValue = useObservable(observable, 0)
|
|
20
18
|
useEffect(() => {
|
|
21
19
|
mountCount++
|
|
22
20
|
}, [])
|
|
21
|
+
const observedValue = useObservable(observable)
|
|
23
22
|
returnedValues.push(observedValue)
|
|
24
23
|
return createElement(Fragment, null, observedValue)
|
|
25
24
|
}
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
expect(mountCount).toBe(2)
|
|
26
|
+
render(createElement(StrictMode, null, createElement(ObservableComponent)))
|
|
27
|
+
expect(mountCount).toEqual(2)
|
|
30
28
|
|
|
31
29
|
expect(returnedValues).toEqual([0, 0])
|
|
32
30
|
|
|
@@ -62,46 +60,3 @@ test('Strict mode should unsubscribe the source observable on unmount', () => {
|
|
|
62
60
|
rerender(createElement(StrictMode, null, createElement('div')))
|
|
63
61
|
expect(unsubscribed).toEqual([0, 1])
|
|
64
62
|
})
|
|
65
|
-
|
|
66
|
-
test('useMemoObservable should unsubscribe the source observable on unmount', () => {
|
|
67
|
-
const subscribed: number[] = []
|
|
68
|
-
const unsubscribed: number[] = []
|
|
69
|
-
let nextId = 0
|
|
70
|
-
function ObservableComponent() {
|
|
71
|
-
useMemoObservable(() => {
|
|
72
|
-
return new Observable(() => {
|
|
73
|
-
const id = nextId++
|
|
74
|
-
subscribed.push(id)
|
|
75
|
-
return () => {
|
|
76
|
-
unsubscribed.push(id)
|
|
77
|
-
}
|
|
78
|
-
})
|
|
79
|
-
}, [])
|
|
80
|
-
return createElement(Fragment, null)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const {rerender} = render(createElement(StrictMode, null, createElement(ObservableComponent)))
|
|
84
|
-
expect(subscribed).toEqual([0, 1])
|
|
85
|
-
rerender(createElement(StrictMode, null, createElement('div')))
|
|
86
|
-
expect(unsubscribed).toEqual([0, 1])
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
test('useAsObservable should work in strict mode', async () => {
|
|
90
|
-
const returnedValues: unknown[] = []
|
|
91
|
-
function ObservableComponent(props: {count: number}) {
|
|
92
|
-
const count$ = useAsObservable(props.count)
|
|
93
|
-
const count = useObservable(count$, props.count)
|
|
94
|
-
returnedValues.push(count)
|
|
95
|
-
return createElement(Fragment, null, 'ok')
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const {rerender} = render(
|
|
99
|
-
createElement(StrictMode, null, createElement(ObservableComponent, {count: 0})),
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
expect(returnedValues).toEqual([0, 0])
|
|
103
|
-
|
|
104
|
-
rerender(createElement(StrictMode, null, createElement(ObservableComponent, {count: 1})))
|
|
105
|
-
|
|
106
|
-
expect(returnedValues).toEqual([0, 0, 0, 0, 1, 1])
|
|
107
|
-
})
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {act, render, renderHook} from '@testing-library/react'
|
|
2
|
-
import {
|
|
2
|
+
import {createElement, Fragment} from 'react'
|
|
3
3
|
import {asyncScheduler, Observable, of, scheduled, Subject, timer} from 'rxjs'
|
|
4
4
|
import {mapTo} from 'rxjs/operators'
|
|
5
|
-
|
|
6
|
-
import {
|
|
5
|
+
|
|
6
|
+
import {useObservable} from '../useObservable'
|
|
7
7
|
|
|
8
8
|
test('should subscribe immediately on component mount and unsubscribe on component unmount', () => {
|
|
9
9
|
let subscribed = false
|
package/src/index.ts
CHANGED
|
@@ -1,15 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
export {observeState, observeCallback, observeContext, observeElement} from './utils'
|
|
4
|
-
export {
|
|
5
|
-
observeState as state,
|
|
6
|
-
observeCallback as handler,
|
|
7
|
-
observeContext as context,
|
|
8
|
-
observeElement as elementRef,
|
|
9
|
-
} from './utils'
|
|
10
|
-
export {observableCallback} from 'observable-callback'
|
|
11
|
-
|
|
12
|
-
// hooks
|
|
13
|
-
export {useObservable, useMemoObservable} from './useObservable'
|
|
14
|
-
export {useAsObservable} from './useAsObservable'
|
|
15
|
-
export {useObservableCallback} from './useObservableCallback'
|
|
1
|
+
export * from './useObservable'
|
|
2
|
+
export * from './useObservableEvent'
|