Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
stream-ci-bot committed Oct 28, 2024
1 parent 83f83c0 commit 2ccd27e
Showing 1 changed file with 28 additions and 26 deletions.
54 changes: 28 additions & 26 deletions docusaurus/docs/React/guides/sdk-state-management.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -152,45 +152,45 @@ Selectors are functions provided by integrators that run whenever state object c

#### Rules of Selectors

1. Selectors should return array of data sorted by their "change factor"; meaning values that change often should come first for the best performance.
1. Selectors should return a named object.

```ts
const selector = (nextValue: ThreadManagerState) => [
nextValue.unreadThreadsCount, // <-- changes often
nextValue.active, // <-- changes less often
nextvalue.lastConnectionDownAt, // <-- changes rarely
];
const selector = (nextValue: ThreadManagerState) => ({
unreadThreadsCount: nextValue.unreadThreadsCount,
active: nextValue.active,
lastConnectionDownAt: nextvalue.lastConnectionDownAt,
});
```

2. Selectors should live outside components scope or should be memoized if it requires "outside" information (`userId` for `read` object for example). Not memoizing selectors (or not stabilizing them) will lead to bad performance as each time your component re-renders, the selector function is created anew and `useSimpleStateStore` goes through unsubscribe and resubscribe process unnecessarily.
2. Selectors should live outside components scope or should be memoized if it requires "outside" information (`userId` for `read` object for example). Not memoizing selectors (or not stabilizing them) will lead to bad performance as each time your component re-renders, the selector function is created anew and `useStateStore` goes through unsubscribe and resubscribe process unnecessarily.

```tsx
// ❌ not okay
const Component1 = () => {
const [latestReply] = useThreadState((nextValue: ThreadState) => [
nextValue.latestReplies.at(-1),
]);
const { latestReply } = useThreadState((nextValue: ThreadState) => ({
latestReply: nextValue.latestReplies.at(-1),
}));

return <div>{latestReply.text}</div>;
};

// ✅ okay
const selector = (nextValue: ThreadState) => [nextValue.latestReplies.at(-1)];
const selector = (nextValue: ThreadState) => ({ latestReply: nextValue.latestReplies.at(-1) });

const Component2 = () => {
const [latestReply] = useThreadState(selector);
const { latestReply } = useThreadState(selector);

return <div>{latestReply.text}</div>;
};

// ✅ also okay
const Component3 = ({ userId }: { userId: string }) => {
const selector = useCallback(
(nextValue: ThreadState) => [nextValue.read[userId].unread_messages],
(nextValue: ThreadState) => ({ unreadMessagesCount: nextValue.read[userId].unread_messages }),
[userId],
);

const [unreadMessagesCount] = useThreadState(selector);
const { unreadMessagesCount } = useThreadState(selector);

return <div>{unreadMessagesCount}</div>;
};
Expand All @@ -215,9 +215,9 @@ client.threads.state.subscribe(console.log);
let latestThreads;
client.threads.state.subscribeWithSelector(
// called each time theres a change in the state object
(nextValue) => [nextValue.threads],
(nextValue) => ({ threads: nextValue.threads }),
// called only when threads change (selected value)
([threads]) => {
({ threads }) => {
latestThreads = threads;
},
);
Expand All @@ -233,19 +233,19 @@ thread?.state.subscribeWithSelector(/*...*/);
thread?.state.getLatestValue(/*...*/);
```
#### useSimpleStateStore Hook
#### useStateStore Hook
For the ease of use - the React SDK comes with the appropriate state acesss hook which wraps `SimpleStateStore.subscribeWithSelector` API for the React-based applications.
For the ease of use - the React SDK comes with the appropriate state acesss hook which wraps `StateStore.subscribeWithSelector` API for the React-based applications.
```tsx
import { useSimpleStateStore } from 'stream-chat-react';
import { useStateStore } from 'stream-chat-react';
import type { ThreadManagerState } from 'stream-chat';

const selector = (nextValue: ThreadManagerState) => [nextValue.threads] as const;
const selector = (nextValue: ThreadManagerState) => ({ threads: nextValue.threads });

const CustomThreadList = () => {
const { client } = useChatContext();
const [threads] = useSimpleStateStore(client.threads.state, selector);
const { threads } = useStateStore(client.threads.state, selector);

return (
<ul>
Expand All @@ -259,16 +259,18 @@ const CustomThreadList = () => {
#### useThreadState and useThreadManagerState
Both of these hooks use `useSimpleStateStore` under the hood but access their respective states through appropriate contexts; for `ThreadManagerState` it's `ChatContext` (accessing `client.threads.state`) and for `ThreadState` it's `ThreadListItemContext` first and `ThreadContext` second meaning that the former is prioritized. While these hooks make it sligthly easier for our integrators to reach reactive state
Both of these hooks use `useStateStore` under the hood but access their respective states through appropriate contexts; for `ThreadManagerState` it's `ChatContext` (accessing `client.threads.state`) and for `ThreadState` it's `ThreadListItemContext` first and `ThreadContext` second meaning that the former is prioritized. While these hooks make it sligthly easier for our integrators to reach reactive state
```ts
// memoized or living outside component's scope
const threadStateSelector = (nextValue: ThreadState) => [nextValue.replyCount] as const;
const threadManagerStateSelector = (nextValue: ThreadState) => [nextValue.threads.length] as const;
const threadStateSelector = (nextValue: ThreadState) => ({ replyCount: nextValue.replyCount });
const threadManagerStateSelector = (nextValue: ThreadState) => ({
threadsCount: nextValue.threads.length,
});

const MyComponent = () => {
const [replyCount] = useThreadState(threadStateSelector);
const [threadsCount] = useThreadManagerState(threadManagerStateSelector);
const { replyCount } = useThreadState(threadStateSelector);
const { threadsCount } = useThreadManagerState(threadManagerStateSelector);

return null;
};
Expand Down

0 comments on commit 2ccd27e

Please sign in to comment.