react-rx 2.1.2 → 3.0.0-0

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.
Files changed (42) hide show
  1. package/dist/cjs/WithObservable.d.ts +1 -1
  2. package/dist/cjs/__tests__/strictmode.test.js +43 -18
  3. package/dist/cjs/__tests__/useAsObservable.test.js +12 -11
  4. package/dist/cjs/__tests__/useObservable.test.js +31 -30
  5. package/dist/cjs/reactiveComponent.d.ts +2 -2
  6. package/dist/cjs/useAsObservable.d.ts +1 -0
  7. package/dist/cjs/useObservable.js +16 -48
  8. package/dist/es2015/WithObservable.d.ts +1 -1
  9. package/dist/es2015/__tests__/strictmode.test.js +34 -9
  10. package/dist/es2015/__tests__/useAsObservable.test.js +1 -0
  11. package/dist/es2015/__tests__/useObservable.test.js +1 -0
  12. package/dist/es2015/reactiveComponent.d.ts +2 -2
  13. package/dist/es2015/useAsObservable.d.ts +1 -0
  14. package/dist/es2015/useObservable.js +18 -50
  15. package/dist/esm/WithObservable.d.ts +1 -1
  16. package/dist/esm/__tests__/strictmode.test.js +35 -10
  17. package/dist/esm/__tests__/useAsObservable.test.js +1 -0
  18. package/dist/esm/__tests__/useObservable.test.js +1 -0
  19. package/dist/esm/reactiveComponent.d.ts +2 -2
  20. package/dist/esm/useAsObservable.d.ts +1 -0
  21. package/dist/esm/useObservable.js +18 -50
  22. package/package.json +21 -27
  23. package/src/__tests__/strictmode.test.ts +37 -9
  24. package/src/__tests__/useAsObservable.test.tsx +1 -0
  25. package/src/__tests__/useObservable.test.tsx +1 -0
  26. package/src/useAsObservable.ts +1 -0
  27. package/src/useObservable.ts +20 -70
  28. package/src/utils.ts +1 -1
  29. package/dist/cjs/useIsomorphicEffect.d.ts +0 -2
  30. package/dist/cjs/useIsomorphicEffect.js +0 -5
  31. package/dist/cjs/withPropsStream.d.ts +0 -8
  32. package/dist/cjs/withPropsStream.js +0 -55
  33. package/dist/es2015/useIsomorphicEffect.d.ts +0 -2
  34. package/dist/es2015/useIsomorphicEffect.js +0 -2
  35. package/dist/es2015/withPropsStream.d.ts +0 -8
  36. package/dist/es2015/withPropsStream.js +0 -17
  37. package/dist/esm/useIsomorphicEffect.d.ts +0 -2
  38. package/dist/esm/useIsomorphicEffect.js +0 -2
  39. package/dist/esm/withPropsStream.d.ts +0 -8
  40. package/dist/esm/withPropsStream.js +0 -28
  41. package/src/useIsomorphicEffect.ts +0 -3
  42. package/src/withPropsStream.tsx +0 -27
@@ -4,7 +4,7 @@ interface Props<T> {
4
4
  observable: Observable<T>;
5
5
  children: (value: T) => ReactNode;
6
6
  }
7
- declare type ObservableComponent<T> = ComponentType<Props<T>>;
7
+ type ObservableComponent<T> = ComponentType<Props<T>>;
8
8
  /**
9
9
  * @deprecated Use the useObservable hook instead
10
10
  */
@@ -14,7 +14,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
14
14
  function verb(n) { return function (v) { return step([n, v]); }; }
