Skip to content

Commit ce5fba1

Browse files
Add generic way to use channel state with the help of useSyncExternalStore hook
1 parent f80ef02 commit ce5fba1

File tree

4 files changed

+81
-44
lines changed

4 files changed

+81
-44
lines changed

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@
132132
"textarea-caret": "^3.1.0",
133133
"tslib": "^2.6.2",
134134
"unist-builder": "^3.0.0",
135-
"unist-util-visit": "^5.0.0"
135+
"unist-util-visit": "^5.0.0",
136+
"use-sync-external-store": "^1.4.0"
136137
},
137138
"optionalDependencies": {
138139
"@stream-io/transliterate": "^1.5.5",
@@ -207,6 +208,7 @@
207208
"@types/react-image-gallery": "^1.2.4",
208209
"@types/react-is": "^18.2.4",
209210
"@types/textarea-caret": "3.0.0",
211+
"@types/use-sync-external-store": "^0.0.6",
210212
"@types/uuid": "^8.3.0",
211213
"@typescript-eslint/eslint-plugin": "5.62.0",
212214
"@typescript-eslint/parser": "5.62.0",
@@ -257,7 +259,7 @@
257259
"react-dom": "^18.1.0",
258260
"react-test-renderer": "^18.1.0",
259261
"semantic-release": "^19.0.5",
260-
"stream-chat": "^8.50.0",
262+
"stream-chat": "link:../stream-chat-js/",
261263
"ts-jest": "^29.1.4",
262264
"typescript": "^5.4.5"
263265
},
Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,15 @@
1-
import { useEffect, useState } from 'react';
2-
import type { Channel, ChannelState, ExtendableGenerics } from 'stream-chat';
3-
4-
import { useChatContext } from '../../../context';
5-
6-
export const useChannelMembershipState = <SCG extends ExtendableGenerics>(
7-
channel?: Channel<SCG>,
8-
) => {
9-
const [membership, setMembership] = useState<ChannelState<SCG>['membership']>(
10-
channel?.state.membership || {},
11-
);
12-
13-
const { client } = useChatContext<SCG>();
14-
15-
useEffect(() => {
16-
if (!channel) return;
17-
18-
const subscriptions = ['member.updated'].map((v) =>
19-
client.on(v, () => {
20-
setMembership(channel.state.membership);
21-
}),
22-
);
23-
24-
return () => subscriptions.forEach((subscription) => subscription.unsubscribe());
25-
}, [client, channel]);
26-
27-
return membership;
28-
};
1+
import type { Channel, ChannelMemberResponse, EventTypes, ExtendableGenerics } from 'stream-chat';
2+
import { useSelectedChannelState } from './useSelectedChannelState';
3+
4+
const selector = <SCG extends ExtendableGenerics>(c: Channel<SCG>) => c.state.membership;
5+
const keys: EventTypes[] = ['member.updated'];
6+
7+
export function useChannelMembershipState<SCG extends ExtendableGenerics>(
8+
channel: Channel<SCG>,
9+
): ChannelMemberResponse<SCG>;
10+
export function useChannelMembershipState<SCG extends ExtendableGenerics>(
11+
channel?: Channel<SCG> | undefined,
12+
): ChannelMemberResponse<SCG> | undefined;
13+
export function useChannelMembershipState<SCG extends ExtendableGenerics>(channel?: Channel<SCG>) {
14+
return useSelectedChannelState({ channel, selector, stateChangeEventKeys: keys });
15+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { useCallback } from 'react';
2+
import { useSyncExternalStore } from 'use-sync-external-store/shim';
3+
import type { Channel, EventTypes, ExtendableGenerics } from 'stream-chat';
4+
5+
// eslint-disable-next-line @typescript-eslint/no-empty-function
6+
const noop = () => {};
7+
8+
export function useSelectedChannelState<SCG extends ExtendableGenerics, O>(_: {
9+
channel: Channel<SCG>;
10+
selector: (channel: Channel<SCG>) => O;
11+
stateChangeEventKeys?: EventTypes[];
12+
}): O;
13+
export function useSelectedChannelState<SCG extends ExtendableGenerics, O>(_: {
14+
selector: (channel: Channel<SCG>) => O;
15+
channel?: Channel<SCG> | undefined;
16+
stateChangeEventKeys?: EventTypes[];
17+
}): O | undefined;
18+
export function useSelectedChannelState<SCG extends ExtendableGenerics, O>({
19+
channel,
20+
stateChangeEventKeys = ['all'],
21+
selector,
22+
}: {
23+
selector: (channel: Channel<SCG>) => O;
24+
channel?: Channel<SCG>;
25+
stateChangeEventKeys?: EventTypes[];
26+
}): O | undefined {
27+
const subscribe = useCallback(
28+
(onStoreChange: (value: O) => void) => {
29+
if (!channel) return noop;
30+
31+
const subscriptions = stateChangeEventKeys.map((et) =>
32+
channel.on(et, () => {
33+
onStoreChange(selector(channel));
34+
}),
35+
);
36+
37+
return () => subscriptions.forEach((subscription) => subscription.unsubscribe());
38+
},
39+
[channel, selector, stateChangeEventKeys],
40+
);
41+
42+
const getSnapshot = useCallback(() => {
43+
if (!channel) return undefined;
44+
45+
return selector(channel);
46+
}, [channel, selector]);
47+
48+
return useSyncExternalStore(subscribe, getSnapshot);
49+
}

yarn.lock

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2769,6 +2769,11 @@
27692769
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d"
27702770
integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==
27712771

2772+
"@types/use-sync-external-store@^0.0.6":
2773+
version "0.0.6"
2774+
resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz#60be8d21baab8c305132eb9cb912ed497852aadc"
2775+
integrity sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==
2776+
27722777
"@types/uuid@^8.3.0":
27732778
version "8.3.0"
27742779
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.0.tgz#215c231dff736d5ba92410e6d602050cce7e273f"
@@ -12231,20 +12236,9 @@ [email protected]:
1223112236
resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
1223212237
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
1223312238

12234-
stream-chat@^8.50.0:
12235-
version "8.50.0"
12236-
resolved "https://registry.yarnpkg.com/stream-chat/-/stream-chat-8.50.0.tgz#ae9bd40da3d38a0302d62d0165b2b8f45d500ae2"
12237-
integrity sha512-n7iAp0QTpZmRbygKFdwKlJy4QhhWep+6mQ+ctn2OjBEkrhtKITc4fIaMcQT4lOc7om0K9YTskMRvdWa2ZBvEWg==
12238-
dependencies:
12239-
"@babel/runtime" "^7.16.3"
12240-
"@types/jsonwebtoken" "~9.0.0"
12241-
"@types/ws" "^7.4.0"
12242-
axios "^1.6.0"
12243-
base64-js "^1.5.1"
12244-
form-data "^4.0.0"
12245-
isomorphic-ws "^4.0.1"
12246-
jsonwebtoken "~9.0.0"
12247-
ws "^7.5.10"
12239+
"stream-chat@link:../stream-chat-js":
12240+
version "0.0.0"
12241+
uid ""
1224812242

1224912243
stream-combiner2@~1.1.1:
1225012244
version "1.1.1"
@@ -13197,6 +13191,11 @@ use-latest@^1.0.0:
1319713191
dependencies:
1319813192
use-isomorphic-layout-effect "^1.0.0"
1319913193

13194+
use-sync-external-store@^1.4.0:
13195+
version "1.4.0"
13196+
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz#adbc795d8eeb47029963016cefdf89dc799fcebc"
13197+
integrity sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==
13198+
1320013199
use@^3.1.0:
1320113200
version "3.1.1"
1320213201
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"

0 commit comments

Comments
 (0)