Skip to Content

Client Hooks

React hooks for accessing synchronized data.

useModel

Get a single model instance by ID.

Signature

function useModel<T extends Model>( type: string, id: string | null | undefined ): T | null

Parameters

ParameterTypeDescription
typestringModel type name
idstring | null | undefinedModel ID (can be null)

Returns

Model instance or null if id is null/undefined or model not found.

Example

const IssueDetail = observer(({ issueId }) => { const issue = useModel<Issue>('issue', issueId) if (!issue) return <div>Issue not found</div> return <div>{issue.title}</div> })

Reactivity

Re-renders when:

  • Model is updated in the pool
  • Model is deleted from the pool

useCollectionModels

Get a collection of models, optionally filtered.

Signature

function useCollectionModels<T extends Model>( type: string, filter?: (model: T) => boolean ): T[]

Parameters

ParameterTypeDescription
typestringModel type name
filter(model: T) => booleanOptional filter function

Returns

Array of model instances.

Examples

// All tasks const TaskList = observer(() => { const tasks = useCollectionModels<Task>('task') return tasks.map(task => ( <TaskRow key={task.id} task={task} /> )) }) // Filtered tasks const ActiveTasks = observer(() => { const tasks = useCollectionModels<Task>('task', task => !task.done ) return tasks.map(task => ( <TaskRow key={task.id} task={task} /> )) }) // Tasks for specific team const TeamTasks = observer(({ teamId }) => { const tasks = useCollectionModels<Task>('task', task => task.teamId === teamId ) return tasks.map(task => ( <TaskRow key={task.id} task={task} /> )) })

Reactivity

Re-renders when:

  • Any model of this type is added
  • Any model of this type is updated
  • Any model of this type is deleted

Type Hydration

useCollectionModels automatically hydrates the type from storage on mount:

// First render: Loads type from storage const tasks = useCollectionModels<Task>('task') // Subsequent renders: Uses cached data from ObjectPool

useStore

Access the store instance for mutations.

Signature

function useStore(): Store

Returns

Store instance with mutation methods.

Example

const TaskEditor = observer(({ task }) => { const store = useStore() const handleSave = async () => { await store.save('task', task.id, { title: task.title, done: task.done }) } const handleDelete = async () => { await store.remove('task', task.id) } return ( <div> <input value={task.title} onChange={e => task.title = e.target.value} /> <button onClick={handleSave}>Save</button> <button onClick={handleDelete}>Delete</button> </div> ) })

useConnectionState

Monitor sync connection state.

Signature

function useConnectionState(): ConnectionState

Returns

interface ConnectionState { isOnline: boolean isSyncing: boolean }

Example

const SyncIndicator = observer(() => { const { isOnline, isSyncing } = useConnectionState() if (!isOnline) return <Badge variant='warning'>Offline</Badge> if (isSyncing) return <Badge variant='info'>Syncing...</Badge> return <Badge variant='success'>Synced ✓</Badge> })

observer

MobX observer HOC - re-exported from MobX for convenience.

Signature

function observer<T>(component: React.FC<T>): React.FC<T>

Example

import { observer, useModel } from '@gluonic/client' const TaskRow = observer(({ taskId }) => { const task = useModel<Task>('task', taskId) if (!task) return null // Automatically re-renders when task updates return <div>{task.title}</div> })

Rules

All components that read model data MUST be wrapped with observer():

// ✅ Correct const TaskRow = observer(({ task }) => { return <div>{task.title}</div> // Will update }) // ❌ Wrong const TaskRow = ({ task }) => { return <div>{task.title}</div> // Won't update! }

useIdentityMap

Access the IdentityMap instance (advanced use only).

Signature

function useIdentityMap(): IdentityMap

Returns

IdentityMap instance with identity mapping methods.

Example

const AdvancedComponent = () => { const bridge = useIdentityMap() // Access model directly const issue = bridge.getModel<Issue>('issue', issueId) // Query by index const teamIssues = bridge.queryByIndex<Issue>('issue', 'teamId', teamId) return <div>...</div> }

Advanced: Most apps should use useModel and useCollectionModels instead. Only use useIdentityMap for advanced patterns.


Hook Patterns

Combining Hooks

const IssueDetail = observer(({ issueId }) => { const issue = useModel<Issue>('issue', issueId) const comments = useCollectionModels<Comment>('comment', c => c.issueId === issueId ) const store = useStore() const { isOnline } = useConnectionState() if (!issue) return <div>Not found</div> return ( <div> <h1>{issue.title}</h1> <CommentList comments={comments} /> {isOnline && <button onClick={() => store.save(...)}>Save</button>} </div> ) })

Custom Hooks

// Custom hook wrapping Gluonic hooks function useTeamIssues(teamId: string) { const issues = useCollectionModels<Issue>('issue', issue => issue.teamId === teamId ) const openIssues = issues.filter(i => !i.done) const closedIssues = issues.filter(i => i.done) return { issues, openIssues, closedIssues } } // Usage const TeamBoard = observer(({ teamId }) => { const { openIssues, closedIssues } = useTeamIssues(teamId) return ( <div> <Column>{openIssues.map(...)}</Column> <Column>{closedIssues.map(...)}</Column> </div> ) })

See Also

Last updated on