@knocklabs/react-core 0.6.10 → 0.6.12
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/CHANGELOG.md +16 -0
- package/dist/cjs/modules/feed/hooks/useNotifications.js +1 -1
- package/dist/cjs/modules/feed/hooks/useNotifications.js.map +1 -1
- package/dist/esm/modules/feed/hooks/useNotifications.mjs +27 -9
- package/dist/esm/modules/feed/hooks/useNotifications.mjs.map +1 -1
- package/dist/types/modules/feed/hooks/useNotifications.d.ts.map +1 -1
- package/package.json +3 -5
- package/src/modules/feed/hooks/useNotifications.ts +52 -19
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.12
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [4e73f12]
|
|
8
|
+
- @knocklabs/client@0.14.9
|
|
9
|
+
|
|
10
|
+
## 0.6.11
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- dbbbaf7: Dispose of feed on unmount in `useNotifications` hook
|
|
15
|
+
|
|
16
|
+
Previously, the `useNotifications` hook did not clean up old instances of `Feed`
|
|
17
|
+
on unmount. This has been fixed.
|
|
18
|
+
|
|
3
19
|
## 0.6.10
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";const
|
|
1
|
+
"use strict";const n=require("react");require("../../core/context/KnockProvider.js");require("@knocklabs/client");const p=require("../../core/hooks/useStableOptions.js");require("date-fns");function b(c,s,l={}){const r=n.useCallback((u,f)=>{const i=c.feeds.initialize(u,f);return i.store.subscribe(d=>i.store.setState(d)),i.listenForUpdates(),i},[c]),e=p(l),[t,a]=n.useState(()=>({feedClient:r(s,e),options:e})),o=n.useRef(!1);return n.useEffect(()=>{const u=o.current;if(t.feedClient.feedId!==s||t.options!==e||u){o.current=!1,a({feedClient:r(s,e),options:e});return}return()=>{o.current=!0,t.feedClient.dispose()}},[r,s,e,t]),t.feedClient}module.exports=b;
|
|
2
2
|
//# sourceMappingURL=useNotifications.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useNotifications.js","sources":["../../../../../src/modules/feed/hooks/useNotifications.ts"],"sourcesContent":["import Knock, { Feed, FeedClientOptions } from \"@knocklabs/client\";\nimport {
|
|
1
|
+
{"version":3,"file":"useNotifications.js","sources":["../../../../../src/modules/feed/hooks/useNotifications.ts"],"sourcesContent":["import Knock, { Feed, FeedClientOptions } from \"@knocklabs/client\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { useStableOptions } from \"../../core\";\n\ninterface State {\n feedClient: Feed;\n options: FeedClientOptions;\n}\n\nfunction useNotifications(\n knock: Knock,\n feedChannelId: string,\n options: FeedClientOptions = {},\n): Feed {\n const initFeedClient = useCallback(\n (feedChannelId: string, options: FeedClientOptions) => {\n const feedClient = knock.feeds.initialize(feedChannelId, options);\n\n // In development, we need to introduce this extra set state to force a render\n // for Zustand as otherwise the state doesn't get reflected correctly\n feedClient.store.subscribe((t) => feedClient.store.setState(t));\n\n feedClient.listenForUpdates();\n\n return feedClient;\n },\n [knock],\n );\n\n const stableOptions = useStableOptions(options);\n const [state, setState] = useState<State>(() => ({\n // FIXME In strict mode, useState initializer functions are called twice,\n // which results in one extra instance of the feed client being created\n // and not disposed of. This only affects strict mode.\n feedClient: initFeedClient(feedChannelId, stableOptions),\n options: stableOptions,\n }));\n const disposedRef = useRef(false);\n\n useEffect(() => {\n const isDisposed = disposedRef.current;\n\n // Initialize a new feed client if the feed ID has changed,\n // options have changed, or the current feed client has been disposed\n const needsReinit =\n state.feedClient.feedId !== feedChannelId ||\n state.options !== stableOptions ||\n isDisposed;\n\n if (needsReinit) {\n disposedRef.current = false;\n setState({\n feedClient: initFeedClient(feedChannelId, stableOptions),\n options: stableOptions,\n });\n return;\n }\n\n return () => {\n disposedRef.current = true;\n state.feedClient.dispose();\n };\n }, [initFeedClient, feedChannelId, stableOptions, state]);\n\n return state.feedClient;\n}\n\nexport default useNotifications;\n"],"names":["useNotifications","knock","feedChannelId","options","initFeedClient","useCallback","feedClient","feeds","initialize","store","subscribe","t","setState","listenForUpdates","stableOptions","useStableOptions","state","useState","disposedRef","useRef","useEffect","isDisposed","current","feedId","dispose"],"mappings":"8LAUA,SAASA,EACPC,EACAC,EACAC,EAA6B,CAAA,EACvB,CACN,MAAMC,EAAiBC,EAAAA,YACrB,CAACH,EAAuBC,IAA+B,CACrD,MAAMG,EAAaL,EAAMM,MAAMC,WAAWN,EAAeC,CAAO,EAIhEG,OAAAA,EAAWG,MAAMC,UAAWC,GAAML,EAAWG,MAAMG,SAASD,CAAC,CAAC,EAE9DL,EAAWO,iBAAiB,EAErBP,CAAAA,EAET,CAACL,CAAK,CACR,EAEMa,EAAgBC,EAAiBZ,CAAO,EACxC,CAACa,EAAOJ,CAAQ,EAAIK,WAAgB,KAAO,CAI/CX,WAAYF,EAAeF,EAAeY,CAAa,EACvDX,QAASW,CAAAA,EACT,EACII,EAAcC,SAAO,EAAK,EAEhCC,OAAAA,EAAAA,UAAU,IAAM,CACd,MAAMC,EAAaH,EAAYI,QAS/B,GAJEN,EAAMV,WAAWiB,SAAWrB,GAC5Bc,EAAMb,UAAYW,GAClBO,EAEe,CACfH,EAAYI,QAAU,GACbV,EAAA,CACPN,WAAYF,EAAeF,EAAeY,CAAa,EACvDX,QAASW,CAAAA,CACV,EACD,MAAA,CAGF,MAAO,IAAM,CACXI,EAAYI,QAAU,GACtBN,EAAMV,WAAWkB,QAAQ,CAC3B,GACC,CAACpB,EAAgBF,EAAeY,EAAeE,CAAK,CAAC,EAEjDA,EAAMV,UACf"}
|
|
@@ -1,16 +1,34 @@
|
|
|
1
|
-
import { useRef as
|
|
1
|
+
import { useCallback as a, useState as d, useRef as m, useEffect as b } from "react";
|
|
2
2
|
import "../../core/context/KnockProvider.mjs";
|
|
3
3
|
import "@knocklabs/client";
|
|
4
|
-
import
|
|
4
|
+
import C from "../../core/hooks/useStableOptions.mjs";
|
|
5
5
|
import "date-fns";
|
|
6
|
-
function
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
function z(f, s, p = {}) {
|
|
7
|
+
const o = a((r, u) => {
|
|
8
|
+
const i = f.feeds.initialize(r, u);
|
|
9
|
+
return i.store.subscribe((l) => i.store.setState(l)), i.listenForUpdates(), i;
|
|
10
|
+
}, [f]), e = C(p), [t, c] = d(() => ({
|
|
11
|
+
// FIXME In strict mode, useState initializer functions are called twice,
|
|
12
|
+
// which results in one extra instance of the feed client being created
|
|
13
|
+
// and not disposed of. This only affects strict mode.
|
|
14
|
+
feedClient: o(s, e),
|
|
15
|
+
options: e
|
|
16
|
+
})), n = m(!1);
|
|
17
|
+
return b(() => {
|
|
18
|
+
const r = n.current;
|
|
19
|
+
if (t.feedClient.feedId !== s || t.options !== e || r) {
|
|
20
|
+
n.current = !1, c({
|
|
21
|
+
feedClient: o(s, e),
|
|
22
|
+
options: e
|
|
23
|
+
});
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
return () => {
|
|
27
|
+
n.current = !0, t.feedClient.dispose();
|
|
28
|
+
};
|
|
29
|
+
}, [o, s, e, t]), t.feedClient;
|
|
12
30
|
}
|
|
13
31
|
export {
|
|
14
|
-
|
|
32
|
+
z as default
|
|
15
33
|
};
|
|
16
34
|
//# sourceMappingURL=useNotifications.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useNotifications.mjs","sources":["../../../../../src/modules/feed/hooks/useNotifications.ts"],"sourcesContent":["import Knock, { Feed, FeedClientOptions } from \"@knocklabs/client\";\nimport {
|
|
1
|
+
{"version":3,"file":"useNotifications.mjs","sources":["../../../../../src/modules/feed/hooks/useNotifications.ts"],"sourcesContent":["import Knock, { Feed, FeedClientOptions } from \"@knocklabs/client\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { useStableOptions } from \"../../core\";\n\ninterface State {\n feedClient: Feed;\n options: FeedClientOptions;\n}\n\nfunction useNotifications(\n knock: Knock,\n feedChannelId: string,\n options: FeedClientOptions = {},\n): Feed {\n const initFeedClient = useCallback(\n (feedChannelId: string, options: FeedClientOptions) => {\n const feedClient = knock.feeds.initialize(feedChannelId, options);\n\n // In development, we need to introduce this extra set state to force a render\n // for Zustand as otherwise the state doesn't get reflected correctly\n feedClient.store.subscribe((t) => feedClient.store.setState(t));\n\n feedClient.listenForUpdates();\n\n return feedClient;\n },\n [knock],\n );\n\n const stableOptions = useStableOptions(options);\n const [state, setState] = useState<State>(() => ({\n // FIXME In strict mode, useState initializer functions are called twice,\n // which results in one extra instance of the feed client being created\n // and not disposed of. This only affects strict mode.\n feedClient: initFeedClient(feedChannelId, stableOptions),\n options: stableOptions,\n }));\n const disposedRef = useRef(false);\n\n useEffect(() => {\n const isDisposed = disposedRef.current;\n\n // Initialize a new feed client if the feed ID has changed,\n // options have changed, or the current feed client has been disposed\n const needsReinit =\n state.feedClient.feedId !== feedChannelId ||\n state.options !== stableOptions ||\n isDisposed;\n\n if (needsReinit) {\n disposedRef.current = false;\n setState({\n feedClient: initFeedClient(feedChannelId, stableOptions),\n options: stableOptions,\n });\n return;\n }\n\n return () => {\n disposedRef.current = true;\n state.feedClient.dispose();\n };\n }, [initFeedClient, feedChannelId, stableOptions, state]);\n\n return state.feedClient;\n}\n\nexport default useNotifications;\n"],"names":["useNotifications","knock","feedChannelId","options","initFeedClient","useCallback","feedClient","feeds","initialize","store","subscribe","t","setState","listenForUpdates","stableOptions","useStableOptions","state","useState","disposedRef","useRef","useEffect","isDisposed","current","feedId","dispose"],"mappings":";;;;;AAUA,SAASA,EACPC,GACAC,GACAC,IAA6B,CAAA,GACvB;AACN,QAAMC,IAAiBC,EACrB,CAACH,GAAuBC,MAA+B;AACrD,UAAMG,IAAaL,EAAMM,MAAMC,WAAWN,GAAeC,CAAO;AAIhEG,WAAAA,EAAWG,MAAMC,UAAWC,CAAAA,MAAML,EAAWG,MAAMG,SAASD,CAAC,CAAC,GAE9DL,EAAWO,iBAAiB,GAErBP;AAAAA,EAAAA,GAET,CAACL,CAAK,CACR,GAEMa,IAAgBC,EAAiBZ,CAAO,GACxC,CAACa,GAAOJ,CAAQ,IAAIK,EAAgB,OAAO;AAAA;AAAA;AAAA;AAAA,IAI/CX,YAAYF,EAAeF,GAAeY,CAAa;AAAA,IACvDX,SAASW;AAAAA,EAAAA,EACT,GACII,IAAcC,EAAO,EAAK;AAEhCC,SAAAA,EAAU,MAAM;AACd,UAAMC,IAAaH,EAAYI;AAS/B,QAJEN,EAAMV,WAAWiB,WAAWrB,KAC5Bc,EAAMb,YAAYW,KAClBO,GAEe;AACfH,MAAAA,EAAYI,UAAU,IACbV,EAAA;AAAA,QACPN,YAAYF,EAAeF,GAAeY,CAAa;AAAA,QACvDX,SAASW;AAAAA,MAAAA,CACV;AACD;AAAA,IAAA;AAGF,WAAO,MAAM;AACXI,MAAAA,EAAYI,UAAU,IACtBN,EAAMV,WAAWkB,QAAQ;AAAA,IAC3B;AAAA,KACC,CAACpB,GAAgBF,GAAeY,GAAeE,CAAK,CAAC,GAEjDA,EAAMV;AACf;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useNotifications.d.ts","sourceRoot":"","sources":["../../../../../src/modules/feed/hooks/useNotifications.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"useNotifications.d.ts","sourceRoot":"","sources":["../../../../../src/modules/feed/hooks/useNotifications.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAUnE,iBAAS,gBAAgB,CACvB,KAAK,EAAE,KAAK,EACZ,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE,iBAAsB,GAC9B,IAAI,CAoDN;AAED,eAAe,gBAAgB,CAAC"}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@knocklabs/react-core",
|
|
3
3
|
"description": "A set of React components to build notification experiences powered by Knock",
|
|
4
4
|
"author": "@knocklabs",
|
|
5
|
-
"version": "0.6.
|
|
5
|
+
"version": "0.6.12",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"main": "dist/cjs/index.js",
|
|
8
8
|
"module": "dist/esm/index.mjs",
|
|
@@ -32,8 +32,6 @@
|
|
|
32
32
|
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
|
33
33
|
"format": "prettier \"src/**/*.{js,ts,tsx}\" --write",
|
|
34
34
|
"format:check": "prettier \"src/**/*.{js,ts,tsx}\" --check",
|
|
35
|
-
"test": "vitest run",
|
|
36
|
-
"test:watch": "vitest",
|
|
37
35
|
"type:check": "tsc --noEmit",
|
|
38
36
|
"coverage": "vitest run --coverage",
|
|
39
37
|
"preview": "vite preview"
|
|
@@ -49,7 +47,7 @@
|
|
|
49
47
|
"react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
|
50
48
|
},
|
|
51
49
|
"dependencies": {
|
|
52
|
-
"@knocklabs/client": "^0.14.
|
|
50
|
+
"@knocklabs/client": "^0.14.9",
|
|
53
51
|
"@tanstack/react-store": "0.6.1",
|
|
54
52
|
"date-fns": "^4.0.0",
|
|
55
53
|
"fast-deep-equal": "^3.1.3",
|
|
@@ -61,7 +59,7 @@
|
|
|
61
59
|
"@testing-library/react": "^16.3.0",
|
|
62
60
|
"@types/react": "^18.3.6",
|
|
63
61
|
"@typescript-eslint/eslint-plugin": "^8.32.0",
|
|
64
|
-
"@typescript-eslint/parser": "^8.
|
|
62
|
+
"@typescript-eslint/parser": "^8.32.1",
|
|
65
63
|
"@vitejs/plugin-react": "^4.4.1",
|
|
66
64
|
"babel-plugin-react-require": "^4.0.3",
|
|
67
65
|
"eslint": "^8.56.0",
|
|
@@ -1,36 +1,69 @@
|
|
|
1
1
|
import Knock, { Feed, FeedClientOptions } from "@knocklabs/client";
|
|
2
|
-
import {
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
3
|
|
|
4
4
|
import { useStableOptions } from "../../core";
|
|
5
5
|
|
|
6
|
+
interface State {
|
|
7
|
+
feedClient: Feed;
|
|
8
|
+
options: FeedClientOptions;
|
|
9
|
+
}
|
|
10
|
+
|
|
6
11
|
function useNotifications(
|
|
7
12
|
knock: Knock,
|
|
8
13
|
feedChannelId: string,
|
|
9
14
|
options: FeedClientOptions = {},
|
|
10
|
-
) {
|
|
11
|
-
const
|
|
15
|
+
): Feed {
|
|
16
|
+
const initFeedClient = useCallback(
|
|
17
|
+
(feedChannelId: string, options: FeedClientOptions) => {
|
|
18
|
+
const feedClient = knock.feeds.initialize(feedChannelId, options);
|
|
19
|
+
|
|
20
|
+
// In development, we need to introduce this extra set state to force a render
|
|
21
|
+
// for Zustand as otherwise the state doesn't get reflected correctly
|
|
22
|
+
feedClient.store.subscribe((t) => feedClient.store.setState(t));
|
|
23
|
+
|
|
24
|
+
feedClient.listenForUpdates();
|
|
25
|
+
|
|
26
|
+
return feedClient;
|
|
27
|
+
},
|
|
28
|
+
[knock],
|
|
29
|
+
);
|
|
30
|
+
|
|
12
31
|
const stableOptions = useStableOptions(options);
|
|
32
|
+
const [state, setState] = useState<State>(() => ({
|
|
33
|
+
// FIXME In strict mode, useState initializer functions are called twice,
|
|
34
|
+
// which results in one extra instance of the feed client being created
|
|
35
|
+
// and not disposed of. This only affects strict mode.
|
|
36
|
+
feedClient: initFeedClient(feedChannelId, stableOptions),
|
|
37
|
+
options: stableOptions,
|
|
38
|
+
}));
|
|
39
|
+
const disposedRef = useRef(false);
|
|
13
40
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
feedClientRef.current.dispose();
|
|
17
|
-
}
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
const isDisposed = disposedRef.current;
|
|
18
43
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
44
|
+
// Initialize a new feed client if the feed ID has changed,
|
|
45
|
+
// options have changed, or the current feed client has been disposed
|
|
46
|
+
const needsReinit =
|
|
47
|
+
state.feedClient.feedId !== feedChannelId ||
|
|
48
|
+
state.options !== stableOptions ||
|
|
49
|
+
isDisposed;
|
|
23
50
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
51
|
+
if (needsReinit) {
|
|
52
|
+
disposedRef.current = false;
|
|
53
|
+
setState({
|
|
54
|
+
feedClient: initFeedClient(feedChannelId, stableOptions),
|
|
55
|
+
options: stableOptions,
|
|
56
|
+
});
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
29
59
|
|
|
30
|
-
|
|
60
|
+
return () => {
|
|
61
|
+
disposedRef.current = true;
|
|
62
|
+
state.feedClient.dispose();
|
|
63
|
+
};
|
|
64
|
+
}, [initFeedClient, feedChannelId, stableOptions, state]);
|
|
31
65
|
|
|
32
|
-
|
|
33
|
-
}, [knock, feedChannelId, stableOptions]);
|
|
66
|
+
return state.feedClient;
|
|
34
67
|
}
|
|
35
68
|
|
|
36
69
|
export default useNotifications;
|