Performance
The Data API has various optimisations to allow you to use it in a simple way without having a negative impact on performance.
1// Connects to TalkJS servers2const session = getTalkSession({ appId, userId });34// Later, reuses the first connection5const 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 Components, your Components 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
.
1session.user("alice").createIfNotExists({ name: "alice" });2session.user("alice").set({ role: "buyer" });3session.user("alice").subscribe(...);
Refs like UserRef are stateless pointers to a resource. Creating them is instant and there is no need to reuse them unless you want to.
1// Asks TalkJS to start sending updates2const a = conversationRef.subscribe(updateSidebar);34// Purely client-side, attaches another listener5const b = conversationRef.subscribe(updateHeader);67// After this, `b` is still active8// and `updateHeader` will still get called9a.unsubscribe();1011// Now there are no active subscriptions12// Asks TalkJS servers to stop sending updates13b.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.
1// Sends one big request to TalkJS, not 1000 requests2const promises = user.map((ref) => ref.get());3const 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
1alice.get();2alice.set({ name: 'Ally' });3bob.set({ name: 'Robert' });4bob.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.
1// Create a subscription and wait for it to connect2const sub = session.currentUser.subscribe();3await sub.connected;45// Now `.get` on that resource is fast6await session.currentUser.get();7await session.currentUser.get();8await 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.
1let oldMessage;2session.conversation('test').subscribe((snapshot) => {3 if (oldMessage === snapshot.lastMessage) {4 // No need to re-render5 return;6 }78 oldMessage = snapshot.lastMessage;9 rerenderLastMessage(snapshot.lastMessage);10});
Data API Snapshots are compatible with React.memo, Vue's 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:
1import { getTalkSession } from '@talkjs/core';2import { memo } from 'react';34// Skips re-rendering when the message is the same5const Message = memo(({ message }) => {6 return (7 message && (8 <p key={message.id}>9 <Sender sender={message.sender} />10 <span>{message.plaintext}</span>;11 </p>12 )13 );14});1516// Skips re-rendering when the sender is the same17const Sender = memo(({ sender }) => {18 const name = sender?.name ?? 'SYSTEM';19 return <span key={sender.id}>{name}:</span>;20});