React Hooks
Gluonic provides React hooks for accessing synced data in your components.
Core Hooks
useModel()
Get a single model by ID:
import { observer, useModel } from '@gluonic/client'
import { Post } from '../models'
const PostDetail = observer(({ postId }: { postId: string }) => {
const post = useModel<Post>('post', postId)
if (!post) {
return <div>Loading...</div>
}
return (
<div>
<h1>{post.title}</h1>
<p>{post.content}</p>
</div>
)
})Returns: Model instance or undefined if not loaded.
API Reference: useModel - Complete hook documentation
useCollectionModels()
Get all models of a type, optionally filtered:
const PostList = observer(() => {
// All posts
const allPosts = useCollectionModels<Post>('post')
// Filtered posts
const published = useCollectionModels<Post>(
'post',
post => post.published === true
)
return (
<div>
{published.map(post => (
<PostCard key={post.id} post={post} />
))}
</div>
)
})Returns: Array of model instances.
Filter function:
(model: T) => boolean
// Examples:
post => post.published
user => user.email.includes('@gmail.com')
comment => comment.createdAt > Date.now() - 86400000 // Last 24hAPI Reference: useCollectionModels - Filtering and performance
useStore()
Access the store directly:
const PostEditor = observer(() => {
const store = useStore()
const handleSave = async () => {
await store.save('post', postId, { title, content })
}
return <button onClick={handleSave}>Save</button>
})API Reference: useStore - Store methods
useConnectionState()
Monitor sync and network state:
const SyncStatus = observer(() => {
const {
isOnline, // Network available
isSyncing, // Currently syncing
queueLength, // Pending mutations
lastSync // Last sync timestamp
} = useConnectionState()
return (
<div>
{!isOnline && <Badge>Offline</Badge>}
{isSyncing && <Spinner />}
{queueLength > 0 && <Badge>{queueLength} pending</Badge>}
</div>
)
})API Reference: useConnectionState - All connection state properties
useGraphBridge()
Access GraphBridge for advanced operations:
const bridge = useGraphBridge()
// Get model
const post = bridge.getModel<Post>('post', '123')
// Invalidate cache
bridge.invalidate('post', '123')
// Ensure loaded
await bridge.ensureLoaded('post', '123')State Hooks
useInitializingSession()
Check if initial sync is in progress:
const App = observer(() => {
const initializing = useInitializingSession()
if (initializing) {
return <SplashScreen />
}
return <MainApp />
})Returns: true during bootstrap + queue replay.
useSyncState()
Get detailed sync state:
const {
bootstrapping,
fastForwarding,
replayingQueue,
catchingUp
} = useSyncState()Observer Pattern
Required: observer() HOC
Always wrap components that read model data:
// ✅ Correct
const Component = observer(() => {
const post = useModel<Post>('post', id)
return <h1>{post.title}</h1>
})
// ❌ Wrong - won't update!
const Component = () => {
const post = useModel<Post>('post', id)
return <h1>{post.title}</h1>
}Why?
observer() makes components reactive to MobX changes:
// Without observer
const Component = () => {
const post = useModel<Post>('post', '123')
return <h1>{post.title}</h1>
// Reads post.title once
// Never re-renders when title changes 💥
}
// With observer
const Component = observer(() => {
const post = useModel<Post>('post', '123')
return <h1>{post.title}</h1>
// MobX tracks: "Component depends on post:123.title"
// Re-renders when title changes ✓
})Common Patterns
Loading State
const PostDetail = observer(({ postId }) => {
const post = useModel<Post>('post', postId)
if (!post) {
return <ActivityIndicator />
}
return <PostContent post={post} />
})Empty State
const PostList = observer(() => {
const posts = useCollectionModels<Post>('post')
if (posts.length === 0) {
return <EmptyState />
}
return <FlatList data={posts} ... />
})Filtered Collections
const PublishedPosts = observer(() => {
const posts = useCollectionModels<Post>(
'post',
p => p.published && !p.archived
)
return <PostList posts={posts} />
})Multiple Filters
const [searchQuery, setSearchQuery] = useState('')
const [category, setCategory] = useState('all')
const filteredPosts = useCollectionModels<Post>(
'post',
post => {
const matchesSearch = post.title.toLowerCase().includes(searchQuery.toLowerCase())
const matchesCategory = category === 'all' || post.category === category
return matchesSearch && matchesCategory
}
)Lazy Relationships
const PostWithAuthor = observer(({ post }) => {
const author = post.author.value
if (!author) {
return <Text>Loading author...</Text>
}
return <Text>By: {author.name}</Text>
})Nested Data
const PostWithComments = observer(({ post }) => {
return (
<div>
<h1>{post.title}</h1>
{post.comments.map(comment => (
<div key={comment.id}>
<p>{comment.text}</p>
<small>By: {comment.author.value?.name}</small>
</div>
))}
</div>
)
})Performance Tips
Memoization
const PostAnalysis = observer(({ post }) => {
// Only recompute if post instance changes
const analysis = useMemo(() => {
return analyzePost(post) // Expensive operation
}, [post])
return <div>{analysis.summary}</div>
})Selective Observation
// Observe only specific fields
import { observer } from '@gluonic/client'
import { useLocalObservable } from 'mobx-react-lite' // Advanced MobX utils
const Component = observer(() => {
const post = useModel<Post>('post', id)
// Only re-render when title changes (not content)
return <h1>{post.title}</h1>
})Virtualized Lists
import { FlashList } from '@shopify/flash-list'
const PostList = observer(() => {
const posts = useCollectionModels<Post>('post')
return (
<FlashList
data={posts}
renderItem={({ item }) => <PostItem post={item} />}
estimatedItemSize={100}
/>
)
})Troubleshooting
Component doesn’t update
Problem: Data changes but UI doesn’t update.
Solution: Wrap with observer():
const Component = observer(() => { ... })“Can’t find model” errors
Problem: useModel() returns undefined.
Solutions:
- Ensure model is in bootstrap
- Check spelling of type name
- Verify model is registered:
warmUpModelRegistry([User, Post])
Infinite re-renders
Problem: Component renders continuously.
Solutions:
- Don’t mutate data during render
- Don’t call
store.save()during render - Use
useEffectfor side effects
Next Steps
- Mutations - Create, update, delete
- Relationships - Lazy loading
- API Reference - Complete hooks API