15
15
  function step(op) {
16
16
  if (f) throw new TypeError("Generator is already executing.");
17
- while (_) try {
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
18
  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;
19
19
  if (y = 0, t) op = [op[0] & 2, t.value];
20
20
  switch (op[0]) {
@@ -41,14 +41,15 @@ var useObservable_1 = require("../useObservable");
41
41
  var react_1 = require("react");
42
42
  var react_2 = require("@testing-library/react");
43
43
  var useAsObservable_1 = require("../useAsObservable");
44
+ var vitest_1 = require("vitest");
44
45
  var wait = function (ms) { return new Promise(function (resolve) { return setTimeout(resolve, ms); }); };
45
46
  // NOTE: Jest runs NODE_ENV=test by default, which enables development flags for React
46
- test('Strict mode should trigger double mount effects and re-renders', function () { return __awaiter(void 0, void 0, void 0, function () {
47
+ (0, vitest_1.test)('Strict mode should trigger double mount effects and re-renders', function () { return __awaiter(void 0, void 0, void 0, function () {
47
48
  function ObservableComponent() {
49
+ var observedValue = (0, useObservable_1.useObservable)(observable, 0);
48
50
  (0, react_1.useEffect)(function () {
49
51
  mountCount++;
50
52
  }, []);
51
- var observedValue = (0, useObservable_1.useObservable)(observable);
52
53
  returnedValues.push(observedValue);
53
54
  return (0, react_1.createElement)(react_1.Fragment, null, observedValue);
54
55
  }
@@ -61,26 +62,29 @@ test('Strict mode should trigger double mount effects and re-renders', function
61
62
  returnedValues = [];
62
63
  mountCount = 0;
63
64
  rerender = (0, react_2.render)((0, react_1.createElement)(react_1.StrictMode, null, (0, react_1.createElement)(ObservableComponent))).rerender;
64
- expect(mountCount).toEqual(2);
65
- expect(returnedValues).toEqual([0, 0]);
65
+ (0, vitest_1.expect)(mountCount).toBe(2);
66
+ (0, vitest_1.expect)(returnedValues).toEqual([0, 0]);
66
67
  return [4 /*yield*/, wait(10)];
67
68
  case 1:
68
69
  _a.sent();
69
70
  (0, react_2.act)(function () { return subject.next(1); });
70
- expect(returnedValues).toEqual([0, 0, 1, 1]);
71
+ (0, vitest_1.expect)(returnedValues).toEqual([0, 0, 1, 1]);
71
72
  (0, react_2.act)(function () { return subject.next(2); });
72
- expect(returnedValues).toEqual([0, 0, 1, 1, 2, 2]);
73
- expect(mountCount).toEqual(2);
73
+ (0, vitest_1.expect)(returnedValues).toEqual([0, 0, 1, 1, 2, 2]);
74
+ (0, vitest_1.expect)(mountCount).toEqual(2);
74
75
  return [2 /*return*/];
75
76
  }
76
77
  });
77
78
  }); });
78
- test('Strict mode should unsubscribe the source observable on unmount', function () {
79
- var subscribed = false;
79
+ (0, vitest_1.test)('Strict mode should unsubscribe the source observable on unmount', function () {
80
+ var subscribed = [];
81
+ var unsubscribed = [];
82
+ var nextId = 0;
80
83
  var observable = new rxjs_1.Observable(function () {
81
- subscribed = true;
84
+ var id = nextId++;
85
+ subscribed.push(id);
82
86
  return function () {
83
- subscribed = false;
87
+ unsubscribed.push(id);
84
88
  };
85
89
  });
86
90
  function ObservableComponent() {
@@ -88,14 +92,35 @@ test('Strict mode should unsubscribe the source observable on unmount', function
88
92
  return (0, react_1.createElement)(react_1.Fragment, null);
89
93
  }
90
94
  var rerender = (0, react_2.render)((0, react_1.createElement)(react_1.StrictMode, null, (0, react_1.createElement)(ObservableComponent))).rerender;
91
- expect(subscribed).toBe(true);
95
+ (0, vitest_1.expect)(subscribed).toEqual([0, 1]);
92
96
  rerender((0, react_1.createElement)(react_1.StrictMode, null, (0, react_1.createElement)('div')));
93
- expect(subscribed).toBe(false);
97
+ (0, vitest_1.expect)(unsubscribed).toEqual([0, 1]);
94
98
  });
95
- test('useAsObservable should work in strict mode', function () { return __awaiter(void 0, void 0, void 0, function () {
99
+ (0, vitest_1.test)('useMemoObservable should unsubscribe the source observable on unmount', function () {
100
+ var subscribed = [];
101
+ var unsubscribed = [];
102
+ var nextId = 0;
103
+ function ObservableComponent() {
104
+ (0, useObservable_1.useMemoObservable)(function () {
105
+ return new rxjs_1.Observable(function () {
106
+ var id = nextId++;
107
+ subscribed.push(id);
108
+ return function () {
109
+ unsubscribed.push(id);
110
+ };
111
+ });
112
+ }, []);
113
+ return (0, react_1.createElement)(react_1.Fragment, null);
114
+ }
115
+ var rerender = (0, react_2.render)((0, react_1.createElement)(react_1.StrictMode, null, (0, react_1.createElement)(ObservableComponent))).rerender;
116
+ (0, vitest_1.expect)(subscribed).toEqual([0, 1]);
117
+ rerender((0, react_1.createElement)(react_1.StrictMode, null, (0, react_1.createElement)('div')));
118
+ (0, vitest_1.expect)(unsubscribed).toEqual([0, 1]);
119
+ });
120
+ (0, vitest_1.test)('useAsObservable should work in strict mode', function () { return __awaiter(void 0, void 0, void 0, function () {
96
121
  function ObservableComponent(props) {
97
122
  var count$ = (0, useAsObservable_1.useAsObservable)(props.count);
98
- var count = (0, useObservable_1.useObservable)(count$);
123
+ var count = (0, useObservable_1.useObservable)(count$, props.count);
99
124
  returnedValues.push(count);
100
125
  return (0, react_1.createElement)(react_1.Fragment, null, 'ok');
101
126
  }
@@ -103,9 +128,9 @@ test('useAsObservable should work in strict mode', function () { return __awaite
103
128
  return __generator(this, function (_a) {
104
129
  returnedValues = [];
105
130
  rerender = (0, react_2.render)((0, react_1.createElement)(react_1.StrictMode, null, (0, react_1.createElement)(ObservableComponent, { count: 0 }))).rerender;
106
- expect(returnedValues).toEqual([0, 0]);
131
+ (0, vitest_1.expect)(returnedValues).toEqual([0, 0]);
107
132
  rerender((0, react_1.createElement)(react_1.StrictMode, null, (0, react_1.createElement)(ObservableComponent, { count: 1 })));
108
- expect(returnedValues).toEqual([0, 0, 0, 0, 1, 1]);
133
+ (0, vitest_1.expect)(returnedValues).toEqual([0, 0, 0, 0, 1, 1]);
109
134
  return [2 /*return*/];
110
135
  });
111
136
  }); });
@@ -6,7 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  var useAsObservable_1 = require("../useAsObservable");
7
7
  var react_1 = __importDefault(require("react"));
8
8
  var react_2 = require("@testing-library/react");
9
- test('the returned observable should receive a new value when component is rendered with a new value', function () {
9
+ var vitest_1 = require("vitest");
10
+ (0, vitest_1.test)('the returned observable should receive a new value when component is rendered with a new value', function () {
10
11
  var receivedValues = [];
11
12
  var _a = (0, react_2.renderHook)(function (props) {
12
13
  var observable = (0, useAsObservable_1.useAsObservable)(props.value);
@@ -21,10 +22,10 @@ test('the returned observable should receive a new value when component is rende
21
22
  }, { initialProps: { value: 'initial' } }), unmount = _a.unmount, rerender = _a.rerender;
22
23
  rerender({ value: 'rerender' });
23
24
  rerender({ value: 'rerender again' });
24
- expect(receivedValues).toEqual(['initial', 'rerender', 'rerender again']);
25
+ (0, vitest_1.expect)(receivedValues).toEqual(['initial', 'rerender', 'rerender again']);
25
26
  unmount();
26
27
  });
27
- test('the returned observable should *not* receive a new value when component is rendered with an unchanged value', function () {
28
+ (0, vitest_1.test)('the returned observable should *not* receive a new value when component is rendered with an unchanged value', function () {
28
29
  var receivedValues = [];
29
30
  var _a = (0, react_2.renderHook)(function (props) {
30
31
  var observable = (0, useAsObservable_1.useAsObservable)(props.value);
@@ -39,10 +40,10 @@ test('the returned observable should *not* receive a new value when component is
39
40
  }, { initialProps: { value: 'some value' } }), unmount = _a.unmount, rerender = _a.rerender;
40
41
  rerender({ value: 'some value' });
41
42
  rerender({ value: 'some value' });
42
- expect(receivedValues).toEqual(['some value']);
43
+ (0, vitest_1.expect)(receivedValues).toEqual(['some value']);
43
44
  unmount();
44
45
  });
45
- test('the returned observable should have the same identity across multiple re-renders/hook calls', function () {
46
+ (0, vitest_1.test)('the returned observable should have the same identity across multiple re-renders/hook calls', function () {
46
47
  var returnValues = [];
47
48
  var _a = (0, react_2.renderHook)(function () {
48
49
  returnValues.push((0, useAsObservable_1.useAsObservable)('render'));
@@ -50,11 +51,11 @@ test('the returned observable should have the same identity across multiple re-r
50
51
  rerender();
51
52
  rerender();
52
53
  var initial = returnValues[0], firstRerender = returnValues[1], secondRerender = returnValues[2];
53
- expect(initial).toBe(firstRerender);
54
- expect(firstRerender).toBe(secondRerender);
54
+ (0, vitest_1.expect)(initial).toBe(firstRerender);
55
+ (0, vitest_1.expect)(firstRerender).toBe(secondRerender);
55
56
  unmount();
56
57
  });
57
- test('the returned observable should complete on unmount', function () {
58
+ (0, vitest_1.test)('the returned observable should complete on unmount', function () {
58
59
  var didComplete = false;
59
60
  var _a = (0, react_2.renderHook)(function () {
60
61
  var observable = (0, useAsObservable_1.useAsObservable)('render');
@@ -69,9 +70,9 @@ test('the returned observable should complete on unmount', function () {
69
70
  };
70
71
  }, []);
71
72
  }), unmount = _a.unmount, rerender = _a.rerender;
72
- expect(didComplete).toBe(false);
73
+ (0, vitest_1.expect)(didComplete).toBe(false);
73
74
  rerender();
74
- expect(didComplete).toBe(false);
75
+ (0, vitest_1.expect)(didComplete).toBe(false);
75
76
  unmount();
76
- expect(didComplete).toBe(true);
77
+ (0, vitest_1.expect)(didComplete).toBe(true);
77
78
  });
@@ -5,7 +5,8 @@ var useObservable_1 = require("../useObservable");
5
5
  var rxjs_1 = require("rxjs");
6
6
  var operators_1 = require("rxjs/operators");
7
7
  var react_2 = require("react");
8
- test('should subscribe immediately on component mount and unsubscribe on component unmount', function () {
8
+ var vitest_1 = require("vitest");
9
+ (0, vitest_1.test)('should subscribe immediately on component mount and unsubscribe on component unmount', function () {
9
10
  var subscribed = false;
10
11
  var observable = new rxjs_1.Observable(function () {
11
12
  subscribed = true;
@@ -13,27 +14,27 @@ test('should subscribe immediately on component mount and unsubscribe on compone
13
14
  subscribed = false;
14
15
  };
15
16
  });
16
- expect(subscribed).toBe(false);
17
+ (0, vitest_1.expect)(subscribed).toBe(false);
17
18
  var unmount = (0, react_1.renderHook)(function () { return (0, useObservable_1.useObservable)(observable); }).unmount;
18
- expect(subscribed).toBe(true);
19
+ (0, vitest_1.expect)(subscribed).toBe(true);
19
20
  unmount();
20
- expect(subscribed).toBe(false);
21
+ (0, vitest_1.expect)(subscribed).toBe(false);
21
22
  });
22
- test('should only subscribe once when given same observable on re-renders', function () {
23
+ (0, vitest_1.test)('should only subscribe once when given same observable on re-renders', function () {
23
24
  var subscriptionCount = 0;
24
25
  var observable = new rxjs_1.Observable(function () {
25
26
  subscriptionCount++;
26
27
  });
27
- expect(subscriptionCount).toBe(0);
28
+ (0, vitest_1.expect)(subscriptionCount).toBe(0);
28
29
  var _a = (0, react_1.renderHook)(function () { return (0, useObservable_1.useObservable)(observable); }), unmount = _a.unmount, rerender = _a.rerender;
29
- expect(subscriptionCount).toBe(1);
30
+ (0, vitest_1.expect)(subscriptionCount).toBe(1);
30
31
  rerender();
31
- expect(subscriptionCount).toBe(1);
32
+ (0, vitest_1.expect)(subscriptionCount).toBe(1);
32
33
  unmount();
33
34
  (0, react_1.renderHook)(function () { return (0, useObservable_1.useObservable)(observable); });
34
- expect(subscriptionCount).toBe(2);
35
+ (0, vitest_1.expect)(subscriptionCount).toBe(2);
35
36
  });
36
- test('should not return undefined during render if initial value is given', function () {
37
+ (0, vitest_1.test)('should not return undefined during render if initial value is given', function () {
37
38
  var observable = (0, rxjs_1.timer)(100).pipe((0, operators_1.mapTo)('emitted value'));
38
39
  var returnedValues = [];
39
40
  function ObservableComponent() {
@@ -42,9 +43,9 @@ test('should not return undefined during render if initial value is given', func
42
43
  return (0, react_2.createElement)(react_2.Fragment, null, observedValue);
43
44
  }
44
45
  (0, react_1.render)((0, react_2.createElement)(ObservableComponent));
45
- expect(returnedValues).toEqual(expect.arrayContaining(['initial value']));
46
+ (0, vitest_1.expect)(returnedValues).toEqual(vitest_1.expect.arrayContaining(['initial value']));
46
47
  });
47
- test('should not return undefined during render if observable is sync', function () {
48
+ (0, vitest_1.test)('should not return undefined during render if observable is sync', function () {
48
49
  var observable = (0, rxjs_1.of)('initial value');
49
50
  var returnedValues = [];
50
51
  function ObservableComponent() {
@@ -53,9 +54,9 @@ test('should not return undefined during render if observable is sync', function
53
54
  return (0, react_2.createElement)(react_2.Fragment, null, observedValue);
54
55
  }
55
56
  (0, react_1.render)((0, react_2.createElement)(ObservableComponent));
56
- expect(returnedValues).toEqual(expect.arrayContaining(['initial value']));
57
+ (0, vitest_1.expect)(returnedValues).toEqual(vitest_1.expect.arrayContaining(['initial value']));
57
58
  });
58
- test('should return undefined during first render if observable is async', function () {
59
+ (0, vitest_1.test)('should return undefined during first render if observable is async', function () {
59
60
  var observable = (0, rxjs_1.scheduled)('async value', rxjs_1.asyncScheduler);
60
61
  var returnedValues = [];
61
62
  function ObservableComponent() {
@@ -64,53 +65,53 @@ test('should return undefined during first render if observable is async', funct
64
65
  return (0, react_2.createElement)(react_2.Fragment, null, observedValue);
65
66
  }
66
67
  (0, react_1.render)((0, react_2.createElement)(ObservableComponent));
67
- expect(returnedValues).toEqual(expect.arrayContaining([undefined]));
68
+ (0, vitest_1.expect)(returnedValues).toEqual(vitest_1.expect.arrayContaining([undefined]));
68
69
  });
69
- test('should have sync values from an observable as initial value', function () {
70
+ (0, vitest_1.test)('should have sync values from an observable as initial value', function () {
70
71
  var observable = (0, rxjs_1.of)('something sync');
71
72
  var result = (0, react_1.renderHook)(function () { return (0, useObservable_1.useObservable)(observable); }).result;
72
- expect(result.current).toBe('something sync');
73
+ (0, vitest_1.expect)(result.current).toBe('something sync');
73
74
  });
74
- test('should have undefined as initial value from delayed observables', function () {
75
+ (0, vitest_1.test)('should have undefined as initial value from delayed observables', function () {
75
76
  var _a = (0, react_1.renderHook)(function () {
76
77
  return (0, useObservable_1.useObservable)((0, rxjs_1.scheduled)('something async', rxjs_1.asyncScheduler));
77
78
  }), result = _a.result, unmount = _a.unmount;
78
- expect(result.current).toBeUndefined();
79
+ (0, vitest_1.expect)(result.current).toBeUndefined();
79
80
  unmount();
80
81
  });
81
- test('should have passed initialValue as initial value from delayed observables', function () {
82
+ (0, vitest_1.test)('should have passed initialValue as initial value from delayed observables', function () {
82
83
  var _a = (0, react_1.renderHook)(function () {
83
84
  return (0, useObservable_1.useObservable)((0, rxjs_1.scheduled)('something async', rxjs_1.asyncScheduler), 'initial');
84
85
  }), result = _a.result, unmount = _a.unmount;
85
- expect(result.current).toBe('initial');
86
+ (0, vitest_1.expect)(result.current).toBe('initial');
86
87
  unmount();
87
88
  });
88
- test('should update with values from observables', function () {
89
+ (0, vitest_1.test)('should update with values from observables', function () {
89
90
  var values$ = new rxjs_1.Subject();
90
91
  var _a = (0, react_1.renderHook)(function () { return (0, useObservable_1.useObservable)(values$); }), result = _a.result, unmount = _a.unmount;
91
- expect(result.current).toBe(undefined);
92
+ (0, vitest_1.expect)(result.current).toBe(undefined);
92
93
  (0, react_1.act)(function () { return values$.next('something'); });
93
- expect(result.current).toBe('something');
94
+ (0, vitest_1.expect)(result.current).toBe('something');
94
95
  (0, react_1.act)(function () { return values$.next('otherthing'); });
95
- expect(result.current).toBe('otherthing');
96
+ (0, vitest_1.expect)(result.current).toBe('otherthing');
96
97
  unmount();
97
98
  });
98
- test('should re-subscribe when receiving a new observable', function () {
99
+ (0, vitest_1.test)('should re-subscribe when receiving a new observable', function () {
99
100
  var first$ = new rxjs_1.Subject();
100
101
  var second$ = new rxjs_1.Subject();
101
102
  var current$ = first$;
102
103
  var _a = (0, react_1.renderHook)(function () { return (0, useObservable_1.useObservable)(current$, '!!initial!!'); }), result = _a.result, rerender = _a.rerender, unmount = _a.unmount;
103
104
  (0, react_1.act)(function () { return first$.next('first 1'); });
104
- expect(result.current).toBe('first 1');
105
+ (0, vitest_1.expect)(result.current).toBe('first 1');
105
106
  current$ = second$;
106
107
  rerender();
107
108
  // since observable #2 hasn't emitted a value yet, we should use the initial value
108
- expect(result.current).toBe('!!initial!!');
109
+ (0, vitest_1.expect)(result.current).toBe('!!initial!!');
109
110
  // Now we should be subscribed to second$ and it's emission should be returned
110
111
  (0, react_1.act)(function () { return second$.next('second 1'); });
111
- expect(result.current).toBe('second 1');
112
+ (0, vitest_1.expect)(result.current).toBe('second 1');
112
113
  // we should no longer be subscribed to the first and ignore any emissions
113
114
  (0, react_1.act)(function () { return first$.next('first 2'); });
114
- expect(result.current).toBe('second 1');
115
+ (0, vitest_1.expect)(result.current).toBe('second 1');
115
116
  unmount();
116
117
  });
@@ -1,8 +1,8 @@
1
1
  import { Observable } from 'rxjs';
2
2
  import { FunctionComponent, ReactNode, Ref } from 'react';
3
- declare type Component<Props> = (input$: Observable<Props>) => Observable<ReactNode>;
3
+ type Component<Props> = (input$: Observable<Props>) => Observable<ReactNode>;
4
4
  export declare function reactiveComponent<Props>(observable: Observable<Props>): FunctionComponent<Props>;
5
5
  export declare function reactiveComponent<Props>(component: Component<Props>): FunctionComponent<Props>;
6
- declare type ForwardRefComponent<RefType, Props> = (input$: Observable<Props>, ref: Ref<RefType>) => Observable<ReactNode>;
6
+ type ForwardRefComponent<RefType, Props> = (input$: Observable<Props>, ref: Ref<RefType>) => Observable<ReactNode>;
7
7
  export declare function forwardRef<RefType, Props = {}>(component: ForwardRefComponent<RefType, Props>): import("react").ForwardRefExoticComponent<import("react").PropsWithoutRef<Props> & import("react").RefAttributes<RefType>>;
8
8
  export {};
@@ -4,6 +4,7 @@ import { Observable } from 'rxjs';
4
4
  * Returns an observable representing updates to any React value (props, state or any other calculated value)
5
5
  * Note: the returned observable is the same instance throughout the component lifecycle
6
6
  * @param value
7
+ * @deprecated use an `of` operator and `useMemoObservable` instead for a faster, more robust and siimpler solution
7
8
  */
8
9
  export declare function useAsObservable<T>(value: T): Observable<T>;
9
10
  export declare function useAsObservable<T, K>(value: T, operator: (input: Observable<T>) => Observable<K>): Observable<K>;
@@ -2,62 +2,30 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.useMemoObservable = exports.useObservable = void 0;
4
4
  var react_1 = require("react");
5
- var shim_1 = require("use-sync-external-store/shim");
6
5
  var operators_1 = require("rxjs/operators");
7
6
  function getValue(value) {
8
7
  return typeof value === 'function' ? value() : value;
9
8
  }
10
- var cache = new WeakMap();
11
- function getOrCreateStore(inputObservable, initialValue) {
12
- if (!cache.has(inputObservable)) {
13
- var entry_1 = { currentValue: initialValue };
14
- entry_1.observable = inputObservable.pipe((0, operators_1.shareReplay)({ refCount: true, bufferSize: 1 }), (0, operators_1.tap)(function (value) { return (entry_1.currentValue = value); }));
15
- // Eagerly subscribe to sync set `entry.currentValue` to what the observable returns
16
- entry_1.subscription = entry_1.observable.subscribe();
17
- cache.set(inputObservable, entry_1);
18
- }
19
- return cache.get(inputObservable);
20
- }
21
9
  function useObservable(observable, initialValue) {
22
- var _a = (0, react_1.useMemo)(function () {
23
- var store = getOrCreateStore(observable, getValue(initialValue));
24
- if (store.subscription.closed) {
25
- store.subscription = store.observable.subscribe();
26
- }
27
- return [
28
- function getSnapshot() {
29
- // @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.
30
- return store.currentValue;
31
- },
32
- function subscribe(callback) {
33
- // @TODO: perf opt opportunity: we could do `store.subscription.unsubscribe()` here as we only need 1 subscription active to keep the observer alive
34
- var sub = store.observable.subscribe(callback);
35
- return function () {
36
- sub.unsubscribe();
37
- };
38
- },
39
- ];
40
- }, [observable]), getSnapshot = _a[0], subscribe = _a[1];
41
- var shouldRestoreSubscriptionRef = (0, react_1.useRef)(false);
10
+ var _a = (0, react_1.useState)(function () { return getValue(initialValue); }), value = _a[0], setValue = _a[1];
11
+ var initialValueRef = (0, react_1.useRef)(initialValue);
12
+ initialValueRef.current = initialValue;
42
13
  (0, react_1.useEffect)(function () {
43
- var store = getOrCreateStore(observable, getValue(initialValue));
44
- if (shouldRestoreSubscriptionRef.current) {
45
- if (store.subscription.closed) {
46
- store.subscription = store.observable.subscribe();
47
- }
48
- shouldRestoreSubscriptionRef.current = false;
49
- }
50
- return function () {
51
- // React StrictMode will call effects as `setup + teardown + setup` thus we can't trust this callback as "react is about to unmount"
52
- // 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
53
- shouldRestoreSubscriptionRef.current = !store.subscription.closed;
54
- store.subscription.unsubscribe();
55
- };
56
- }, [observable]);
57
- return (0, shim_1.useSyncExternalStore)(subscribe, getSnapshot);
14
+ setValue(getValue(initialValueRef.current));
15
+ var subscription = observable.pipe((0, operators_1.tap)(function (next) { return setValue(next); })).subscribe();
16
+ return function () { return subscription.unsubscribe(); };
17
+ }, [initialValueRef, observable]);
18
+ return value;
58
19
  }
59
20
  exports.useObservable = useObservable;
60
21
  function useMemoObservable(observableOrFactory, deps, initialValue) {
61
- return useObservable((0, react_1.useMemo)(function () { return getValue(observableOrFactory); }, deps), initialValue);
22
+ var _a = (0, react_1.useState)(function () { return getValue(initialValue); }), value = _a[0], setValue = _a[1];
23
+ (0, react_1.useEffect)(function () {
24
+ var subscription = getValue(observableOrFactory)
25
+ .pipe((0, operators_1.tap)(function (next) { return setValue(next); }))
26
+ .subscribe();
27
+ return function () { return subscription.unsubscribe(); };
28
+ }, deps);
29
+ return value;
62
30
  }
63
31
  exports.useMemoObservable = useMemoObservable;
@@ -4,7 +4,7 @@ interface Props<T> {
4
4
  observable: Observable<T>;
5
5
  children: (value: T) => ReactNode;
6
6
  }
7
- declare type ObservableComponent<T> = ComponentType<Props<T>>;
7
+ type ObservableComponent<T> = ComponentType<Props<T>>;
8
8
  /**
9
9
  * @deprecated Use the useObservable hook instead
10
10
  */
@@ -8,10 +8,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { BehaviorSubject, Observable } from 'rxjs';
11
- import { useObservable } from '../useObservable';
11
+ import { useMemoObservable, useObservable } from '../useObservable';
12
12
  import { createElement, Fragment, StrictMode, useEffect } from 'react';
13
13
  import { act, render } from '@testing-library/react';
14
14
  import { useAsObservable } from '../useAsObservable';
15
+ import { test, expect } from 'vitest';
15
16
  const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));
16
17
  // NOTE: Jest runs NODE_ENV=test by default, which enables development flags for React
17
18
  test('Strict mode should trigger double mount effects and re-renders', () => __awaiter(void 0, void 0, void 0, function* () {
@@ -20,15 +21,15 @@ test('Strict mode should trigger double mount effects and re-renders', () => __a
20
21
  const returnedValues = [];
21
22
  let mountCount = 0;
22
23
  function ObservableComponent() {
24
+ const observedValue = useObservable(observable, 0);
23
25
  useEffect(() => {
24
26
  mountCount++;
25
27
  }, []);
26
- const observedValue = useObservable(observable);
27
28
  returnedValues.push(observedValue);
28
29
  return createElement(Fragment, null, observedValue);
29
30
  }
30
31
  const { rerender } = render(createElement(StrictMode, null, createElement(ObservableComponent)));
31
- expect(mountCount).toEqual(2);
32
+ expect(mountCount).toBe(2);
32
33
  expect(returnedValues).toEqual([0, 0]);
33
34
  yield wait(10);
34
35
  act(() => subject.next(1));
@@ -38,11 +39,14 @@ test('Strict mode should trigger double mount effects and re-renders', () => __a
38
39
  expect(mountCount).toEqual(2);
39
40
  }));
40
41
  test('Strict mode should unsubscribe the source observable on unmount', () => {
41
- let subscribed = false;
42
+ const subscribed = [];
43
+ const unsubscribed = [];
44
+ let nextId = 0;
42
45
  const observable = new Observable(() => {
43
- subscribed = true;
46
+ const id = nextId++;
47
+ subscribed.push(id);
44
48
  return () => {
45
- subscribed = false;
49
+ unsubscribed.push(id);
46
50
  };
47
51
  });
48
52
  function ObservableComponent() {
@@ -50,15 +54,36 @@ test('Strict mode should unsubscribe the source observable on unmount', () => {
50
54
  return createElement(Fragment, null);
51
55
  }
52
56
  const { rerender } = render(createElement(StrictMode, null, createElement(ObservableComponent)));
53
- expect(subscribed).toBe(true);
57
+ expect(subscribed).toEqual([0, 1]);
58
+ rerender(createElement(StrictMode, null, createElement('div')));
59
+ expect(unsubscribed).toEqual([0, 1]);
60
+ });
61
+ test('useMemoObservable should unsubscribe the source observable on unmount', () => {
62
+ const subscribed = [];
63
+ const unsubscribed = [];
64
+ let nextId = 0;
65
+ function ObservableComponent() {
66
+ useMemoObservable(() => {
67
+ return new Observable(() => {
68
+ const id = nextId++;
69
+ subscribed.push(id);
70
+ return () => {
71
+ unsubscribed.push(id);
72
+ };
73
+ });
74
+ }, []);
75
+ return createElement(Fragment, null);
76
+ }
77
+ const { rerender } = render(createElement(StrictMode, null, createElement(ObservableComponent)));
78
+ expect(subscribed).toEqual([0, 1]);
54
79
  rerender(createElement(StrictMode, null, createElement('div')));
55
- expect(subscribed).toBe(false);
80
+ expect(unsubscribed).toEqual([0, 1]);
56
81
  });
57
82
  test('useAsObservable should work in strict mode', () => __awaiter(void 0, void 0, void 0, function* () {
58
83
  const returnedValues = [];
59
84
  function ObservableComponent(props) {
60
85
  const count$ = useAsObservable(props.count);
61
- const count = useObservable(count$);
86
+ const count = useObservable(count$, props.count);
62
87
  returnedValues.push(count);
63
88
  return createElement(Fragment, null, 'ok');
64
89
  }
@@ -1,6 +1,7 @@
1
1
  import { useAsObservable } from '../useAsObservable';
2
2
  import React from 'react';
3
3
  import { renderHook } from '@testing-library/react';
4
+ import { test, expect } from 'vitest';
4
5
  test('the returned observable should receive a new value when component is rendered with a new value', () => {
5
6
  const receivedValues = [];
6
7
  const { unmount, rerender } = renderHook((props) => {
@@ -3,6 +3,7 @@ import { useObservable } from '../useObservable';
3
3
  import { asyncScheduler, Observable, of, scheduled, Subject, timer } from 'rxjs';
4
4
  import { mapTo } from 'rxjs/operators';
5
5
  import { createElement, Fragment } from 'react';
6
+ import { test, expect } from 'vitest';
6
7
  test('should subscribe immediately on component mount and unsubscribe on component unmount', () => {
7
8
  let subscribed = false;
8
9
  const observable = new Observable(() => {
@@ -1,8 +1,8 @@
1
1
  import { Observable } from 'rxjs';
2
2
  import { FunctionComponent, ReactNode, Ref } from 'react';
3
- declare type Component<Props> = (input$: Observable<Props>) => Observable<ReactNode>;
3
+ type Component<Props> = (input$: Observable<Props>) => Observable<ReactNode>;
4
4
  export declare function reactiveComponent<Props>(observable: Observable<Props>): FunctionComponent<Props>;
5
5
  export declare function reactiveComponent<Props>(component: Component<Props>): FunctionComponent<Props>;
6
- declare type ForwardRefComponent<RefType, Props> = (input$: Observable<Props>, ref: Ref<RefType>) => Observable<ReactNode>;
6
+ type ForwardRefComponent<RefType, Props> = (input$: Observable<Props>, ref: Ref<RefType>) => Observable<ReactNode>;
7
7
  export declare function forwardRef<RefType, Props = {}>(component: ForwardRefComponent<RefType, Props>): import("react").ForwardRefExoticComponent<import("react").PropsWithoutRef<Props> & import("react").RefAttributes<RefType>>;
8
8
  export {};
@@ -4,6 +4,7 @@ import { Observable } from 'rxjs';
4
4
  * Returns an observable representing updates to any React value (props, state or any other calculated value)
5
5
  * Note: the returned observable is the same instance throughout the component lifecycle
6
6
  * @param value
7
+ * @deprecated use an `of` operator and `useMemoObservable` instead for a faster, more robust and siimpler solution
7
8
  */
8
9
  export declare function useAsObservable<T>(value: T): Observable<T>;
9
10
  export declare function useAsObservable<T, K>(value: T, operator: (input: Observable<T>) => Observable<K>): Observable<K>;