# Events

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

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-native/v10/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, so 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}`);
});
```

Passing `"all"` as the type is the same as leaving it out. `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-native/v10/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 timing rules apply:

- 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-native/v10/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, a page of channels was loaded, or a channel's message list loaded a page.                                                                                                                                                                                                                                                                                     | `queriedChannels.channels`: each channel's data from the response, such as `channel`, `messages`, `members` and `read`, not `Channel` instances. `queriedChannels.isLatestMessageSet`: whether the response is a newest page that the offline database should store as the latest messages. It's `true` for channel list pages and for a `Channel.watch()` that loaded the newest messages, and `false` for other channel queries and for message list pages. |
| `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. On a reconnect, it isn't dispatched when connection recovery is turned off, and never when the socket dropped again while the reloads ran. `client.connectionRecovery.recover()` dispatches it too, even with recovery turned off.                                 | 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-native/v10/state/channels-and-threads/read-state-and-unread-counts/#turning-read-events-off). | `cid`, and `last_read_message_id` when the newest messages are loaded                                                                                                                                                                                                                                                                                                                                                                                         |
| `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-native/v10/features/rich-content/location-sharing/).                                                                                                                                                                                                                     | `message`: the message with the share                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `live_location_sharing.stopped` | `Channel.stopLiveLocationSharing()` succeeded. It's dispatched locally, so the stop is seen even when the channel isn't watched and no server event arrives. See [Location sharing](https://getstream.io/chat/docs/sdk/react-native/v10/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](https://getstream.io/chat/docs/sdk/react-native/v10/state/offline-support/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();
});
```

`Chat` closes the WebSocket while the app is in the background, so returning to the foreground is a reconnect too, and `connection.recovered` fires after it. `Channel` listens for it to mark the channel read when the list shows the newest messages. With offline support, the active channels reload once the offline database has synced the events it missed. What recovery does is on [Connection](https://getstream.io/chat/docs/sdk/react-native/v10/state/connection/).

## Connection changes

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

- `isHealthy` in `client.wsConnection.state` says whether this client's WebSocket is up.
- `isOnline` in `client.networkConnection.state` says whether the device has a network. It's `undefined` while that's unknown, so test `isOnline === false`. `Chat` reports the device's network to it through `@react-native-community/netinfo`.

```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. Components read the same stores with `useWSConnectionState` and `useNetworkConnectionState` (see [Connection hooks](https://getstream.io/chat/docs/sdk/react-native/v10/reference/hooks/connection-hooks/)). The two stores are explained on [Connection](https://getstream.io/chat/docs/sdk/react-native/v10/state/connection/).

## Used in

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

---

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