Best Practices
Performance Best Practices
Optimize your application performance with these tips.
Store Configuration
Use select for Lookup Stores
Only fetch fields you need:
// ✅ Good - only ID and display name
const store = useStore<Customer>({
datasourceId: 'Customers',
alias: 'customer-options',
select: ['customerId', 'customerName'],
limit: 1000,
});
// ❌ Bad - fetches all fields
const store = useStore<Customer>({
datasourceId: 'Customers',
alias: 'customer-options',
limit: 1000,
});
Use filterLocally for Small Static Datasets
When you've loaded all data:
const store = useStore<Status>({
datasourceId: 'StatusLookup',
alias: 'status-combobox',
limit: 100, // Load all statuses
autoQuery: true,
filterLocally: true, // Filter client-side, no DB round-trips
});
Appropriate limit Values
- List pages:
20(standard pagination) - Lookup stores:
1000(for all options) - Detail pages:
1(single record) - Export operations:
5000+(if needed)
Query Optimization
Avoid Unnecessary Queries
The store handles query deduplication automatically:
// ✅ Good - store handles deduplication
useEffect(() => {
store.executeQuery({ query: { match: { projectId } } });
}, [projectId, store]);
// ❌ Bad - unnecessary guards
useEffect(() => {
if (row?.projectId === projectId) return; // Redundant
store.executeQuery({ query: { match: { projectId } } });
}, [projectId, store, row?.projectId]);
Consolidate Multiple useEffects
// ❌ Bad - duplicate queries on mount
useEffect(() => {
if (customerId) store.setSmartSearchFilters([...]);
store.executeQuery();
}, [customerId, store]);
useEffect(() => {
store.executeQuery(); // Also runs on mount!
}, [includeArchived, store]);
// ✅ Good - single effect
useEffect(() => {
if (customerId) store.setSmartSearchFilters([...]);
store.executeQuery();
}, [customerId, includeArchived, store]);
Component Optimization
Use useMemo for Column Definitions
export default function useTableColumns(store: Store<Entity>) {
return useMemo(
() => [
// column definitions
],
[store],
);
}
Use useMemo for Options
export default function useSmartSearchColumns() {
const { rows: customerOptions } = useCustomerOptions();
return useMemo(
() => [
{
key: 'customerId',
options: customerOptions,
// ...
},
],
[customerOptions],
);
}
Store Sharing
Share Stores Across Components
Stores with the same key are automatically shared:
// ✅ Good - both use the same store instance
const tableStore = useEntityStore();
const formStore = useEntityStore(); // Same instance!
// ❌ Bad - creates separate stores
const tableStore = useStore({ alias: 'entity-list', ... });
const formStore = useStore({ alias: 'entity-form', ... }); // Different!
Auto-Refresh
Use autoRefresh sparingly - only for data that needs real-time updates:
// ✅ Good - notifications need real-time updates
const store = useStore<Notifications>({
datasourceId: 'Notifications',
alias: 'notifications-list',
autoRefresh: true, // Subscribe to SSE
});
// ❌ Bad - static lookup data doesn't need auto-refresh
const store = useStore<Status>({
datasourceId: 'StatusLookup',
alias: 'status-combobox',
autoRefresh: true, // Unnecessary
});
Next Steps
- Code Organization - Structure your codebase
- Pitfalls - Common mistakes to avoid