# What channels and threads hold

> **Info:** This is release candidate documentation for **Stream Chat React Native SDK v10**. For the latest stable version, see the [latest version (v9)](https://getstream.io/chat/docs/sdk/react-native/).

A channel and a thread hold their conversations the same way. Each has its own state store, message paginator, composer, message operations and read state. A thread still belongs to a channel. It's identified by a message in that channel, and a few things, such as members, capabilities and settings, exist only on the channel.

>
> **Info:** **Example**
>
> Take the channel "Design", and the thread under the user's message "Release plan" in it. A reply the user types in the thread goes through the thread's composer into the thread's message paginator. The channel's message paginator and composer are untouched.
>

This page lists what they share, then what belongs only to a channel or only to a thread. Getting a channel or a thread, and loading it, is on [Shared entities](https://getstream.io/chat/docs/sdk/react-native/v10/state/state-and-entities/shared-entities/).

## What channels and threads share

|                    | Channel                     | Thread                                                                 |
| ------------------ | --------------------------- | ---------------------------------------------------------------------- |
| State              | `channel.state`             | `thread.state`                                                         |
| Message paginator  | `channel.messagePaginator`  | `thread.messagePaginator`                                              |
| Composer           | `channel.messageComposer`   | `thread.messageComposer`                                               |
| Message operations | `channel.messageOperations` | `thread.messageOperations`                                             |
| Read state         | `read` in `channel.state`   | `read` in `thread.state`                                               |
| Typing             | `typing` in `channel.state` | `typing` in `channel.state`, the entries with the thread's `parent_id` |

A thread belongs to its channel, `thread.channel`. Its replies are sent through that channel, and its typing events arrive there too. An event for a thread reply carries the thread's `parent_id`. `thread.state` has no `typing` field. Who's typing in a thread is in the `typing` field of `channel.state`, which maps each typing user's id to their typing event. Keep the entries whose `parent_id` is the thread's id.

Each `state` is one store. Read a field once with `getLatestValue()`, or subscribe and select the fields you need:

```ts
const ownUserId = client.userId;

const unsubscribe = channel.state.subscribeWithSelector(
  ({ memberCount, read }) => ({
    memberCount,
    unread: ownUserId ? (read[ownUserId]?.unread_messages ?? 0) : 0,
  }),
  ({ memberCount, unread }) => {
    console.log(`${memberCount} members, ${unread} unread`);
  },
);
```

In a component, read the same store with `useStateStore`, as [Reading state in components](https://getstream.io/chat/docs/sdk/react-native/v10/ui/reading-state-in-components/) shows.

### Where each part is explained

- The message paginator: [Message paginators](https://getstream.io/chat/docs/sdk/react-native/v10/state/loading-data/message-paginators/).
- The composer: [Message composer](https://getstream.io/chat/docs/sdk/react-native/v10/state/writing-messages/message-composer/).
- Message operations: [Message operations](https://getstream.io/chat/docs/sdk/react-native/v10/state/writing-messages/message-operations/).
- Read state: [Read state and unread counts](https://getstream.io/chat/docs/sdk/react-native/v10/state/channels-and-threads/read-state-and-unread-counts/).
- Typing: [Typing indicators](https://getstream.io/chat/docs/sdk/react-native/v10/features/messaging/typing-indicators/).
- What keeps a channel or a thread in memory: [Entity lifecycle](https://getstream.io/chat/docs/sdk/react-native/v10/state/state-and-entities/entity-lifecycle/).

## Channel

### Data and members

`channel.state` holds these fields:

| Field                        | What it holds                                                                                                                                                                                                                |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `data`                       | The channel's server data: `frozen`, `hidden`, `cooldown`, `member_count`, `team`, `custom` and the other fields.                                                                                                            |
| `members`                    | The loaded members, by user id. A large channel loads only some of them. To search all of them, use a `ChannelMemberSearchSource`, see [Search sources](https://getstream.io/chat/docs/sdk/react-native/v10/state/loading-data/search-sources/). |
| `memberCount`                | The channel's total member count.                                                                                                                                                                                            |
| `membership`                 | The current user's own membership.                                                                                                                                                                                           |
| `watchers`, `watcherCount`   | Who is watching the channel right now.                                                                                                                                                                                       |
| `watchStatus`                | Whether this client watches the channel: `watching`, `wasWatching` (a reconnect ended the watch, see [Connection](https://getstream.io/chat/docs/sdk/react-native/v10/state/connection/#channels-re-watched-on-demand)) or `notWatching`.        |
| `read`                       | Every member's read state.                                                                                                                                                                                                   |
| `typing`                     | Who is typing.                                                                                                                                                                                                               |
| `ownCapabilities`            | What the current user may do in the channel.                                                                                                                                                                                 |
| `muteStatus`                 | Whether the current user muted the channel.                                                                                                                                                                                  |
| `initialized`, `offlineMode` | Whether the channel has been loaded from the server, or only from local data.                                                                                                                                                |
| `active`                     | Whether something has opened the channel, with `Channel.activate()`.                                                                                                                                                         |
| `aiState`                    | The state of an AI response in progress, see [SDK integration](https://getstream.io/chat/docs/sdk/react-native/v10/features/ai/sdk-integration/).                                                                                                |

`channel.data`, `channel.state.members` and the other getters on `channel` and `channel.state` return the value at the moment you read them. They don't tell you when it changes. To follow a change, subscribe to `channel.state` and select the field. [Reactive state](https://getstream.io/chat/docs/sdk/react-native/v10/state/state-and-entities/reactive-state/) explains why.

`memberCount` comes from the channel's data. It changes on `channel.updated`, which the server sends whenever members change. `members` changes on `member.added`, `member.updated` and `member.removed`.

With [offline support](https://getstream.io/chat/docs/sdk/react-native/v10/state/offline-support/offline-support/), a channel restored from the offline database has `offlineMode` set and `initialized` unset until a server response loads it. `<Channel>` also sets `offlineMode` when the watch it starts fails, so the channel keeps working with the data it holds. Calls that need a loaded channel, such as `Channel.markRead()`, accept either flag.

### Membership

`membership` is the current user's membership in the channel: their role, when they joined, and two dates the user sets for themselves:

- **`archived_at`** is set while the user has archived the channel;
- **`pinned_at`** is set while the user has pinned it.

`Channel.archive()`, `Channel.unarchive()`, `Channel.pin()` and `Channel.unpin()` change them. The change shows when the server's `member.updated` event arrives, not when the call returns. Channel paginators filtered on `archived` or `pinned` move the channel at the same moment.

```ts
const unsubscribe = channel.state.subscribeWithSelector(
  ({ membership }) => ({
    archived: !!membership.archived_at,
    pinned: !!membership.pinned_at,
  }),
  ({ archived, pinned }) => {
    console.log({ archived, pinned });
  },
);

await channel.archive(); // the handler runs again when member.updated arrives
```

### Capabilities

Capabilities say what the current user may do in this channel, such as `send-message`, `upload-file`, `send-reaction` or `skip-slow-mode`. The server derives them from the user's roles and the channel type's permissions.

They're in the `ownCapabilities` field of `channel.state`, and they update when the channel is loaded again or when `channel.updated` carries new ones. When a query or `Channel.updatePartial()` returns different capabilities, the client also dispatches `capabilities.changed`.

```ts
const canReact = channel.state
  .getLatestValue()
  .ownCapabilities.includes("send-reaction");
```

### Settings

Settings say which features the channel has, such as typing events, read events and replies. There are two views of them:

- **`channel.config`** is what's in effect: the channel type's settings combined with what the app registered in `client.config`. `channel.configState` is the same value as a store.
- **`channel.serverConfig`** is the channel type's settings as the server sent them: `typing_events`, `read_events`, `replies`, `uploads` and the others. It's `undefined` until the channel has been loaded.

Check `channel.config`, not `channel.serverConfig`. Either side can turn a feature off, and only `config` includes both:

```ts
const { readEvents, replies, typingEvents } = channel.config;

console.log({
  readEvents: readEvents.enabled,
  replies: replies.enabled,
  typingEvents: typingEvents.enabled,
});
```

[Configuration](https://getstream.io/chat/docs/sdk/react-native/v10/state/state-and-entities/configuration/) explains which settings the client decides, which the server decides, and which need both.

### Mute

The `muteStatus` field of `channel.state` says whether the current user muted the channel: `{ muted, createdAt, expiresAt }`, with the dates in nanoseconds or `null`.

`Channel.mute()` and `Channel.unmute()` change it. `Channel.mute({ expiration })` mutes for that many milliseconds. Like membership, the field changes when the server's event arrives, `notification.channel_mutes_updated`. A muted channel's new messages don't count as unread.

### Slow mode

In slow mode, a user waits a number of seconds between messages. The channel's `data.cooldown` holds the number of seconds, and `Channel.enableSlowMode(seconds)` and `Channel.disableSlowMode()` change it.

Each channel has a cooldown timer, `channel.cooldownTimer`. Its `state` counts down for the current user:

| Field                       | What it holds                                              |
| --------------------------- | ---------------------------------------------------------- |
| `cooldownConfigSeconds`     | The channel's cooldown, in seconds. `0` without slow mode. |
| `cooldownRemaining`         | Whole seconds until the user can send again.               |
| `canSkipCooldown`           | Whether the user has the `skip-slow-mode` capability.      |
| `ownLatestMessageTimestamp` | When the user last sent a message, in nanoseconds.         |

```ts
const unsubscribe = channel.cooldownTimer.state.subscribeWithSelector(
  ({ cooldownRemaining }) => ({ cooldownRemaining }),
  ({ cooldownRemaining }) => {
    console.log(
      cooldownRemaining > 0 ? `Wait ${cooldownRemaining}s` : "You can send",
    );
  },
);
```

In a component, `useCooldownRemaining()` reads `cooldownRemaining`. [Slow mode](https://getstream.io/chat/docs/sdk/react-native/v10/features/channels/slow-mode/) shows how the composer uses it.

## Thread

### Parent message and replies

A thread is identified by its parent message: `thread.id` is the parent message's id. `thread.state` holds:

| Field                       | What it holds                                                      |
| --------------------------- | ------------------------------------------------------------------ |
| `parentMessage`             | The message the thread replies to.                                 |
| `replyCount`                | How many replies the thread has.                                   |
| `participants`              | The users taking part in the thread.                               |
| `read`                      | Every participant's read state in the thread.                      |
| `channel`                   | The channel the thread belongs to.                                 |
| `title`, `custom`           | The thread's own data.                                             |
| `active`                    | Whether something has opened the thread, with `Thread.activate()`. |
| `isLoading`, `isStateStale` | Whether the thread is loading, and whether it needs loading again. |

The parent message is the same message object the channel's message paginator holds. An edit or a reaction on it shows in both places, and `parentMessage` and `replyCount` follow it.

The replies are in `thread.messagePaginator`. A reply also sent to the channel, with `show_in_channel`, is in the channel's message paginator too. Thread replies aren't stored in the offline database, so a thread needs a connection to load.

`client.threads.ensure({ channel, parentMessage })` returns the thread for a message, creating it if the client doesn't have it yet, without a request. A thread created this way knows only its parent message. When it may have replies the client doesn't hold, it loads them the first time it's activated, which `<Thread>` does when it shows the thread:

```ts
const thread = client.threads.ensure({ channel, parentMessage: message });

// The user opened the thread. Call release() when they close it.
const release = thread.activate();
```

The SDK's message actions get a thread this way when the user opens one, and `<Thread>` activates the thread it shows and releases it when it unmounts.

While a thread is active:

- a new reply doesn't count as unread for the current user, and the thread marks itself read;
- when its state is stale, it loads again.

These happen only while the thread manager, `client.threads`, listens to events, after `ThreadManager.registerSubscriptions()`. `<Chat>` calls it when it mounts, and `ThreadManager.unregisterSubscriptions()` when it unmounts. `<Thread>` also loads a stale thread itself.

## The user's threads

The thread manager, `client.threads`, keeps the user's threads. Its paginator, `client.threads.paginator`, loads the threads, and `client.threads.state` holds what applies to all of them:

| Field                | What it holds                                                                                                                    |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `unreadThreadCount`  | How many of the user's threads have unread replies. The server sends this number, so it counts threads the client hasn't loaded. |
| `unseenThreadIds`    | Threads that got a new reply since the thread paginator last loaded, and aren't in it.                                           |
| `isThreadOrderStale` | Whether a thread in the paginator got a reply, so their order may be out of date.                                                |

`ThreadManager.reload()` loads the thread paginator again when it hasn't loaded yet, when there are unseen threads, or when the order is stale, and clears both once the load succeeds. `ThreadManager.reload({ force: true })` loads it in any case. `<ThreadList>` calls `ThreadManager.activate()` while it's mounted and its `isFocused` prop is `true`, and each activation runs `ThreadManager.reload()`.

```ts
const unsubscribe = client.threads.state.subscribeWithSelector(
  ({ unreadThreadCount, unseenThreadIds }) => ({
    unreadThreadCount,
    unseenThreadIds,
  }),
  ({ unreadThreadCount, unseenThreadIds }) => {
    console.log(
      `${unreadThreadCount} unread threads, ${unseenThreadIds.length} new`,
    );
  },
);
```

The thread list itself is on [Replies and threads](https://getstream.io/chat/docs/sdk/react-native/v10/features/messaging/replies-and-threads/), and its API on [Thread and thread manager](https://getstream.io/chat/docs/sdk/react-native/v10/reference/state/thread-and-thread-manager/).

## Used in

- [Channel details and members](https://getstream.io/chat/docs/sdk/react-native/v10/features/channels/channel-details-and-members/)
- [Replies and threads](https://getstream.io/chat/docs/sdk/react-native/v10/features/messaging/replies-and-threads/)
- [Slow mode](https://getstream.io/chat/docs/sdk/react-native/v10/features/channels/slow-mode/)
- [Flagging, muting, blocking and bans](https://getstream.io/chat/docs/sdk/react-native/v10/features/moderation-and-safety/flagging-muting-blocking-and-bans/)

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react-native/v10/state/channels-and-threads/what-channels-and-threads-hold/](https://getstream.io/chat/docs/sdk/react-native/v10/state/channels-and-threads/what-channels-and-threads-hold/).