---
url: https://talkjs.com/docs/Data_APIs/JavaScript/Performance
---

# Performance

Ask a question Copy for LLM [View as Markdown](/docs/Data_APIs/JavaScript/Performance.md)
The Data API has various optimisations to allow you to use it in a simple way without having a negative impact on performance.

### Using TalkSession in multiple places

```ts
// Connects to TalkJS servers
const session = getTalkSession({ appId, userId });

// Later, reuses the first connection
const session = getTalkSession({ appId, userId });
```

It is OK to call `getTalkSession` whenever you need it, rather than calling it once and passing the `TalkSession` object around your codebase.
When you call `getTalkSession` the second time with the same `appId` and `userId`, it returns the first session object again, rather than creating a new one.

If you use TalkJS [React components or web components](/docs/UI_Components/), your UI and your Data API code will automatically share one session object.
This is true even if you mount multiple Components UIs on the page, as long as they all use the same `appId` and `userId`.

### Create refs as-needed

```ts
session.user("alice").createIfNotExists({ name: "alice" });
session.user("alice").set({ role: "buyer" });
session.user("alice").subscribe(...);
```

Refs like [UserRef](/docs/JavaScript_Data_API/Users/#UserRef) are stateless pointers to a resource.
Creating them is instant. If you create a ref with the same ID multiple times, TalkJS will reuse the same object.

### Subscribing in multiple places

```ts
// Asks TalkJS to start sending updates
const a = conversationRef.subscribe(updateSidebar);

// Purely client-side, attaches another listener
const b = conversationRef.subscribe(updateHeader);

// After this, `b` is still active
// and `updateHeader` will still get called
a.unsubscribe();

// Now there are no active subscriptions
// Asks TalkJS servers to stop sending updates
b.unsubscribe();
```

It is OK to call `.subscribe` whenever you need it, rather than calling it once and passing the `Snapshot` objects around.
When you call `.subscribe` on a resource for the second time, it does not cause any extra requests to the backend.
This is totally seamless.
The second subscription will keep working even if you unsubscribe the first subscription.

Make sure that you call `.unsubscribe` when you don't need it any more.
Having lots of useless subscriptions will cause your device to do extra unnecessary work.

### Fetching lots of data at once

```ts
// Sends one big request to TalkJS, not 1000 requests
const promises = user.map((ref) => ref.get());
const users = await Promise.all(promises);
```

If you need to fetch lots of data at once, put all the calls together in a row.
The Data API will automatically combine the calls into one big request, which is much faster and more efficient than 1000 small requests.

To make sure this happens, you should group similar calls together.
If you want to call `UserRef.createIfNotExists` then `UserRef.get` for each user in a list, do all `createIfNotExists` calls first, then all `get` calls second.
Make sure the calls happen in one synchronous block, rather than using `await` in between each call.

Why it matters (technical explanation)

When you do a potentially-batchable call, the Data API does not send the request
immediately. Instead, it waits for the next "microtask" in the JavaScript event loop.
When you use `await`, the microtask runs and the batch is sent too early.

Other things can also cause the batch to be sent early, such as reaching the maximum batch
size or making an incompatible call, as seen in the following code

```ts
alice.get();
alice.set({ name: 'Ally' });
bob.set({ name: 'Robert' });
bob.get();
```

Here, the Data API cannot batch together the `.get` calls without breaking the order of the requests.
If the `.get` batch happens at the start, Bob's `.get` would incorrectly return his old name.
If the `.get` batch happens at the end, Alice's `.get` would incorrectly return her new name.

This is why it's recommend to group similar requests together.

### Getting the same data repeatedly

```ts
// Create a subscription and wait for it to connect
const sub = session.currentUser.subscribe();
await sub.connected;

// Now `.get` on that resource is fast
await session.currentUser.get();
await session.currentUser.get();
await session.currentUser.get();
```

If you need to call `.get` many times in a row, consider creating a subscription first.
When you call `.get` on a resource you are already subscribed to, the data is usually returned from the locale cache without needing to ask the TalkJS servers.

### Knowing when to skip re-rendering

```ts
let oldMessage;
session.conversation('test').subscribe((snapshot) => {
  if (oldMessage === snapshot.lastMessage) {
    // No need to re-render
    return;
  }

  oldMessage = snapshot.lastMessage;
  rerenderLastMessage(snapshot.lastMessage);
});
```

Data API Snapshots are compatible with [React.memo](https://react.dev/reference/react/memo), [Vue's v‑memo](https://vuejs.org/api/built-in-directives.html#v-memo), and most other tools designed to skip re-rendering when nothing changed.
Specifically, snapshots are immutable and referentially-stable.

For example, when a conversation's subject changes, your conversation subscription will emit a new snapshot.
However, the `lastMessage` property will still point to the old `MessageSnapshot`, so you don't need to re-render that part of the UI.

**Note:** Immutability is guaranteed, but referential stability is best-effort.
In edge cases like when recovering from network loss, the Data API sometimes create a new snapshot even though nothing changed.
It is OK to depend on this for performance, but not for business logic.

**Framework-specific examples:**

**React**
```tsx
import { getTalkSession } from '@talkjs/core';
import { memo } from 'react';

// Skips re-rendering when the message is the same
const Message = memo(({ message }) => {
  return (
    message && (
      <p key={message.id}>
        <Sender sender={message.sender} />
        <span>{message.plaintext}</span>;
      </p>
    )
  );
});

// Skips re-rendering when the sender is the same
const Sender = memo(({ sender }) => {
  const name = sender?.name ?? 'SYSTEM';
  return <span key={sender.id}>{name}:</span>;
});
```

**Vue**
```tsx
{/* Message.vue */}
<template>
  <p v-if="message" v-memo="[message]">
    <Sender :sender="message.sender" />
    <span>{{ message.plaintext }}</span>
  </p>
</template>

<script setup>
  import { defineProps } from 'vue';
  import Sender from './Sender.vue';
  const props = defineProps([ 'message' ]);
</script>

{/* Sender.vue */}
<template>
  <span v-memo="[sender]">{{ senderName }}:</span>
</template>

<script setup>
  import { defineProps, computed } from 'vue';
  const props = defineProps([ 'sender' ]);
  const senderName = computed(() => props.sender?.name ?? 'SYSTEM');
</script>
```