@alcadur/react-events-hook 1.0.1 → 1.1.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.
- package/README.md +106 -4
- package/package.json +48 -48
- package/src/actions/clear-memo.ts +8 -0
- package/src/actions/emit-event.ts +19 -2
- package/src/actions/index.test.ts +147 -77
- package/src/actions/index.ts +1 -0
- package/src/actions/on-event.ts +7 -2
- package/src/hooks/useEvents.test.ts +36 -1
- package/src/hooks/useEvents.ts +2 -2
- package/src/shared-events.ts +2 -0
package/README.md
CHANGED
|
@@ -2,15 +2,117 @@
|
|
|
2
2
|
|
|
3
3
|
Simple Observer pattern for React.
|
|
4
4
|
|
|
5
|
+
[Live Demo](https://codesandbox.io/p/sandbox/w9wqwh)
|
|
6
|
+
|
|
7
|
+
[npm package](https://www.npmjs.com/package/@alcadur/react-events-hook)
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
```
|
|
11
|
+
npm i @alcadur/react-events-hook
|
|
12
|
+
```
|
|
13
|
+
|
|
5
14
|
## What for?
|
|
6
15
|
Useful for communication between components in different parts of the view.
|
|
7
16
|
|
|
8
17
|
Using standard React mechanisms, it's also possible to synchronize with external events.
|
|
9
18
|
|
|
10
|
-
|
|
19
|
+
Lets look at an example:
|
|
20
|
+
|
|
21
|
+

|
|
22
|
+
|
|
23
|
+
If we want to change background colors for C1, C2 and C3 from C6 we have few options:
|
|
24
|
+
1. use context
|
|
25
|
+
2. use global state
|
|
26
|
+
3. props drilling
|
|
27
|
+
|
|
28
|
+
[](https://postimg.cc/BjdpLgZZ)
|
|
29
|
+
|
|
30
|
+
With events, we can avoid unnecessary boilerplate.
|
|
31
|
+
|
|
32
|
+
[](https://postimg.cc/ZWcY0fxy)
|
|
33
|
+
[](https://postimg.cc/SX3BK83X)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## Docs
|
|
37
|
+
|
|
38
|
+
### onEvent
|
|
39
|
+
`onEvent` is a function that subscribes to event outsite of React context.
|
|
40
|
+
|
|
41
|
+
:warning: If you want to use `onEvent` in react component without `useEvents` hook, make sure to not add a new listiner on each render.
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
onEvent('eventName', (...args) => console.log('event fired with: ', args));
|
|
45
|
+
````
|
|
46
|
+
|
|
47
|
+
### useEvents
|
|
48
|
+
Use to subscribe to events in React components.
|
|
49
|
+
```tsx
|
|
50
|
+
const { onEvent, emitEvent, removeEvent } = useEvents({ eventName: (...args) => console.log('event fired with: ', args) })
|
|
11
51
|
```
|
|
12
|
-
|
|
52
|
+
|
|
53
|
+
Event name can be: `string`, `number` or `symbol`
|
|
54
|
+
```ts
|
|
55
|
+
export const Events = {
|
|
56
|
+
C4_TITLE_CHANGE: Symbol(),
|
|
57
|
+
REMOVE_LEFT_CHILD: "remove-element",
|
|
58
|
+
CHANGE_COLOR: 3
|
|
59
|
+
} as const;
|
|
13
60
|
```
|
|
14
61
|
|
|
15
|
-
|
|
16
|
-
|
|
62
|
+
You don't need to use `useEvents` hook to emit or remove events.
|
|
63
|
+
|
|
64
|
+
### emitEvent
|
|
65
|
+
`emitEvent` is a function that emits single event.
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
emitEvent('eventName', 'some data');
|
|
69
|
+
emitEvent('eventWithParams', 1, 2, 3);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
#### emitEvent.memo
|
|
73
|
+
`emitEvent.memo` remembers the last emitted value and triggers a new callback immediately with that value. To clear memorized value, call `emitEvent.memo(undefined)` or to avoid extra emission, use `clearMemo` (described below)
|
|
74
|
+
```ts
|
|
75
|
+
onEvent('eventName', (data) => console.log(data))
|
|
76
|
+
emitEvent('eventName', 'some data');
|
|
77
|
+
// expected log: some data
|
|
78
|
+
|
|
79
|
+
emitEvent('eventName', 'some data');
|
|
80
|
+
onEvent('eventName', (data) => console.log(data))
|
|
81
|
+
// no logs expected, event was emited before subscription
|
|
82
|
+
|
|
83
|
+
emitEvent.memo('eventName', 'some data');
|
|
84
|
+
onEvent('eventName', (data) => console.log(data))
|
|
85
|
+
// expected log: some data
|
|
86
|
+
|
|
87
|
+
emitEvent.memo('eventName', 'some data');
|
|
88
|
+
emitEvent('eventName', 123); // value emited without memo
|
|
89
|
+
onEvent('eventName', (data) => console.log(data))
|
|
90
|
+
// expected log: some data
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
There no difference between directly usage of `emitEvent` and `emitEvent` returned from `useEvents` hook.
|
|
95
|
+
|
|
96
|
+
### removeEvent
|
|
97
|
+
`removeEvent` is a function that removes event listener.
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
const eventHandler = () => console.log('event fired');
|
|
101
|
+
onEvent('eventName', eventHandler);
|
|
102
|
+
removeEvent('eventName', eventHandler);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
There no difference between directly usage of `removeEvent` and `removeEvent` returned from `useEvents` hook.
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
### clearMemo
|
|
109
|
+
`clearMemo` removes the last memorized event emitted value
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
clearMemo('eventName');
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
There no difference between directly usage of `clearMemo` and `clearMemo` returned from `useEvents` hook.
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
|
package/package.json
CHANGED
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@alcadur/react-events-hook",
|
|
3
|
-
"version": "1.0
|
|
4
|
-
"description": "
|
|
5
|
-
"keywords": [
|
|
6
|
-
"react",
|
|
7
|
-
"react",
|
|
8
|
-
"hook",
|
|
9
|
-
"react",
|
|
10
|
-
"hooks",
|
|
11
|
-
"events",
|
|
12
|
-
"events",
|
|
13
|
-
"hook"
|
|
14
|
-
],
|
|
15
|
-
"homepage": "https://github.com/Alcadur/react-events-hook#readme",
|
|
16
|
-
"bugs": {
|
|
17
|
-
"url": "https://github.com/Alcadur/react-events-hook/issues"
|
|
18
|
-
},
|
|
19
|
-
"repository": {
|
|
20
|
-
"type": "git",
|
|
21
|
-
"url": "git+https://github.com/Alcadur/react-events-hook.git"
|
|
22
|
-
},
|
|
23
|
-
"license": "MIT",
|
|
24
|
-
"author": "Alcadur",
|
|
25
|
-
"type": "module",
|
|
26
|
-
"main": "index.ts",
|
|
27
|
-
"scripts": {
|
|
28
|
-
"test": "vitest run"
|
|
29
|
-
},
|
|
30
|
-
"peerDependencies": {
|
|
31
|
-
"react": "*18.0.0"
|
|
32
|
-
},
|
|
33
|
-
"devDependencies": {
|
|
34
|
-
"@rollup/plugin-typescript": "^12.3.0",
|
|
35
|
-
"@testing-library/dom": "^10.4.1",
|
|
36
|
-
"@testing-library/jest-dom": "^6.9.1",
|
|
37
|
-
"@testing-library/react": "^14.3.1",
|
|
38
|
-
"@types/react": "^18.3.28",
|
|
39
|
-
"@types/react-dom": "^18.3.7",
|
|
40
|
-
"@vitejs/plugin-react": "^6.0.1",
|
|
41
|
-
"jsdom": "^29.0.2",
|
|
42
|
-
"react": "^18.3.1",
|
|
43
|
-
"react-dom": "^18.3.1",
|
|
44
|
-
"rollup": "^4.59.0",
|
|
45
|
-
"typescript": "^5.9.3",
|
|
46
|
-
"vitest": "^4.1.3"
|
|
47
|
-
}
|
|
48
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@alcadur/react-events-hook",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Helps in easy communication between React components as also with external sources",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"react",
|
|
7
|
+
"react",
|
|
8
|
+
"hook",
|
|
9
|
+
"react",
|
|
10
|
+
"hooks",
|
|
11
|
+
"events",
|
|
12
|
+
"events",
|
|
13
|
+
"hook"
|
|
14
|
+
],
|
|
15
|
+
"homepage": "https://github.com/Alcadur/react-events-hook#readme",
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/Alcadur/react-events-hook/issues"
|
|
18
|
+
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/Alcadur/react-events-hook.git"
|
|
22
|
+
},
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"author": "Alcadur",
|
|
25
|
+
"type": "module",
|
|
26
|
+
"main": "index.ts",
|
|
27
|
+
"scripts": {
|
|
28
|
+
"test": "vitest run"
|
|
29
|
+
},
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"react": "*18.0.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@rollup/plugin-typescript": "^12.3.0",
|
|
35
|
+
"@testing-library/dom": "^10.4.1",
|
|
36
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
37
|
+
"@testing-library/react": "^14.3.1",
|
|
38
|
+
"@types/react": "^18.3.28",
|
|
39
|
+
"@types/react-dom": "^18.3.7",
|
|
40
|
+
"@vitejs/plugin-react": "^6.0.1",
|
|
41
|
+
"jsdom": "^29.0.2",
|
|
42
|
+
"react": "^18.3.1",
|
|
43
|
+
"react-dom": "^18.3.1",
|
|
44
|
+
"rollup": "^4.59.0",
|
|
45
|
+
"typescript": "^5.9.3",
|
|
46
|
+
"vitest": "^4.1.3"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { sharedEvents } from "../shared-events";
|
|
1
|
+
import { lastEmittedValues, sharedEvents } from "../shared-events";
|
|
2
2
|
import { EventNameType } from "../events.model";
|
|
3
|
+
import { clearMemo } from "./clear-memo";
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
function emitter(event: EventNameType, ...args: any[]) {
|
|
5
6
|
const callbacks = sharedEvents[event];
|
|
6
7
|
if (!callbacks) {
|
|
7
8
|
return;
|
|
@@ -9,3 +10,19 @@ export function emitEvent(event: EventNameType, ...args: any[]) {
|
|
|
9
10
|
|
|
10
11
|
callbacks.forEach(callback => callback(...args));
|
|
11
12
|
}
|
|
13
|
+
|
|
14
|
+
export function emitEvent(event: EventNameType, ...args: any[]) {
|
|
15
|
+
emitter(event, ...args);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (!emitEvent.memo) {
|
|
19
|
+
emitEvent.memo = (event: EventNameType, ...args: any[]) => {
|
|
20
|
+
lastEmittedValues[event] = args;
|
|
21
|
+
|
|
22
|
+
if (args.length === 1 && args[0] === undefined) {
|
|
23
|
+
clearMemo(event);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
emitter(event, ...args);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -1,77 +1,147 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { sharedEvents } from "../shared-events";
|
|
3
|
-
import { emitEvent } from "./emit-event";
|
|
4
|
-
import { onEvent } from "./on-event";
|
|
5
|
-
import { removeEvent } from "./remove-event";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { lastEmittedValues, sharedEvents } from "../shared-events";
|
|
3
|
+
import { emitEvent } from "./emit-event";
|
|
4
|
+
import { onEvent } from "./on-event";
|
|
5
|
+
import { removeEvent } from "./remove-event";
|
|
6
|
+
import { clearMemo } from "./clear-memo";
|
|
7
|
+
|
|
8
|
+
describe("actions", () => {
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
Object.keys(sharedEvents).forEach((key) => delete sharedEvents[key]);
|
|
11
|
+
Object.keys(lastEmittedValues).forEach((key) => delete lastEmittedValues[key]);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
describe("onEvent", () => {
|
|
15
|
+
it("registers a single callback", () => {
|
|
16
|
+
const callback = vi.fn();
|
|
17
|
+
|
|
18
|
+
onEvent("test-event", callback);
|
|
19
|
+
|
|
20
|
+
expect(sharedEvents["test-event"]).toEqual([callback]);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("registers an array of callbacks", () => {
|
|
24
|
+
const callback1 = vi.fn();
|
|
25
|
+
const callback2 = vi.fn();
|
|
26
|
+
|
|
27
|
+
onEvent("test-event", [callback1, callback2]);
|
|
28
|
+
|
|
29
|
+
expect(sharedEvents["test-event"]).toEqual([callback1, callback2]);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe("emitEvent", () => {
|
|
34
|
+
it("invokes all callbacks with provided args", () => {
|
|
35
|
+
const callback1 = vi.fn();
|
|
36
|
+
const callback2 = vi.fn();
|
|
37
|
+
|
|
38
|
+
onEvent("test-event", [callback1, callback2]);
|
|
39
|
+
emitEvent("test-event", "data", 1);
|
|
40
|
+
|
|
41
|
+
expect(callback1).toHaveBeenCalledWith("data", 1);
|
|
42
|
+
expect(callback2).toHaveBeenCalledWith("data", 1);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("does nothing when event has no callbacks", () => {
|
|
46
|
+
expect(() => emitEvent("missing-event", "data")).not.toThrow();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe("memo", () => {
|
|
50
|
+
it("invokes all callback and stores the value", () => {
|
|
51
|
+
const callback = vi.fn();
|
|
52
|
+
onEvent("memo-event", callback);
|
|
53
|
+
|
|
54
|
+
emitEvent.memo("memo-event", "memo-data");
|
|
55
|
+
|
|
56
|
+
expect(callback).toHaveBeenCalledWith("memo-data");
|
|
57
|
+
expect(lastEmittedValues["memo-event"]).toEqual(["memo-data"]);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("triggers callback immediately if event was already memoized", () => {
|
|
61
|
+
const callback = vi.fn();
|
|
62
|
+
|
|
63
|
+
emitEvent.memo("memo-event", "old-data");
|
|
64
|
+
onEvent("memo-event", callback);
|
|
65
|
+
|
|
66
|
+
expect(callback).toHaveBeenCalledWith("old-data");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("clears memoized value when emitEvent.memo is called with undefined", () => {
|
|
70
|
+
const callback = vi.fn();
|
|
71
|
+
onEvent("memo-event", callback);
|
|
72
|
+
|
|
73
|
+
emitEvent.memo("memo-event", "some-data");
|
|
74
|
+
expect(lastEmittedValues["memo-event"]).toEqual(["some-data"]);
|
|
75
|
+
expect(callback).toHaveBeenCalledWith("some-data");
|
|
76
|
+
|
|
77
|
+
emitEvent.memo("memo-event", undefined);
|
|
78
|
+
expect(lastEmittedValues["memo-event"]).toBeUndefined();
|
|
79
|
+
expect(callback).toHaveBeenCalledWith(undefined);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("should not trigger callback immediately when last emitted value is undefined", () => {
|
|
83
|
+
const callback = vi.fn();
|
|
84
|
+
const callback2 = vi.fn();
|
|
85
|
+
|
|
86
|
+
emitEvent.memo("memo-event", "some-data");
|
|
87
|
+
onEvent("memo-event", callback);
|
|
88
|
+
expect(callback).toHaveBeenCalledWith("some-data");
|
|
89
|
+
|
|
90
|
+
emitEvent.memo("memo-event", undefined);
|
|
91
|
+
onEvent("memo-event", callback2);
|
|
92
|
+
expect(callback).toHaveBeenCalledWith(undefined);
|
|
93
|
+
expect(callback2).not.toHaveBeenCalled();
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
describe("clearMemo", () => {
|
|
99
|
+
it("removes the memoized value for a given event", () => {
|
|
100
|
+
emitEvent.memo("test-event", "data");
|
|
101
|
+
expect(lastEmittedValues["test-event"]).toEqual(["data"]);
|
|
102
|
+
|
|
103
|
+
clearMemo("test-event");
|
|
104
|
+
expect(lastEmittedValues["test-event"]).toBeUndefined();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("does not trigger callbacks", () => {
|
|
108
|
+
const callback = vi.fn();
|
|
109
|
+
onEvent("test-event", callback);
|
|
110
|
+
emitEvent.memo("test-event", "data");
|
|
111
|
+
callback.mockClear();
|
|
112
|
+
|
|
113
|
+
clearMemo("test-event");
|
|
114
|
+
expect(callback).not.toHaveBeenCalled();
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe("removeEvent", () => {
|
|
119
|
+
it("removes a single callback", () => {
|
|
120
|
+
const callback = vi.fn();
|
|
121
|
+
|
|
122
|
+
onEvent("test-event", callback);
|
|
123
|
+
removeEvent("test-event", callback);
|
|
124
|
+
emitEvent("test-event", "data");
|
|
125
|
+
|
|
126
|
+
expect(callback).not.toHaveBeenCalled();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("removes multiple callbacks from array input", () => {
|
|
130
|
+
const callback1 = vi.fn();
|
|
131
|
+
const callback2 = vi.fn();
|
|
132
|
+
|
|
133
|
+
onEvent("test-event", [callback1, callback2]);
|
|
134
|
+
removeEvent("test-event", [callback1, callback2]);
|
|
135
|
+
emitEvent("test-event", "data");
|
|
136
|
+
|
|
137
|
+
expect(callback1).not.toHaveBeenCalled();
|
|
138
|
+
expect(callback2).not.toHaveBeenCalled();
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("does nothing when event has no callbacks", () => {
|
|
142
|
+
const callback = vi.fn();
|
|
143
|
+
|
|
144
|
+
expect(() => removeEvent("missing-event", callback)).not.toThrow();
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
});
|
package/src/actions/index.ts
CHANGED
package/src/actions/on-event.ts
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
import { sharedEvents } from "../shared-events";
|
|
1
|
+
import { lastEmittedValues, sharedEvents } from "../shared-events";
|
|
2
2
|
import { getAsArray } from "../utils";
|
|
3
3
|
import { EventCallbackType, EventNameType } from "../events.model";
|
|
4
4
|
|
|
5
5
|
export function onEvent(event: EventNameType, callback: EventCallbackType[] | EventCallbackType) {
|
|
6
6
|
const callbacks = sharedEvents[event] ?? [];
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
const newCallbacks = getAsArray(callback)
|
|
9
|
+
callbacks.push(...newCallbacks);
|
|
9
10
|
|
|
10
11
|
sharedEvents[event] = callbacks;
|
|
12
|
+
|
|
13
|
+
if (lastEmittedValues[event]) {
|
|
14
|
+
newCallbacks.forEach(callback => callback(...lastEmittedValues[event]));
|
|
15
|
+
}
|
|
11
16
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { renderHook, act } from '@testing-library/react';
|
|
2
2
|
import { useEvents } from './useEvents';
|
|
3
3
|
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
4
|
-
import { sharedEvents } from '../shared-events';
|
|
4
|
+
import { lastEmittedValues, sharedEvents } from '../shared-events';
|
|
5
5
|
|
|
6
6
|
describe('useEvents', () => {
|
|
7
7
|
beforeEach(() => {
|
|
8
8
|
Object.keys(sharedEvents).forEach(key => delete sharedEvents[key]);
|
|
9
|
+
Object.keys(lastEmittedValues).forEach(key => delete lastEmittedValues[key]);
|
|
9
10
|
});
|
|
10
11
|
|
|
11
12
|
it('should register and emit events', () => {
|
|
@@ -57,4 +58,38 @@ describe('useEvents', () => {
|
|
|
57
58
|
expect(callback).toHaveBeenCalledWith('init-data');
|
|
58
59
|
expect(callbackSymbol).toHaveBeenCalled()
|
|
59
60
|
});
|
|
61
|
+
|
|
62
|
+
it('should support memoized events', () => {
|
|
63
|
+
const { result } = renderHook(() => useEvents());
|
|
64
|
+
const callback = vi.fn();
|
|
65
|
+
|
|
66
|
+
act(() => {
|
|
67
|
+
result.current.emitEvent.memo('memo-event', 'memo-data');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
act(() => {
|
|
71
|
+
result.current.onEvent('memo-event', callback);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
expect(callback).toHaveBeenCalledWith('memo-data');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should clear memoized events', () => {
|
|
78
|
+
const { result } = renderHook(() => useEvents());
|
|
79
|
+
const callback = vi.fn();
|
|
80
|
+
|
|
81
|
+
act(() => {
|
|
82
|
+
result.current.emitEvent.memo('memo-event', 'memo-data');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
act(() => {
|
|
86
|
+
result.current.clearMemo('memo-event');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
act(() => {
|
|
90
|
+
result.current.onEvent('memo-event', callback);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
expect(callback).not.toHaveBeenCalled();
|
|
94
|
+
});
|
|
60
95
|
});
|
package/src/hooks/useEvents.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect } from "react";
|
|
2
2
|
import { InitEventsType } from "../events.model";
|
|
3
|
-
import { emitEvent, onEvent, removeEvent } from "../actions";
|
|
3
|
+
import { emitEvent, onEvent, removeEvent, clearMemo } from "../actions";
|
|
4
4
|
|
|
5
5
|
export const useEvents = (eventMap: InitEventsType = {}) => {
|
|
6
6
|
useEffect(() => {
|
|
@@ -20,5 +20,5 @@ export const useEvents = (eventMap: InitEventsType = {}) => {
|
|
|
20
20
|
}
|
|
21
21
|
}, [eventMap])
|
|
22
22
|
|
|
23
|
-
return { onEvent, emitEvent, removeEvent };
|
|
23
|
+
return { onEvent, emitEvent, removeEvent, clearMemo };
|
|
24
24
|
};
|