Skip to content

Commit

Permalink
Add generic way to use channel state with the help of useSyncExternal…
Browse files Browse the repository at this point in the history
…Store hook
  • Loading branch information
arnautov-anton committed Jan 22, 2025
1 parent f80ef02 commit ce5fba1
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 44 deletions.
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@
"textarea-caret": "^3.1.0",
"tslib": "^2.6.2",
"unist-builder": "^3.0.0",
"unist-util-visit": "^5.0.0"
"unist-util-visit": "^5.0.0",
"use-sync-external-store": "^1.4.0"
},
"optionalDependencies": {
"@stream-io/transliterate": "^1.5.5",
Expand Down Expand Up @@ -207,6 +208,7 @@
"@types/react-image-gallery": "^1.2.4",
"@types/react-is": "^18.2.4",
"@types/textarea-caret": "3.0.0",
"@types/use-sync-external-store": "^0.0.6",
"@types/uuid": "^8.3.0",
"@typescript-eslint/eslint-plugin": "5.62.0",
"@typescript-eslint/parser": "5.62.0",
Expand Down Expand Up @@ -257,7 +259,7 @@
"react-dom": "^18.1.0",
"react-test-renderer": "^18.1.0",
"semantic-release": "^19.0.5",
"stream-chat": "^8.50.0",
"stream-chat": "link:../stream-chat-js/",
"ts-jest": "^29.1.4",
"typescript": "^5.4.5"
},
Expand Down
43 changes: 15 additions & 28 deletions src/components/ChannelList/hooks/useChannelMembershipState.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,15 @@
import { useEffect, useState } from 'react';
import type { Channel, ChannelState, ExtendableGenerics } from 'stream-chat';

import { useChatContext } from '../../../context';

export const useChannelMembershipState = <SCG extends ExtendableGenerics>(
channel?: Channel<SCG>,
) => {
const [membership, setMembership] = useState<ChannelState<SCG>['membership']>(
channel?.state.membership || {},
);

const { client } = useChatContext<SCG>();

useEffect(() => {
if (!channel) return;

const subscriptions = ['member.updated'].map((v) =>
client.on(v, () => {
setMembership(channel.state.membership);
}),
);

return () => subscriptions.forEach((subscription) => subscription.unsubscribe());
}, [client, channel]);

return membership;
};
import type { Channel, ChannelMemberResponse, EventTypes, ExtendableGenerics } from 'stream-chat';
import { useSelectedChannelState } from './useSelectedChannelState';

const selector = <SCG extends ExtendableGenerics>(c: Channel<SCG>) => c.state.membership;
const keys: EventTypes[] = ['member.updated'];

export function useChannelMembershipState<SCG extends ExtendableGenerics>(
channel: Channel<SCG>,
): ChannelMemberResponse<SCG>;
export function useChannelMembershipState<SCG extends ExtendableGenerics>(
channel?: Channel<SCG> | undefined,
): ChannelMemberResponse<SCG> | undefined;
export function useChannelMembershipState<SCG extends ExtendableGenerics>(channel?: Channel<SCG>) {
return useSelectedChannelState({ channel, selector, stateChangeEventKeys: keys });
}
49 changes: 49 additions & 0 deletions src/components/ChannelList/hooks/useSelectedChannelState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { useCallback } from 'react';
import { useSyncExternalStore } from 'use-sync-external-store/shim';
import type { Channel, EventTypes, ExtendableGenerics } from 'stream-chat';

// eslint-disable-next-line @typescript-eslint/no-empty-function
const noop = () => {};

export function useSelectedChannelState<SCG extends ExtendableGenerics, O>(_: {
channel: Channel<SCG>;
selector: (channel: Channel<SCG>) => O;
stateChangeEventKeys?: EventTypes[];
}): O;
export function useSelectedChannelState<SCG extends ExtendableGenerics, O>(_: {
selector: (channel: Channel<SCG>) => O;
channel?: Channel<SCG> | undefined;
stateChangeEventKeys?: EventTypes[];
}): O | undefined;
export function useSelectedChannelState<SCG extends ExtendableGenerics, O>({
channel,
stateChangeEventKeys = ['all'],
selector,
}: {
selector: (channel: Channel<SCG>) => O;
channel?: Channel<SCG>;
stateChangeEventKeys?: EventTypes[];
}): O | undefined {
const subscribe = useCallback(
(onStoreChange: (value: O) => void) => {
if (!channel) return noop;

const subscriptions = stateChangeEventKeys.map((et) =>
channel.on(et, () => {
onStoreChange(selector(channel));
}),
);

return () => subscriptions.forEach((subscription) => subscription.unsubscribe());
},
[channel, selector, stateChangeEventKeys],
);

const getSnapshot = useCallback(() => {
if (!channel) return undefined;

return selector(channel);
}, [channel, selector]);

return useSyncExternalStore(subscribe, getSnapshot);
}
27 changes: 13 additions & 14 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2769,6 +2769,11 @@
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d"
integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==

"@types/use-sync-external-store@^0.0.6":
version "0.0.6"
resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz#60be8d21baab8c305132eb9cb912ed497852aadc"
integrity sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==

"@types/uuid@^8.3.0":
version "8.3.0"
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.0.tgz#215c231dff736d5ba92410e6d602050cce7e273f"
Expand Down Expand Up @@ -12231,20 +12236,9 @@ [email protected]:
resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==

stream-chat@^8.50.0:
version "8.50.0"
resolved "https://registry.yarnpkg.com/stream-chat/-/stream-chat-8.50.0.tgz#ae9bd40da3d38a0302d62d0165b2b8f45d500ae2"
integrity sha512-n7iAp0QTpZmRbygKFdwKlJy4QhhWep+6mQ+ctn2OjBEkrhtKITc4fIaMcQT4lOc7om0K9YTskMRvdWa2ZBvEWg==
dependencies:
"@babel/runtime" "^7.16.3"
"@types/jsonwebtoken" "~9.0.0"
"@types/ws" "^7.4.0"
axios "^1.6.0"
base64-js "^1.5.1"
form-data "^4.0.0"
isomorphic-ws "^4.0.1"
jsonwebtoken "~9.0.0"
ws "^7.5.10"
"stream-chat@link:../stream-chat-js":
version "0.0.0"
uid ""

stream-combiner2@~1.1.1:
version "1.1.1"
Expand Down Expand Up @@ -13197,6 +13191,11 @@ use-latest@^1.0.0:
dependencies:
use-isomorphic-layout-effect "^1.0.0"

use-sync-external-store@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz#adbc795d8eeb47029963016cefdf89dc799fcebc"
integrity sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==

use@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
Expand Down

0 comments on commit ce5fba1

Please sign in to comment.