# Events

> **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/).

Most changes to the state start as events from the server: a message arrives, a member joins, someone starts typing. The client applies each event to its state, then calls the listeners your code registered.

Listeners are for side effects, such as playing a sound or logging. To show data, read the [stores](https://getstream.io/chat/docs/sdk/react/v15/state/state-and-entities/reactive-state/), which already hold the result of every event.

## Subscribing

`client.on()` hears every event the client receives: the events of every channel it watches, and notifications for the user. `Channel.on()` hears the events of one channel. Both take an event type and a listener, and return `{ unsubscribe }`:

```ts
const newMessages = client.on("message.new", (event) => {
  if (event.user?.id !== client.userId) playNotificationSound();
});

const typing = channel.on("typing.start", (event) => {
  console.log(`${event.user?.name ?? "Someone"} is typing in ${channel.cid}`);
});

// When the listeners are no longer needed:
newMessages.unsubscribe();
typing.unsubscribe();
```

The type narrows the event: a `message.new` listener gets the fields of a `message.new` event. Leave the type out to hear every event, and narrow the event on `event.type`:

```ts
const everything = client.on((event) => {
  if (event.type === "member.added")
    console.log(`${event.member.user_id} joined ${event.cid}`);
});
```

Don't pass `"all"` as the type: the listener hears every event at runtime, but its event is typed `never`. `client.off()` and `Channel.off()` also remove a listener. Your own event types are declared on [Data conventions](https://getstream.io/chat/docs/sdk/react/v15/state/state-and-entities/data-conventions/#custom-data-types).

## What the SDK already applies

By the time your listener runs, the client has applied the event to the channel's state. For a `message.new` event, the message is in the channel's message list. For `typing.start`, the user is in `channel.state.typing`. For `member.added`, the member is in `channel.state.members`.

So a listener shouldn't apply the event again. If it needs the result, it reads the state.

Two things are worth knowing about timing:

- A message list publishes live changes in batches, so a subscriber to `messagePaginator.state` can hear about a new message a moment after your listener runs. See [Throttling and the memory cap](https://getstream.io/chat/docs/sdk/react/v15/state/loading-data/message-paginators/#throttling-and-the-memory-cap).
- Channel lists and threads update from the same events, but there's no fixed order between those updates and your listener. To react to a list or a thread changing, subscribe to its store instead of listening for the event.

## Events the client raises itself

The client dispatches a few events of its own, which reach the same listeners as the server's:

| Event                           | When                                                                                                                                                                                                                                                                                                                                                                                     | What it carries                                                                                                                                                                                                               |
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `channels.queried`              | A channel was queried or watched, or a page of channels was loaded.                                                                                                                                                                                                                                                                                                                      | `queriedChannels.channels`: each channel's data from the response, such as `channel`, `messages`, `members` and `read`, not `Channel` instances. `queriedChannels.isLatestMessageSet`: whether those messages are the newest. |
| `capabilities.changed`          | A query or `Channel.updatePartial()` returned different capabilities for the current user in a channel. `channel.state.ownCapabilities` holds them already.                                                                                                                                                                                                                              | `cid`, `own_capabilities`                                                                                                                                                                                                     |
| `connection.recovered`          | The client reloaded its active channels and threads after a reconnect. Channel lists refresh alongside and can finish later.                                                                                                                                                                                                                                                             | nothing                                                                                                                                                                                                                       |
| `message.read_locally`          | `Channel.markReadLocally()` reset the unread count without sending a request. It's for busy channels such as livestreams, where read events are best turned off and `readEvents.localUnreadCountEnabled` counts unread messages on the client. See [Turning read events off](https://getstream.io/chat/docs/sdk/react/v15/state/channels-and-threads/read-state-and-unread-counts/#turning-read-events-off). | `cid`, `last_read_message_id`                                                                                                                                                                                                 |
| `live_location_sharing.started` | `Channel.sendSharedLocation()` started a live share, one with an `end_at`. See [Location sharing](https://getstream.io/chat/docs/sdk/react/v15/features/rich-content/location-sharing/).                                                                                                                                                                                                                     | `message`: the message with the share                                                                                                                                                                                         |
| `live_location_sharing.stopped` | `Channel.stopLiveLocationSharing()` succeeded. It also ends the share when the channel is no longer watched. See [Location sharing](https://getstream.io/chat/docs/sdk/react/v15/features/rich-content/location-sharing/).                                                                                                                                                                                   | `live_location`: the stopped share                                                                                                                                                                                            |
| `connection.fallback_activated` | With `enableWSFallback`, the WebSocket couldn't connect and the client switched to long-polling.                                                                                                                                                                                                                                                                                         | `mode`: `"longpoll"`                                                                                                                                                                                                          |
| `offline_reactions.queried`     | With offline support, `client.queryReactionsAndHydrate()` found cached reactions for the first page, before the request.                                                                                                                                                                                                                                                                 | `offlineReactions`: the cached reactions                                                                                                                                                                                      |

`typing.stop` is a server event, but the client also raises it when a user who was typing has sent nothing for 7 seconds.

`connection.recovered` is the one apps use most. When it fires, the client has already reloaded its active channels and threads, so use it to refresh data your app keeps itself:

```ts
client.on("connection.recovered", () => {
  void refreshOrderStatus();
});
```

What recovery does is on [Connection](https://getstream.io/chat/docs/sdk/react/v15/state/connection/).

## Connection changes

The connection's status isn't sent as an event. It's in two stores:

- `client.wsConnection.state`: `isHealthy` says whether this client's WebSocket is up.
- `client.networkConnection.state`: `isOnline` says whether the device has a network. It's `undefined` while that's unknown, so test `isOnline === false`.

```ts
const unsubscribe = client.wsConnection.state.subscribeWithSelector(
  ({ isHealthy }) => ({ isHealthy }),
  ({ isHealthy }) => showReconnectingBanner(!isHealthy),
);
```

The handler runs once right away, then on every change. Both stores publish a change as soon as it happens, so if a short drop shouldn't show a banner, delay it yourself. The two stores are explained on [Connection](https://getstream.io/chat/docs/sdk/react/v15/state/connection/).

## Used in

- [Connection status](https://getstream.io/chat/docs/sdk/react/v15/features/app-experience/connection-status/)

---

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