# Shared entities

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

The client keeps one object for each channel and each thread, and one copy of each message. Every channel list, every search result and your own code get the same objects, so a change made in one place shows everywhere.

This page covers how to get those objects and how to load them. How long the client keeps them is on [Entity lifecycle](https://getstream.io/chat/docs/sdk/react/v15/state/state-and-entities/entity-lifecycle/).

## One instance per id

### One Channel and one Thread per id

>
> **Info:** **Example**
>
> Say the channel "general" is in the user's channel list and is also open on screen. Both use the same `Channel` object. When a message arrives in "general", the list row and the open channel both show it, because they read the same stores.
>

The client finds them by id:

```ts
// undefined when the client has no channel with this cid
const general = client.channelManager.get("messaging:general");

// A thread's id is the id of its parent message.
const thread = client.threads.get(messageId);
```

### Messages are shared, read-only snapshots

The client keeps one copy of each message. A message shown in the channel's message list and as the parent of a thread is that one copy. When the user adds a reaction to it, the reaction shows in the channel's list and in the thread at once.

The message objects you get are snapshots of that copy. Each update replaces the message with a new object, so code that compares objects sees the change. Changing a field on a message object you received doesn't update the message, and no list shows the change. The next update of that message discards it.

Change messages through the SDK's methods instead, such as `channel.messageOperations.update()` for an edit and `channel.messageOperations.addReaction()` for a reaction. See [Message operations](https://getstream.io/chat/docs/sdk/react/v15/state/writing-messages/message-operations/).

## Getting an instance

### client.channel

`client.channel(type, id, custom)` returns the channel for a type and an id: the one the client has, or a new one, which the client keeps from then on. It sends no request.

```ts
const general = client.channel("messaging", "general");
const same = client.channel("messaging", "general");

console.log(general === same); // true
```

A new channel holds only the data you passed until it's loaded, as described in [Loading](#loading). For a channel the client already has, `client.channel()` applies only `custom` and ignores the rest.

It needs a user: call it after `client.connectUser()`, which doesn't have to finish first. Without a user, it throws.

### Provisional channels

A channel you create from its members, without an id, is provisional until the server answers its first query. That's the usual way to start a direct message: you know who's in it, and the server either creates the channel or returns the one these members already have. If the client already has that conversation, you get the loaded channel straight away.

```ts
import type { StreamChat } from "stream-chat";

// The direct message between the current user and another user, created if it doesn't exist yet.
export const getDirectMessage = (client: StreamChat, otherUserId: string) =>
  client
    .channel("messaging", {
      members: [{ user_id: client.userId! }, { user_id: otherUserId }],
    })
    .watch();
```

The SDK's components and `ChatView` handle the rest. If your own code stores the channel or its cid, for example in a URL or a map, wait for `Channel.watch()` and store the channel it returns. Its cid is final from then on; before that, it's temporary.

A channel you create with an id is never provisional.

### Threads

Each parent message has one thread object. `client.threads.ensure()` returns it: the one the client already has, for example from the thread list, or a new one. Like `client.channel()`, it sends no request.

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

Get threads this way, not with `new Thread()`. A thread from `ThreadManager.ensure()` is the one the thread list and the layout use, and it receives events. Opening a thread in the UI calls `ThreadManager.ensure()` for you. You call it yourself when your code needs the thread object: to render a thread in a layout of your own, to read a thread's state in a component of your own, or to load its replies ahead of time.

A new thread holds only its parent message. Its replies load when the UI shows it. Without a UI, call `Thread.activate()`: it loads them, and returns the function that releases the thread when you're done.

### What not to do

- **Don't create instances with `new Channel()` or `new Thread()`.** The client doesn't keep an object you construct: events don't update it, and channel paginators hold the client's own instance instead. Use `client.channel()` or `ThreadManager.ensure()`.
- **Don't keep the threads that `client.getThreadAndHydrate()` and `client.queryThreadsAndHydrate()` return.** They return the client's instance when it has one for that thread. Otherwise they return a separate object, which the client doesn't keep and events don't update. Get threads through `client.threads.ensure()` or `client.threads.get()`.

## Loading

### watch and query

There are two ways to load a channel:

- `Channel.watch()` makes a channel ready to show. It loads the channel (its data, members, read state and latest messages) and subscribes this connection to its events. Calling it again is safe, even from several places at once: it sends a request only when the channel isn't loaded yet, or when you pass options it wasn't watched with.
- `Channel.query(options)` loads the channel with the options you pass, and always sends a request. It watches the channel only when they include `watch: true`.

So call `Channel.watch()` wherever a channel is about to be shown, as often as needed, even from several places at once: a click, a notification, a deep link. It resolves with the channel to use from then on:

```ts
const openChannel = async (id: string) => {
  const opened = await client.channel("messaging", id).watch();

  // `opened` is watched. Use this object from here on.
  return opened;
};
```

To open a channel at a particular message, for example from a deep link, watch it and then jump to the message:

```ts
import type { Channel } from "stream-chat";

const openAtMessage = async (channel: Channel, messageId: string) => {
  const opened = await channel.watch();
  await opened.messagePaginator.jumpToMessage(messageId);
  return opened;
};
```

Don't pass `messages` options to `Channel.watch()` for this: options the current watch wasn't sent with make it send another watch request. `MessagePaginator.jumpToMessage()` loads the page around the message only when it isn't loaded; see [Message paginators](https://getstream.io/chat/docs/sdk/react/v15/state/loading-data/message-paginators/).

### reload

`Channel.reload()` watches the channel again and refreshes its newest loaded messages, which repeating `Channel.watch()` doesn't do. It refreshes as many as it had loaded, not only the latest page. It catches up on what the client missed: new messages, edits, and messages deleted on the server in the meantime. Messages that failed to send stay in the list.

It does nothing for a channel that was never loaded. After a reconnect, the client reloads active channels for you; see [Keeping a channel in memory](https://getstream.io/chat/docs/sdk/react/v15/state/state-and-entities/entity-lifecycle/#keeping-a-channel-in-memory) and [Connection](https://getstream.io/chat/docs/sdk/react/v15/state/connection/).

### Loading flags

Three flags in `channel.state` say how far a channel is loaded:

| Flag          | What it says                                                                                                                                                                                                                 |
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `initialized` | A live server response was applied to this object: a query, a watch or a channel list page. It never goes back to `false`. While it's `false`, nothing has confirmed that the channel exists on the server.                  |
| `offlineMode` | The channel's data was restored from a local offline database, not loaded from the server.                                                                                                                                   |
| `watchStatus` | `"watching"`: events arrive. `"wasWatching"`: the connection dropped, so the watch ended and should be restored. `"notWatching"`: never watched, or watching was stopped on purpose. The values are in `ChannelWatchStatus`. |

They're in the store, so subscribe to them:

```ts
import { ChannelWatchStatus } from "stream-chat";

const unsubscribe = channel.state.subscribeWithSelector(
  ({ initialized, watchStatus }) => ({ initialized, watchStatus }),
  ({ initialized, watchStatus }) => {
    if (!initialized) console.log("Not loaded yet");
    else if (watchStatus === ChannelWatchStatus.Watching) console.log("Live");
    else console.log("Loaded, not live");
  },
);
```

`channel.initialized`, `channel.offlineMode` and `channel.watchStatus` read the same values once, without subscribing.

### Watching needs a connection

The server ties a watch to this client's WebSocket connection. So:

- `Channel.watch()` and any query with `watch: true` wait until the WebSocket has connected. Calling them right after `client.connectUser()`, without waiting for it, is fine.
- With no WebSocket open or opening, for example after `client.closeConnection()`, they fail at once.
- To stop waiting, pass a signal: `Channel.watch(undefined, { signal })`.
- `Channel.query()` without `watch: true` doesn't need the WebSocket. It loads the channel without watching it.

Watching keeps the channel current only while the connection lasts. What the client does after a reconnect is on [Connection](https://getstream.io/chat/docs/sdk/react/v15/state/connection/).

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react/v15/state/state-and-entities/shared-entities/](https://getstream.io/chat/docs/sdk/react/v15/state/state-and-entities/shared-entities/).