Skip to main content

Live Queries

Description - Couchbase Lite Live Query Concepts Related Content - SQL++ for Mobile

Activating a Live Query

A live query is a query that, once activated, remains active and monitors the database for changes; refreshing the result set whenever a change occurs. As such, it is a great way to build reactive user interfaces — especially table/list views — that keep themselves up to date.

So, a simple use case may be: A replicator running and pulling new data from a server, whilst a live-query-driven UI automatically updates to show the data without the user having to manually refresh. This helps your app feel quick and responsive.

With Couchbase Lite for React Native, live queries can be watched through:

  • Listener callbacks: Query.addChangeListener

Each time you start watching a live query, the query is executed and an initial change notification is dispatched. The query is then kept active and further change notifications are dispatched whenever a change occurs.

Example 1. Starting a Live Query - Change Listener

import { ListenerToken } from 'cbl-reactnative';

// Register a change listener
const token: ListenerToken = await query.addChangeListener((change) => {
if (change.error) {
console.error('Query error:', change.error);
return;
}

const results = change.results;
// results is an array of result objects
for (const doc of results) {
console.log('Result:', doc);
}
});
Version 1.0

Change listeners now return a ListenerToken object with a remove() method for cleanup.

Example 2. Stopping a Live Query - Change Listener

// Remove listener using new API
await token.remove();
Deprecated

The query.removeChangeListener(token) method is deprecated. It remains available for backward compatibility, but new applications should use token.remove(). Existing applications are strongly encouraged to migrate.

// DEPRECATED
await query.removeChangeListener(token);

// RECOMMENDED
await token.remove();

Query Change Data Structure

When a query result changes, your callback receives a QueryChange object:

interface QueryChange {
error: string; // Error message if query failed
query: Query; // Reference to the query
results: ResultSet; // Array of result objects
}

Example 3. Complete Live Query with Error Handling

import { ListenerToken } from 'cbl-reactnative';

const query = database.createQuery(
'SELECT META().id, name, email FROM _default.users WHERE isActive = true'
);

const token: ListenerToken = await query.addChangeListener((change) => {
if (change.error) {
console.error('Query error:', change.error);
return;
}

console.log(`Found ${change.results.length} active users`);
change.results.forEach(result => {
console.log(`User: ${result.name} - ${result.email}`);
});
});

// Remove when done
await token.remove();

Example 4. React Hook Pattern for Live Queries

import { useEffect, useState } from 'react';
import { ListenerToken } from 'cbl-reactnative';

function ActiveUsersScreen({ database }) {
const [users, setUsers] = useState([]);

useEffect(() => {
if (!database) return;

let token;

const setup = async () => {
const query = database.createQuery(
'SELECT META().id, name FROM _default.users WHERE isActive = true'
);

token = await query.addChangeListener((change) => {
if (!change.error) {
setUsers(change.results);
}
});
};
setup();

// Cleanup on unmount
return () => {
if (token && !token.isRemoved()) {
token.remove();
}
};
}, [database]);

return (
// Render users list
);
}

Best Practices

Always Remove Listeners

In React components, use the cleanup function to prevent memory leaks:

useEffect(() => {
let token;

const setup = async () => {
token = await query.addChangeListener((change) => {
// Handle changes
});
};
setup();

return () => {
if (token && !token.isRemoved()) {
token.remove();
}
};
}, [query]);

Check Before Removing

if (!token.isRemoved()) {
await token.remove();
}