### 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 (
)
}
```
--------------------------------
### 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 (
)
}
```
--------------------------------
### 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 (
)
}
```
--------------------------------
### 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 (
)
}
```
--------------------------------
### 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 (
)}
)
}
```
--------------------------------
### 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 (