### Install Legend-State with npm Source: https://www.legendapp.com/open-source/state/v3/intro/getting-started Installs the beta version of Legend-State using npm. This is the initial step to integrate Legend-State into your project. ```bash npm install @legendapp/state@beta ``` -------------------------------- ### Observing State Changes with get() and shallow modifiers in JavaScript Source: https://www.legendapp.com/open-source/state/v2/llms-full Demonstrates how to observe state changes using `get()` and shallow listeners within an `observe` context. It shows which operations trigger tracking (like `get()` and `map()`) and which do not (like direct property access or `peek()`). This example highlights tracking individual properties, entire objects, and shallow array changes. ```javascript const state$ = observable({ settings: { theme: "dark", }, chats: { messages: [{ id: 0, text: "hi" }], }, }); observe(() => { // Example 1: Tracking a primitive value using get() const theme = state$.settings.theme.get(); // ✅ Tracking [state$.settings.theme] because of get() // Example 2: Accessing an object, then getting a primitive value const settings = state$.settings; // ❌ Not tracking the 'settings' object itself directly const theme = settings.theme.get(); // ✅ Tracking [state$.settings.theme] because of get() // Example 3: Accessing an observable without get() does not track const theme$ = state$.settings.theme; // ❌ Not tracking with no get() // Example 4: Shallow tracking of an array using map() state$.chats.messages.map((m) => ); // ✅ Tracking [state$.chats.messages (shallow)] because of map() // Example 5: Shallow tracking of object keys using Object.keys() Object.keys(state$.settings); // ✅ Tracking [state$.settings (shallow)] }); ``` -------------------------------- ### Full Legend-State and Keel Integration Example Source: https://www.legendapp.com/open-source/state/v3/sync/keel A comprehensive example showcasing the setup of Legend-State with the Keel plugin, including authentication, global configuration, and observable creation for data synchronization. ```typescript import { observable } from '@legendapp/state' import { ObservablePersistLocalStorage } from '@legendapp/state/persist-plugins/local-storage' import { configureSynced } from '@legendapp/state/sync/' import { generateKeelId, syncedKeel } from '@legendapp/state/sync-plugins/keel' import { APIClient } from './keelClient' const client = new APIClient({ baseUrl: process.env.API_BASE_URL }) const isAuthed$ = observable(false); // Set defaults const sync = configureSynced(syncedKeel, { client, persist: { plugin: ObservablePersistLocalStorage, retrySync: true }, debounceSet: 500, retry: { infinite: true, }, changesSince: 'last-sync', waitFor: isAuthed$ }) // enable sync after authentication succeeds async function doAuth() { // authenticate the client await keel.auth.authenticateWithPassword(email, pass) // check that the client is authenticated const isAuthenticated = await keel.auth.isAuthenticated() // Set isAuthed$ to start syncing isAuthed$.set(true) } // Set up your observables with Keel queries const { mutations, queries } = client.api // create an observable with the action functions const messages$ = observable(sync({ list: queries.getMessages, create: mutations.createMessage, update: mutations.updateMessage, delete: mutations.deleteMessage, persist: { name: 'messages' }, })) // get() activates and starts syncing const messages = messages$.get() function addMessage(text: string) { const id = generateKeelId() // Add keyed by id to the messages$ observable to trigger the create action messages$[id].set({ id, text, createdAt: undefined, updatedAt: undefined }) } function updateMessage(id: string, text: string) { // Just set valudes in the observable to trigger the update action messages$[id].text.set(text) } ``` -------------------------------- ### Integrate Legend-State with Supabase for Real-time Sync Source: https://www.legendapp.com/open-source/state/v3/llms-full This example shows a full setup for syncing data with Supabase using Legend-State. It configures Supabase client, defines a custom ID generator, and sets up a `syncedSupabase` observable for managing messages. Dependencies include `@supabase/supabase-js`, `@legendapp/state`, `@legendapp/state/sync-plugins/supabase`, and `uuid`. ```typescript import { createClient } from '@supabase/supabase-js' import { Database } from './database.types' import { observable } from '@legendapp/state' import { configureSyncedSupabase, syncedSupabase } from '@legendapp/state/sync-plugins/supabase' import { v4 as uuidv4 } from "uuid" const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_ANON_KEY) // provide a function to generate ids locally const generateId = () => uuidv4() configureSyncedSupabase({ generateId }) const uid = '' const messages$ = observable(syncedSupabase({ supabase, collection: 'messages', // Optional: // Select only id and text fields select: (from) => from.select('id,text'), // Filter by the current user filter: (select) => select.eq('user_id', uid), // Don't allow delete actions: ['read', 'create', 'update'], // Realtime filter by user_id realtime: { filter: `user_id=eq.${uid}` }, // Persist data and pending changes locally persist: { name: 'messages', retrySync: true }, // Sync only diffs changesSince: 'last-sync' })) // get() activates and starts syncing const messages = messages$.get() function addMessage(text: string) { const id = generateId() // Add keyed by id to the messages$ observable to trigger a create in Supabase messages$[id].set({ id, text, created_at: null, updated_at: null }) } function updateMessage(id: string, text: string) { // Just set values in the observable to trigger an update to Supabase messages$[id].text.set(text) } ``` -------------------------------- ### Full Example: Todo App with Legend-State and React Source: https://www.legendapp.com/open-source/state/v3/intro/getting-started This code snippet demonstrates a complete Todo application using Legend-State for state management and React for the UI. It includes creating observables, defining computed values, handling user input, and rendering lists reactively. Dependencies include '@legendapp/state' and '@legendapp/state/react'. ```typescript import { observable } from "@legendapp/state" import { observer, useValue, useObservable } from "@legendapp/state/react" interface Todo { id: number; text: string; completed?: boolean; } interface Store { todos: Todo[]; total: number; numCompleted: number; addTodo: () => void; } // Create a global observable for the Todos let nextId = 0; const store$ = observable({ todos: [], // Computeds total: (): number => { return store$.todos.length; }, numCompleted: (): number => { return store$.todos.get().filter((todo) => todo.completed).length; }, addTodo: () => { const todo: Todo = { id: nextId++, text: "New todo", }; store$.todos.push(todo); }, }); // Receives item$ prop from the For component function TodoItem({ item$ }) { const onKeyDown = (e) => { // Call addTodo from the global store$ if (e.key === 'Enter') store$.addTodo() } return (
item$.text.set(e.target.value)} onKeyDown={onKeyDown} style={{ color: 'inherit', backgroundColor: 'rgba(255,255,255,0.1)', border: '1px solid #6b7280', borderRadius: '4px', padding: '4px 8px', marginLeft: '8px', width: '120px' }} />
); } function App() { const theme$ = useObservable('dark') const theme = useValue(theme$) const total = useValue(store$.total) const completed = useValue(store$.numCompleted) const onClickClear = () => store$.todos.set([]) return (
Total: {total}
Completed: {completed}
) } ``` -------------------------------- ### Enable $get Configuration (JavaScript) Source: https://www.legendapp.com/open-source/state/v3/llms-full Details how to enable the `$` shorthand for `get()` and `set()` operations on observables via the `enable$get` configuration. This provides a more concise syntax for accessing and modifying observable values. ```javascript import { enable$get } from "@legendapp/state/config/enable$get" enable$get() // Now you can use $ as a shorthand for get() const testValue = state$.test.$ // Assign to $ as a shorthand for set() state$.test.$ = "hello" // Assign objects too just like you can with set() state$.$ = { test: "hello" } ``` -------------------------------- ### Create Synced Observable with Advanced Options (JavaScript) Source: https://www.legendapp.com/open-source/state/v3/llms-full An advanced example of creating a synced observable with numerous options. This includes detailed configurations for 'get', 'set', 'persist' (with MMKV plugin), 'initial' value, 'mode', 'subscribe', 'retry', and 'debounceSet'. ```javascript import { observable } from '@legendapp/state' import { synced } from '@legendapp/state/sync' import { ObservablePersistMMKV } from '@legendapp/state/persist-plugins/mmkv' const state$ = observable(synced({ get: () => { // get is an observing function which will re-run whenever any accessed observables // change. You can use that for paging getting data for a specific user. return fetch('https://url.to.get/page=' + page.get()) .then((res) => res.json()) }, set: ({ value }) => { // set is run when the observable changes, debounced by the debounceSet option fetch('https://url.to.set', { method: 'POST', data: JSON.stringify(value) }) }, persist: { // The name to be saved in the local persistence name: 'test', // Set the plugin to override the global setting plugin: ObservablePersistMMKV, // persist pending changes to be retried after the app restarts retrySync: true, options: { // Customize the persist plugin options } }, // The initial value before the remote data loads or if it doesn't exist. initial: { numUsers: 0, messages: [] }, // How to update the initial value when the remote data comes in. // defaults to "set" mode: 'set' | 'assign' | 'merge' | 'append' | 'prepend', // The subscribe function is called once to give you an opportunity to // subscribe to another service to trigger refresh subscribe: ({ refresh, update }) => { const unsubscribe = pusher.subscribe({ /*...*/ }, (data) => { // Either update with the received data update(data) // Or trigger a refresh of the get function refresh() }) // return unsubscribe function return unsubscribe }, // Options for retrying in case of error. Applies to both get and set. retry: { infinite: true, backoff: 'exponential', maxDelay: 30 }, // A time to debounce changes before sending them to the server. Use this to // batch multiple changes together or preventing saving every keystroke. debounceSet: 500, })) ``` -------------------------------- ### Set up Global Persistence with MMKV Source: https://www.legendapp.com/open-source/state/v3/intro/getting-started This snippet demonstrates how to initialize a global configuration for Legend-State's persistence layer using the MMKV plugin. It sets up an observable and then syncs it with a named persistence configuration, ensuring all changes are automatically saved. ```typescript import { observable } from "@legendapp/state" import { syncObservable } from '@legendapp/state/sync' import { ObservablePersistMMKV } from "@legendapp/state/persist-plugins/mmkv" const store$ = observable({ todos: {}, }) // Persist the observable to the named key of the global persist plugin syncObservable(store$, { persist: { name: 'gettingStarted', plugin: ObservablePersistMMKV } }) ``` -------------------------------- ### Basic Observable Usage in React Source: https://www.legendapp.com/open-source/state/v2/llms-full Provides a fundamental example of using Legend-State observables within a React component. It demonstrates creating an observable, getting and setting its values, and how `get()` calls within a component trigger re-renders when the observable changes. Assumes `enableReactTracking` has been called. ```jsx // Create an observable object const state$ = observable({ settings: { theme: "dark" } }); // Just get and set const theme = state$.settings.theme.get(); state$.settings.theme.set("light"); // observe re-runs when accessed observables change observe(() => { console.log(state$.settings.theme.get()); }); // Enable React components to automatically track observables enableReactTracking({ auto: true }); const Component = function Component() { // get() makes this component re-render whenever theme changes const theme = state$.settings.theme.get(); return
Theme: {theme}
; }; ``` -------------------------------- ### Full Example: Todo App with Legend-State and React Source: https://www.legendapp.com/open-source/state/v3/llms-full This example demonstrates a complete Todo application using Legend-State for state management and React for the UI. It showcases observable state, computed values, and UI interactions like adding, updating, and clearing todos. It utilizes `@legendapp/state/react` for React integration. ```jsx import { observable } from "@legendapp/state" import { observer, useValue, useObservable } from "@legendapp/state/react" interface Todo { id: number; text: string; completed?: boolean; } interface Store { todos: Todo[]; total: number; numCompleted: number; addTodo: () => void; } // Create a global observable for the Todos let nextId = 0; const store$ = observable({ todos: [], // Computeds total: (): number => { return store$.todos.length; }, numCompleted: (): number => { return store$.todos.get().filter((todo) => todo.completed).length; }, addTodo: () => { const todo: Todo = { id: nextId++, text: "New todo", }; store$.todos.push(todo); }, }); // Receives item$ prop from the For component function TodoItem({ item$ }) { const onKeyDown = (e) => { // Call addTodo from the global store$ if (e.key === 'Enter') store$.addTodo() } return (
item$.text.set(e.target.value)} onKeyDown={onKeyDown} style={{ color: 'inherit', backgroundColor: 'rgba(255,255,255,0.1)', border: '1px solid #6b7280', borderRadius: '4px', padding: '4px 8px', marginLeft: '8px', width: '120px' }} />
); } function App() { const theme$ = useObservable('dark') const theme = useValue(theme$) const total = useValue(store$.total) const completed = useValue(store$.numCompleted) const onClickClear = () => store$.todos.set([]) return (
Total: {total}
Completed: {completed}
) } ``` -------------------------------- ### Full Supabase Integration Example with Legend-State Source: https://www.legendapp.com/open-source/state/v3/sync/supabase This example demonstrates a comprehensive setup for integrating Supabase with Legend-State. It includes initializing the Supabase client, configuring local ID generation, creating typed observables for collections, and defining functions for adding and updating messages. It showcases features like selecting specific fields, filtering data, defining allowed actions, enabling persistence, and setting up real-time listeners. ```typescript import { createClient } from '@supabase/supabase-js' import { Database } from './database.types' import { observable } from '@legendapp/state' import { configureSyncedSupabase, syncedSupabase } from '@legendapp/state/sync-plugins/supabase' import { v4 as uuidv4 } from "uuid" const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_ANON_KEY) // provide a function to generate ids locally const generateId = () => uuidv4() configureSyncedSupabase({ generateId }) const uid = '' const messages$ = observable(syncedSupabase({ supabase, collection: 'messages', // Optional: // Select only id and text fields select: (from) => from.select('id,text'), // Filter by the current user filter: (select) => select.eq('user_id', uid), // Don't allow delete actions: ['read', 'create', 'update'], // Realtime filter by user_id realtime: { filter: `user_id=eq.${uid}` }, // Persist data and pending changes locally persist: { name: 'messages', retrySync: true }, // Sync only diffs changesSince: 'last-sync' })) // get() activates and starts syncing const messages = messages$.get() function addMessage(text: string) { const id = generateId() // Add keyed by id to the messages$ observable to trigger a create in Supabase messages$[id].set({ id, text, created_at: null, updated_at: null }) } function updateMessage(id: string, text: string) { // Just set values in the observable to trigger an update to Supabase messages$[id].text.set(text) } ``` -------------------------------- ### Integrate Fetch Plugin for Remote Persistence (JavaScript) Source: https://www.legendapp.com/open-source/state/v2/llms-full Shows how to use the `persistPluginFetch` to easily configure remote persistence using simple URLs for GET and POST requests. This plugin simplifies the process of fetching and setting data from specified endpoints. ```javascript import { observable } from "@legendapp/state"; import { persistObservable } from "@legendapp/state/persist" import { persistPluginQuery } from "@legendapp/state/persist-plugins/query" const state$ = observable({ name: '' }) persistObservable(state$, { pluginRemote: persistPluginFetch({ get: 'https://url.to.get', set: 'https://url.to.set' }) }) ``` -------------------------------- ### Use Tracking Namespace for Shallow Option (JavaScript) Source: https://www.legendapp.com/open-source/state/v2/other/migrating Illustrates the updated way to use the shallow tracking option with `get()` and `obs()`. Instead of passing `shallow` directly, it now uses the `Tracking.shallow` namespace for clarity and consistency. ```javascript import { observable, Tracking } from "@legendapp/state"; const obs = observable([]); // Before // obs.get(shallow); // Now obs.get(Tracking.shallow); ``` -------------------------------- ### Install Legend-State (npm) Source: https://www.legendapp.com/open-source/state/v3/intro/introduction Command to install the beta version of Legend-State using npm. This command fetches the latest beta release, which may be subject to changes before the final release. ```bash npm install @legendapp/state@beta ``` -------------------------------- ### Set up Supabase Typed Client Source: https://www.legendapp.com/open-source/state/v3/llms-full This code snippet shows how to set up a strongly typed Supabase client using the types generated from your Supabase schema. This allows for type-safe interactions with your Supabase database. Dependencies include `@supabase/supabase-js`. ```typescript import { createClient } from '@supabase/supabase-js' import { Database } from './database.types' const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_ANON_KEY) ``` -------------------------------- ### Install Legend-State using npm Source: https://www.legendapp.com/open-source/state/v2/intro/introduction Provides the command to install the Legend-State library using npm, the Node Package Manager. This is the standard way to add the library to a JavaScript project. ```bash npm install @legendapp/state ``` -------------------------------- ### All Available Options Source: https://www.legendapp.com/open-source/state/v3/llms-full A comprehensive list of all configuration options available for `syncedCrud`, including data fetching, manipulation, synchronization, and error handling. ```APIDOC ## syncedCrud Options Overview ### Description This section provides a detailed list and description of all available options for configuring the `syncedCrud` observable. ### Method N/A (Configuration Options) ### Endpoint N/A (Configuration Options) ### Parameters #### General Options - **get** (function) - Function to fetch a single value from the backend. - **list** (function) - Function to fetch an array of values from the backend. - **create** (function) - Function to create a single value on the backend. - **update** (function) - Function to update a single value on the backend. - **delete** (function) - Function to delete a single value on the backend. #### Synchronization and Update Options - **onSaved** (function) - Callback to manually update local observable with remote data. - **onSavedUpdate** (string) - Automatically updates local observable with created and updated times (e.g., 'createdUpdatedAt'). - **fieldCreatedAt** (string) - Specifies the backend field for creation timestamps. - **fieldUpdatedAt** (string) - Specifies the backend field for update timestamps. - **fieldDeleted** (string) - Specifies the backend field for soft deletes. - **updatePartial** (boolean) - If true, sends only changed fields during updates. - **changesSince** (string) - Controls differential syncing ('all' or 'last-sync'). Defaults to 'all'. - **subscribe** (function) - Sets up realtime connection or polling for changes. #### Advanced Options - **generateId** (function) - Provides a custom function for generating row IDs. - **retry** (object) - Options for configuring request retries in case of errors. - **persist** (object) - Options for local data persistence (refer to Persist and sync documentation). - **debounceSet** (number) - Debounce time in milliseconds for saving changes to reduce update frequency. - **mode** (string) - How to apply incoming changes ('set', 'assign', 'merge', 'append', 'prepend'). - **transform** (object) - Functions to transform data before sending to or receiving from the remote source. - **waitFor** (Promise | Observable) - A Promise or Observable to wait for before executing the `get` operation. - **waitForSet** (Promise | Observable) - A Promise or Observable to wait for before executing a set (create, update, delete) operation. ### Request Example ```ts const myObservable$ = observable(syncedCrud({ get: async () => fetch('/api/item/1').then(res => res.json()), list: async () => fetch('/api/items').then(res => res.json()), fieldUpdatedAt: 'modified_at', changesSince: 'last-sync', retry: { attempts: 3 }, mode: 'merge' })) ``` ### Response #### Success Response (200) - Varies based on the specific operation (get, list, etc.). Refer to individual endpoint documentation. #### Response Example ```json { "status": "configured" } ``` ``` -------------------------------- ### Install Keel Plugin Dependency Source: https://www.legendapp.com/open-source/state/v3/sync/keel Instructions for installing the `ksuid` library, which is a dependency for the Keel plugin to generate IDs locally. ```bash npm install ksuid ``` -------------------------------- ### Create a global observable state in Legend-State Source: https://www.legendapp.com/open-source/state/v2/intro/getting-started Demonstrates how to create a single, large global observable store for managing application state. This approach is common for organizing state in larger applications. ```typescript import { observable } from '@legendapp/state'; export const state$ = observable({ UI: { windowSize: undefined as { width: number, height: number }, activeTab: 'home' as 'home' | 'user' | 'profile', ... }, settings: { theme: 'light' as 'light' | 'dark', fontSize: 14, ... }, todos: [] }) ``` -------------------------------- ### Setup Supabase Table for Sync Diffs and Soft Deletes (SQL) Source: https://www.legendapp.com/open-source/state/v3/llms-full SQL script to add `created_at`, `updated_at`, and `deleted` columns to a Supabase table. It also includes a trigger function to automatically manage the `created_at` and `updated_at` timestamps on row inserts and updates. This is necessary for the `changesSince` feature and soft deletes. ```sql ALTER TABLE YOUR_TABLE_NAME ADD COLUMN created_at timestamptz default now(), ADD COLUMN updated_at timestamptz default now(), -- Add column for soft deletes, remove this if you don't need that ADD COLUMN deleted boolean default false; -- This will set the `created_at` column on create and `updated_at` column on every update CREATE OR REPLACE FUNCTION handle_times() RETURNS trigger AS $$ BEGIN IF (TG_OP = 'INSERT') THEN NEW.created_at := now(); NEW.updated_at := now(); ELSEIF (TG_OP = 'UPDATE') THEN NEW.created_at = OLD.created_at; NEW.updated_at = now(); END IF; RETURN NEW; END; $$ language plpgsql; CREATE TRIGGER handle_times BEFORE INSERT OR UPDATE ON YOUR_TABLE_NAME FOR EACH ROW EXECUTE PROCEDURE handle_times(); ``` -------------------------------- ### Configure and Persist Observables with Legend-State Source: https://www.legendapp.com/open-source/state/v2/intro/getting-started Demonstrates how to globally configure persistence plugins for Legend-State observables using `configureObservablePersistence`. It shows how to set default local storage providers for web and React Native, and then how to persist a specific observable using `persistObservable` with a unique key. ```javascript // Global configuration configureObservablePersistence({ // Use Local Storage on web pluginLocal: ObservablePersistLocalStorage // Use react-native-mmkv in React Native pluginLocal: ObservablePersistMMKV }) const state$ = observable({ store: { bigObject: { ... } } }) // Persist this observable persistObservable(state$, { local: 'store' // Unique name }) ``` -------------------------------- ### Persist Global State with Local Storage Source: https://www.legendapp.com/open-source/state/v3/llms-full This example demonstrates how to create a global observable state and persist it to Local Storage using `@legendapp/state/persist-plugins/local-storage`. Changes to the state are automatically saved and restored upon refresh, maintaining the application's previous state. ```javascript import { observable } from "@legendapp/state" import { syncObservable } from "@legendapp/state/sync" import { ObservablePersistLocalStorage } from "@legendapp/state/persist-plugins/local-storage" import { $React } from "@legendapp/state/react-web" import { motion } from "framer-motion" import { useRef } from "react" const state$ = observable({ settings: { showSidebar: false, theme: 'light' }, user: { profile: { name: '', avatar: '' }, messages: {} } }) // Persist state syncObservable(state$, { persist:{ name: 'persistenceExample', plugin: ObservablePersistLocalStorage, } }) // Create a reactive Framer-Motion div const MotionDiv = reactive(motion.div) function App() { const renderCount = ++useRef(0).current const sidebarHeight = () => ( state$.settings.showSidebar.get() ? 96 : 0 ) return (
Renders: {renderCount}
Username:
<$React.input className="input" $value={state$.user.profile.name} /> ({ height: state$.settings.showSidebar.get() ? 96 : 0 })} >
Footer
) } ``` -------------------------------- ### Configure MMKV Persistence (React Native) Source: https://www.legendapp.com/open-source/state/v3/llms-full This snippet shows how to integrate MMKV for persistent storage in React Native applications. It requires installing the `react-native-mmkv` library and then configuring it as the persist plugin for `syncObservable`. ```javascript import { syncObservable } from '@legendapp/state/sync' import { ObservablePersistMMKV } from '@legendapp/state/persist-plugins/mmkv' syncObservable(state$, { persist: { name: "documents", plugin: ObservablePersistMMKV } }) ``` -------------------------------- ### Install React Native Storage Dependencies Source: https://www.legendapp.com/open-source/state/v2/guides/persistence Installs necessary packages for using local storage persistence in React Native applications. Users can choose between `react-native-mmkv` or `@react-native-async-storage/async-storage`. ```bash npm install react-native-mmkv ``` ```bash npm install @react-native-async-storage/async-storage ``` -------------------------------- ### Persistence and Sync with Legend-State Source: https://www.legendapp.com/open-source/state/v3/intro/why Shows how to configure persistence and synchronization for observable state using Legend-State. This example includes setting up local storage persistence and defining asynchronous get and set operations for data synchronization. ```javascript import { ObservablePersistLocalStorage } from '@legendapp/state/persist-plugins/local-storage' import { synced } from '@legendapp/state/sync' import { observable } from '@legendapp/state' const state$ = observable({ initial: { { bigObject: { ... } } }, get: () => fetch('url').then(res => res.json()), set: ({ value }) => fetch('https://url.to.set', { method: 'POST', data: JSON.stringify(value) }), persist: { name: 'test' } }) ``` -------------------------------- ### subscribe: Realtime Updates or Polling Source: https://www.legendapp.com/open-source/state/v3/llms-full Sets up realtime data subscriptions or periodic polling for changes. The `subscribe` function is called once after the initial `get` and should return an unsubscribe function. ```APIDOC ## GET /api/data (Example Endpoint) ### Description This endpoint shows how to configure the `subscribe` option for real-time updates or polling. It demonstrates how to either directly update the observable with incoming data or trigger a refresh of the `get` function. ### Method GET ### Endpoint /api/data ### Parameters #### Query Parameters - **subscribe** (function) - Optional - A function that takes `refresh` and `update` callbacks. It should return an unsubscribe function. ### Request Example ```ts const profile$ = observable(syncedCrud({ // ... other configurations list: () => {/* ... */}, subscribe: ({ refresh, update }) => { const unsubscribe = pusher.subscribe({ /*...*/ }, (data) => { // Either update with the received data update(data) // Or trigger a refresh of the get function refresh() }) // return unsubscribe function return unsubscribe } })) ``` ### Response #### Success Response (200) - **data** (any) - The data received from the subscription or polling. #### Response Example ```json { "data": "updated content" } ``` ``` -------------------------------- ### Configure AsyncStorage Persistence (React Native) Source: https://www.legendapp.com/open-source/state/v3/llms-full This snippet demonstrates configuring AsyncStorage for persistence in React Native. It requires installing `@react-native-async-storage/async-storage` and providing the AsyncStorage instance during the global configuration of `configureSynced`. ```javascript import { configureSynced, syncObservable } from '@legendapp/state/sync' import { observablePersistAsyncStorage } from '@legendapp/state/persist-plugins/async-storage' import AsyncStorage from '@react-native-async-storage/async-storage' // Global configuration const persistOptions = configureSynced({ persist: { plugin: observablePersistAsyncStorage({ AsyncStorage }) } }) syncObservable(state$, persistOptions({ persist: { name: 'store' } })) ``` -------------------------------- ### Sync List Diffs with changesSince (TypeScript) Source: https://www.legendapp.com/open-source/state/v3/llms-full Illustrates how to use the `changesSince: 'last-sync'` option with Legend-State's `syncedKeel` to sync only the differences in a list, significantly reducing bandwidth. It also shows how to emulate a 'get' with a 'list' action. ```TypeScript // Sync diffs of a list syncedKeel({ list: queries.listMessages, changesSince: 'last-sync', persist: { name: 'messages' } }) // Sync diffs of a single value syncedKeel({ list: queries.listUserById, where: { id: myId }, as: 'value', changesSince: 'last-sync', persist: { name: 'me' } }) ``` -------------------------------- ### Performance: Batching State Updates (JavaScript) Source: https://www.legendapp.com/open-source/state/v3/llms-full Demonstrates how to use the `batch` function to group multiple state updates into a single operation. This prevents unnecessary re-renders of components and observers, improving performance when making numerous changes. ```javascript const state$ = observable({ items: [] }); function addItems() { for (let i = 0; i < 1000; i++) { state$.items.push({ text: `Item ${i}` }); } } // ❌ This can render 1000 times while pushing to the array // addItems(); // ✅ Batching delays until complete and renders once batch(addItems); ``` -------------------------------- ### Install next-transpile-modules for Next.js Source: https://www.legendapp.com/open-source/motion/v2/resources/next-js Installs the `next-transpile-modules` package, which is required to transpile packages that use ES modules in a Next.js project. This is a prerequisite for integrating Legend-Motion. ```bash npm install next-transpile-modules ``` -------------------------------- ### Message List with Synced Fetch and For in React Source: https://www.legendapp.com/open-source/state/v3/llms-full This example demonstrates displaying a list of messages fetched from a server using `syncedFetch`. It utilizes `useComputed` to create a computed observable for the username and the `For` component for high-performance rendering of the message array. Dependencies include `@legendapp/state/react`, `@legendapp/state/react-web`, and `@legendapp/state/sync-plugins/fetch`. ```jsx import { For, Show, useObservable, useObservable } from "@legendapp/state/react" import { $React } from "@legendapp/state/react-web" import { syncedFetch } from "@legendapp/state/sync-plugins/fetch" let nextID = 0 function generateID() { return nextID ++ } function App() { const renderCount = ++useRef(0).current // Create profile from fetch promise const profile$ = useObservable(syncedFetch({ get: 'https://reqres.in/api/users/1' })) // Username const userName = useObservable(() => { const p = profile$.data.get() return p ? p.first_name + ' ' + p.last_name : '' }) // Chat state const { messages, currentMessage } = useObservable({ messages: [], currentMessage: '' }) // Button click const onClickAdd = () => { messages.push({ id: generateID(), text: currentMessage.get(), }) currentMessage.set('') } return (
Renders: {renderCount}
Loading...}>
Chatting with {userName}
{(message) =>
{message.text.get()}
}
<$React.input className="input" placeholder="Enter message" $value={currentMessage} onKeyDown={e => e.key === 'Enter' && onClickAdd()} />
) } ``` -------------------------------- ### Install Legend-Motion via npm Source: https://www.legendapp.com/open-source/motion/v2/getting-started/introduction This command installs the Legend-Motion library using npm. It's a prerequisite for using the library in your React Native or React Native Web project. ```bash npm install @legendapp/motion ``` -------------------------------- ### Form Validation with useObserve in React Source: https://www.legendapp.com/open-source/state/v3/llms-full This example demonstrates form validation in React using the `useObserve` hook to listen for form state changes and update error messages in real-time. It defers validation until the first 'Save' button click for a better user experience. Dependencies include `@legendapp/state/react` and `@legendapp/state/react-web`. ```jsx import { useRef } from "react" import { useObservable, useObserve, Memo, Show } from "@legendapp/state/react" import { $React } from "@legendapp/state/react-web" function App() { const renderCount = ++useRef(0).current const username$ = useObservable('') const password$ = useObservable('') const usernameError$ = useObservable('') const passwordError$ = useObservable('') const didSave$ = useObservable(false) const successMessage$ = useObservable('') useObserve(() => { if (didSave$.get()) { usernameError$.set(username$.get().length < 3 ? 'Username must be > 3 characters' : '' ) const pass = password$.get() passwordError$.set( pass.length < 10 ? 'Password must be > 10 characters' : !pass.match(/\\d/)? 'Password must include a number' : '' ) } }) const onClickSave = () => { // setting triggers useObserve, updating error messages didSave$.set(true) if (!usernameError$.get() && !passwordError$.get()) { console.log('Submit form') passwordError$.delete() successMessage$.set('Saved!') } } return (
Renders: {renderCount}
Username:
<$React.input className="input" $value={username$} />
{usernameError$}
Password:
<$React.input type="password" className="input" $value={password$} />
{passwordError$}
{() => (
{successMessage$.get()}
)}
) } ``` -------------------------------- ### Specify Supported Actions with `actions` in TypeScript Source: https://www.legendapp.com/open-source/state/v3/llms-full Control which CRUD operations (create, read, update, delete) are supported by the observable using the `actions` parameter. This example demonstrates enabling only 'read' and 'create' actions, disabling 'update' and 'delete'. ```typescript const messages$ = observable(syncedSupabase({ supabase, collection: 'messages', // Only read and create, no update or delete actions: ['read', 'create'], })) ``` -------------------------------- ### Enable Sync Diffs with Legend-State `syncedSupabase` (TypeScript) Source: https://www.legendapp.com/open-source/state/v3/llms-full Example of configuring the `syncedSupabase` function in Legend-State to sync only data differences from Supabase. It utilizes the `changesSince: 'last-sync'` option and specifies the corresponding timestamp and delete fields from the Supabase table. ```typescript syncedSupabase({ supabase, collection: 'messages', persist: { name: 'messages' }, // Enable syncing only changes since last-sync changesSince: 'last-sync', fieldCreatedAt: 'created_at', fieldUpdatedAt: 'updated_at', // Optionally enable soft deletes fieldDeleted: 'deleted' }) ``` -------------------------------- ### changesSince: 'last-sync' for Efficient Syncing Source: https://www.legendapp.com/open-source/state/v3/llms-full Optimizes bandwidth by syncing only changes since the last query using the `changesSince: 'last-sync'` option. Requires `fieldUpdatedAt` and proper handling of deleted rows. ```APIDOC ## GET /api/data (Example Endpoint) ### Description This endpoint demonstrates the `changesSince: 'last-sync'` option, which significantly reduces bandwidth by fetching only data modified since the last synchronization. This requires proper configuration of `fieldUpdatedAt` and handling of soft deletes. ### Method GET ### Endpoint /api/data ### Parameters #### Query Parameters - **changesSince** (string) - Optional - Set to 'last-sync' to enable differential syncing. Defaults to 'all'. - **fieldUpdatedAt** (string) - Required - The field name used by the backend to track the last update time. This field should be managed by the backend. - **fieldDeleted** (string) - Optional - The field name used for soft deletes. If used, the list function must include deleted rows. ### Request Example ```ts const dataList$ = observable(syncedCrud({ // ... other configurations list: () => {/* ... */}, fieldUpdatedAt: 'updatedAt', // Example: backend uses 'updatedAt' for updates fieldDeleted: 'isDeleted', // Example: backend uses 'isDeleted' for soft deletes changesSince: 'last-sync' })) ``` ### Response #### Success Response (200) - **items** (array) - An array of data items that have changed since the last sync. #### Response Example ```json { "items": [ { "id": "123", "name": "Updated Item", "updatedAt": "2023-10-27T11:00:00Z" } ] } ``` ``` -------------------------------- ### Quick Start: Basic Legend List Usage in React Native Source: https://www.legendapp.com/open-source/list/v2/getting-started This example demonstrates the basic usage of Legend List in a React Native component. It shows how to import the component, define data, and configure essential props like `data`, `renderItem`, and `keyExtractor`. The `recycleItems` prop is included to enable item recycling for performance. ```javascript import { Text } from "react-native"; import { LegendList } from "@legendapp/list"; const items = [ { id: "1", title: "Item 1" }, { id: "2", title: "Item 2" }, { id: "3", title: "Item 3" }, ]; export function MyList() { return ( {item.title}} keyExtractor={(item) => item.id} recycleItems /> ); } ``` -------------------------------- ### Filter Data with syncedSupabase in Legend-State Source: https://www.legendapp.com/open-source/state/v3/llms-full This example demonstrates how to apply filters when using `syncedSupabase` in Legend-State to retrieve specific data from a collection. It uses the `filter` parameter, which accepts a function to build Supabase query filters. Refer to Supabase's documentation for available filter options. ```typescript const messages$ = observable(syncedSupabase({ supabase, collection: 'messages', // Filter by the current user filter: (select) => select.eq('user_id', 'uid') })) ``` -------------------------------- ### Auto-saving Form with TanStack Query Source: https://www.legendapp.com/open-source/state/v3/llms-full This example utilizes the `useObservableSyncedQuery` hook to integrate Legend App state with TanStack Query for automatic form data synchronization. It binds observable state directly to input fields, sending mutations to the server whenever the observable changes, effectively syncing form data with server data. ```javascript import axios from "axios" import { useRef } from "react" import { QueryClient, QueryClientProvider } from "@tanstack/react-query" import { useObservable, Memo } from "@legendapp/state/react" import { $React } from "@legendapp/state/react-web" import { useObservableSyncedQuery } from '@legendapp/state/sync-plugins/tanstack-react-query' const queryClient = new QueryClient() function App() { return ( ) } function Example() { const renderCount = ++useRef(0).current const lastSaved$ = useObservable(0) const data$ = useObservableSyncedQuery({ queryClient, query: { queryKey: ["data"], queryFn: () => axios.get("https://reqres.in/api/users/1") .then((res) => res.data.data), }, mutation: { mutationFn: (newData) => { // Uncomment to actually save /* debounce(() => { axios .post("https://reqres.in/api/users/1", newData) .then((res) => lastSaved$.set(Date.now()) ) }, 1000) */ lastSaved$.set(Date.now()) } } }) return (
Renders: {renderCount}
Name:
<$React.input className="input" $value={data$.first_name} />
Email:
<$React.input className="input" $value={data$.email} />
Last saved: {lastSaved$}
) } ```