PageSourceSearch

https://cbl-reactnative.dev/assets/js/392dc68c.fa673edd.js

js cbl-reactnative.dev collected 2026-10-01 08:10:47 UTC 9,451 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunkcbl_reactnative=self.webpackChunkcbl_reactnative||[]).push([[1737],{1271:(e,n,r)=>{r.r(n),r.d(n,{assets:()=>c,contentTitle:()=>o,default:()=>d,frontMatter:()=>a,metadata:()=>t,toc:()=>l});const t=JSON.parse('{"id":"Queries/live-queries","title":"Live Queries","description":"Description - Couchbase Lite Live Query Concepts","source":"@site/docs/Queries/live-queries.md","sourceDirName":"Queries","slug":"/Queries/live-queries","permalink":"/Queries/live-queries","draft":false,"unlisted":false,"editUrl":"https://github.com/couchbase/cbl-reactnative-docs/tree/main/docs/Queries/live-queries.md","tags":[],"version":"current","sidebarPosition":6,"frontMatter":{"id":"live-queries","sidebar_position":6},"sidebar":"tutorialSidebar","previous":{"title":"Query Result Sets","permalink":"/Queries/query-result-set"},"next":{"title":"Query Troubleshoooting","permalink":"/Queries/query-troubleshooting"}}');var i=r(4848),s=r(8453);const a={id:"live-queries",sidebar_position:6},o="Live Queries",c={},l=[{value:"Activating a Live Query",id:"activating-a-live-query",level:2},{value:"Example 1. Starting a Live Query - Change Listener",id:"example-1-starting-a-live-query---change-listener",level:4},{value:"Example 2. Stopping a Live Query - Change Listener",id:"example-2-stopping-a-live-query---change-listener",level:4},{value:"Query Change Data Structure",id:"query-change-data-structure",level:2},{value:"Example 3. Complete Live Query with Error Handling",id:"example-3-complete-live-query-with-error-handling",level:4},{value:"Example 4. React Hook Pattern for Live Queries",id:"example-4-react-hook-pattern-for-live-queries",level:4},{value:"Best Practices",id:"best-practices",level:2}];function u(e){const n={a:"a",admonition:"admonition",blockquote:"blockquote",code:"code",em:"em",h1:"h1",h2:"h2",h4:"h4",header:"header",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.header,{children:(0,i.jsx)(n.h1,{id:"live-queries",children:"Live Queries"})}),"\n",(0,i.jsxs)(n.blockquote,{children:["\n",(0,i.jsxs)(n.p,{children:["Description - ",(0,i.jsx)(n.em,{children:"Couchbase Lite Live Query Concepts"}),"\nRelated Content - ",(0,i.jsx)(n.a,{href:"/Queries/sqlplusplus",children:"SQL++ for Mobile"})]}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"activating-a-live-query",children:"Activating a Live Query"}),"\n",(0,i.jsx)(n.p,{children:"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."}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"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."]}),"\n",(0,i.jsx)(n.p,{children:"With Couchbase Lite for React Native, live queries can be watched through:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Listener callbacks: ",(0,i.jsx)(n.code,{children:"Query.addChangeListener"})]}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"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."}),"\n",(0,i.jsx)(n.h4,{id:"example-1-starting-a-live-query---change-listener",children:"Example 1. Starting a Live Query - Change Listener"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"import { ListenerToken } from '@couchbase/couchbase-lite-react-native';\n\n// Register a change listener\nconst token: ListenerToken = await query.addChangeListener((change) => {  \n  if (change.error) {  \n    console.error('Query error:', change.error);\n    return;\n  }\n  \n  const results = change.results;  \n  // results is an array of result objects\n  for (const doc of results) {  \n    console.log('Result:', doc);                \n  }  \n}); \n"})}),"\n",(0,i.jsx)(n.admonition,{title:"Version 1.0",type:"note",children:(0,i.jsxs)(n.p,{children:["Change listeners now return a ",(0,i.jsx)(n.code,{children:"ListenerToken"})," object with a ",(0,i.jsx)(n.code,{children:"remove()"})," method for cleanup."]})}),"\n",(0,i.jsx)(n.admonition,{title:"Version 1.1",type:"note",children:(0,i.jsx)(n.p,{children:"Live query listeners use the TurboModule typed event path on New Architecture builds. This improves listener routing and cleanup consistency across iOS and Android."})}),"\n",(0,i.jsx)(n.h4,{id:"example-2-stopping-a-live-query---change-listener",children:"Example 2. Stopping a Live Query 
1- Change Listener"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"// Remove listener using new API\nawait token.remove();\n"})}),"\n",(0,i.jsxs)(n.admonition,{title:"Deprecated",type:"caution",children:[(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.code,{children:"query.removeChangeListener(token)"})," method is deprecated. It remains available for backward compatibility, but new applications should use ",(0,i.jsx)(n.code,{children:"token.remove()"}),". Existing applications are strongly encouraged to migrate."]}),(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"// DEPRECATED\nawait query.removeChangeListener(token);\n\n// RECOMMENDED\nawait token.remove();\n"})})]}),"\n",(0,i.jsx)(n.h2,{id:"query-change-data-structure",children:"Query Change Data Structure"}),"\n",(0,i.jsxs)(n.p,{children:["When a query result changes, your callback receives a ",(0,i.jsx)(n.code,{children:"QueryChange"})," object:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"interface QueryChange {\n  error: string;      // Error message if query failed\n  query: Query;       // Reference to the query\n  results: ResultSet; // Array of result objects\n}\n"})}),"\n",(0,i.jsx)(n.h4,{id:"example-3-complete-live-query-with-error-handling",children:"Example 3. Complete Live Query with Error Handling"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"import { ListenerToken } from '@couchbase/couchbase-lite-react-native';\n\nconst query = database.createQuery(\n  'SELECT META().id, name, email FROM _default.users WHERE isActive = true'\n);\n\nconst token: ListenerToken = await query.addChangeListener((change) => {\n  if (change.error) {\n    console.error('Query error:', change.error);\n    return;\n  }\n\n  console.log(`Found ${change.results.length} active users`);\n  change.results.forEach(result => {\n    console.log(`User: ${result.name} - ${result.email}`);\n  });\n});\n\n// Remove when done\nawait token.remove();\n"})}),"\n",(0,i.jsx)(n.h4,{id:"example-4-react-hook-pattern-for-live-queries",children:"Example 4. React Hook Pattern for Live Queries"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"import { useEffect, useState } from 'react';\nimport { ListenerToken } from '@couchbase/couchbase-lite-react-native';\n\nfunction ActiveUsersScreen({ database }) {\n  const [users, setUsers] = useState([]);\n\n  useEffect(() => {\n    if (!database) return;\n\n    let token;\n    \n    const setup = async () => {\n      const query = database.createQuery(\n        'SELECT META().id, name FROM _default.users WHERE isActive = true'\n      );\n      \n      token = await query.addChangeListener((change) => {\n        if (!change.error) {\n          setUsers(change.results);\n        }\n      });\n    };\n    setup();\n\n    // Cleanup on unmount\n    return () => {\n      if (token && !token.isRemoved()) {\n        token.remove();\n      }\n    };\n  }, [database]);\n\n  return (\n    // Render users list\n  );\n}\n"})}),"\n",(0,i.jsx)(n.h2,{id:"best-practices",children:"Best Practices"}),"\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.strong,{children:"Always Remove Listeners"})}),"\n",(0,i.jsx)(n.p,{children:"In React components, use the cleanup function to prevent memory leaks:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"useEffect(() => {\n  let token;\n  \n  const setup = async () => {\n    token = await query.addChangeListener((change) => {\n      // Handle changes\n    });\n  };\n  setup();\n  \n  return () => {\n    if (token && !token.isRemoved()) {\n      token.remove();\n    }\n  };\n}, [query]);\n"})}),"\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.strong,{children:"Check Before Removing"})}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"if (!token.isRemoved()) {\n  await token.remove();\n}\n"})})]})}function d(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(u,{...e})}):u(e)}},8453:(e,n,r)=>{r.d(n,{R:()=>a,x:()=>o});var t=r(6540);const i={},s=t.createContext(i);function a(e){const n=t.useContext(s);return t.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),t.createElement(s.Provider,{value:n},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.