# What's optimistic and what isn't

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

An optimistic action shows its result before the server confirms it. In `stream-chat`, the message operations, poll votes and drafts are optimistic, and conversation actions aren't. This page lists which is which, and what happens when the server answers or a request fails.

>
> **Info:** **Example**
>
> The user likes a message and then archives the channel:
>
> 1. The like appears on the message at once. `channel.messageOperations.addReaction()` changed the message before sending the request.
> 2. The server answers, and its copy of the message, with its reaction counts, replaces the local one.
> 3. The channel doesn't look archived yet. `Channel.archive()` only sends the request.
> 4. The server sends a `member.updated` event. It updates the user's membership in `channel.state`, and the channel now looks archived, in every list that shows it.
>

## Message operations

These actions change local state before the request goes out:

| Action                                                                     | Shown at once                                              | When the request fails                                                                       |
| -------------------------------------------------------------------------- | ---------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| `MessageOperations.send()` and `MessageOperations.retry()`                 | the message, with the status `sending`                     | the message stays, marked `failed`, ready to retry                                           |
| `MessageOperations.update()`                                               | the new text, marked as edited                             | the edit stays, and the message is marked `failed`; editing again saves it, retrying doesn't |
| `MessageOperations.delete()`                                               | the message as deleted, or gone for a hard delete          | the message comes back                                                                       |
| `MessageOperations.addReaction()` and `MessageOperations.deleteReaction()` | the reaction added or removed                              | undone                                                                                       |
| `Poll.castVote()` and `Poll.removeVote()`                                  | the vote cast or removed                                   | undone                                                                                       |
| `Poll.close()`                                                             | the poll as closed                                         | reopened, unless the server confirmed the close in the meantime                              |
| `MessageComposer.createDraft()` and `MessageComposer.deleteDraft()`        | the draft is the composer's content, so it's already shown | nothing is undone                                                                            |

The composer's `MessageComposer.send()` and `MessageComposer.update()` go through these operations, so they behave the same way. The composer also clears itself before the request goes out.

When the server answers, its copy replaces the local one. For a send or an edit, that happens only if no newer copy arrived in the meantime, for example through a `message.updated` event. Poll votes are sent one at a time, in the order they were made. The server's vote counts are shown once the events of all pending votes have arrived.

With offline support, an edit, a delete or a reaction that waits in the offline queue isn't failed but pending, so its local change stays. A send that waits there is still marked `failed`, because retrying is how the user gets it out.

How each operation works is on [Message operations](https://getstream.io/chat/docs/sdk/react/v15/state/writing-messages/message-operations/).

## Conversation actions

Actions on a channel, a membership or a user aren't optimistic, and neither is pinning a message. Their effect appears when the server's event or response arrives, not when the call is made.

### Actions that wait for the server

| Action                                                                                                           | Local state changes when                                                                  |
| ---------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| `Channel.markRead()`, `Channel.markReadViaReporter()`, `Thread.markRead()`                                       | the `message.read` or `notification.mark_read` event updates the read state               |
| `Channel.markUnread()`                                                                                           | the `notification.mark_unread` event updates the read state                               |
| `Channel.archive()`, `Channel.unarchive()`, `Channel.pin()` and `Channel.unpin()`                                | the `member.updated` event updates `channel.state.membership`                             |
| `Channel.mute()` and `Channel.unmute()`                                                                          | the `notification.channel_mutes_updated` event updates `channel.state.muteStatus`         |
| `client.pinMessage()` and `client.unpinMessage()`                                                                | the `message.updated` event updates the message                                           |
| `Channel.hide()` and `Channel.show()`                                                                            | the `channel.hidden` or `channel.visible` event updates `channel.data.hidden`             |
| `Channel.update()`, `Channel.updatePartial()`, `Channel.addMembers()`, `Channel.removeMembers()`                 | the response updates `channel.data`, and the member events update `channel.state.members` |
| `client.blockUser()` and `client.unblockUser()`                                                                  | the response arrives, and `client.blockedUsers` is updated                                |
| `client.reminders.createReminder()`, `client.reminders.updateReminder()` and `client.reminders.deleteReminder()` | the response arrives, and the reminder events after it                                    |

If such a request fails, nothing has changed locally, so there is nothing to undo. How read state and unread counts change is on [Read state and unread counts](https://getstream.io/chat/docs/sdk/react/v15/state/channels-and-threads/read-state-and-unread-counts/).

### The local-only exception

`Channel.markReadLocally()` changes local state and sends nothing. It sets the current user's unread count in the channel to zero by dispatching a `message.read_locally` event, which is handled like a `message.read` event.

It's for busy channels such as livestreams, where read events are best turned off, so the server doesn't count unread messages. With `readEvents.localUnreadCountEnabled` in the channel's [configuration](https://getstream.io/chat/docs/sdk/react/v15/state/state-and-entities/configuration/), the client counts them itself. Marking such a channel read with `Channel.markReadViaReporter()`, which automatic marking read uses too, then calls `Channel.markReadLocally()` for you, unless a `markReadRequest` handler is registered.

### Showing a pending state

A UI that should react at once shows a pending state of its own: from the call until the event arrives, or until the request fails. Here a menu item shows "Archiving…" until the membership changes:

```ts
const setArchiving = (archiving: boolean) => {
  // Update your UI here, for example disable the menu item.
  console.log(archiving ? "Archiving…" : "Done");
};

const archiveChannel = async () => {
  setArchiving(true);

  const stop = channel.state.subscribeWithSelector(
    ({ membership }) => ({ archivedAt: membership.archived_at }),
    ({ archivedAt }, previous) => {
      // The first call reports the current value. Wait for the event to change it.
      if (!previous || !archivedAt) return;
      setArchiving(false);
      stop();
    },
  );

  try {
    await channel.archive();
  } catch (error) {
    setArchiving(false);
    stop();
    throw error;
  }
};
```

## Raw API calls

Some calls send the request and nothing else: `Channel.sendMessage()`, `client.sendReaction()` and `Channel.sendReaction()`, `client.updateMessage()` and `client.deleteMessage()`. They don't change local state, before or after the request:

```ts
// Nothing appears in the list until the server's message.new event arrives.
await channel.sendMessage({ message: { text: "Hello" } });
```

In a watched channel, the server's events still reach the list: `message.new`, `message.updated`, `message.deleted` and the reaction events. But nothing is shown before them, and a failed request leaves no `failed` message to retry. Use them for scripts, or when you keep the local state yourself, and use `channel.messageOperations` or `thread.messageOperations` everywhere else.

## Used in

- [Sending, editing and deleting](https://getstream.io/chat/docs/sdk/react/v15/features/messaging/sending-editing-and-deleting/)
- [Reactions](https://getstream.io/chat/docs/sdk/react/v15/features/messaging/reactions/)
- [Channel lists](https://getstream.io/chat/docs/sdk/react/v15/features/channels/channel-lists/)
- [Read state, unread and receipts](https://getstream.io/chat/docs/sdk/react/v15/features/messaging/read-state-unread-and-receipts/)
- [Flagging, muting, blocking and bans](https://getstream.io/chat/docs/sdk/react/v15/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/v15/state/writing-messages/whats-optimistic-and-what-isnt/](https://getstream.io/chat/docs/sdk/react/v15/state/writing-messages/whats-optimistic-and-what-isnt/).