Pitfalls
Common Pitfalls
Avoid these common mistakes when building with @wayvo-ai/core.
Store Method vs Hook Usage
❌ Don't Call Store Methods During Render
Store methods return values directly but do not subscribe to changes. When called during render, the component won't re-render when data changes.
// ❌ Wrong - won't re-render when rows change
function MyComponent({ store }: Props) {
const rows = store.rows(); // Non-reactive!
const isLoading = store.isLoading(); // Non-reactive!
return <div>{rows.length} items</div>;
}
// ✅ Correct - subscribes to changes
function MyComponent({ store }: Props) {
const rows = useDBRows(store);
const isLoading = useIsStoreLoading(store);
return <div>{rows.length} items</div>;
}
Exception: Store methods ARE safe in event handlers, useEffect, and async functions.
Dialog Initialization
❌ Don't Initialize Store in useEffect
// ❌ Wrong - causes timing issues
useEffect(() => {
if (open) store.createNew({ ... });
}, [open]);
// ✅ Correct - initialize before opening
const handleAdd = () => {
store.createNew({ ... });
setOpen(true);
};
Query After Save
❌ Don't Call executeQuery or refresh After Save
// ❌ Wrong - save auto-refreshes
await store.save();
await store.executeQuery();
// ❌ Also wrong
await store.save();
await store.refresh();
// ✅ Correct - just save
await store.save({ feedback: 'Saved successfully' });
Form Fields
❌ Don't Use useCurrentRow for Form Fields
// ❌ Causes cursor jumping in inputs
const row = useCurrentRow(store);
// ✅ Use sync mode
const row = useCurrentRowSync(store);
Table Columns
❌ Don't Access row.original Directly
// ❌ Wrong - row.original only contains { id: "..." }
cell: ({ row }) => {
const name = row.original.name; // undefined!
return <span>{name}</span>;
}
// ✅ Correct - use TableCell
cell: (props) => <TableCell type="Text" attributeCode="name" {...props} />
// ✅ Alternative - use useRowValue
function NameCell({ store, rowId }: { store: Store<Entity>; rowId: string }) {
const name = useRowValue(store, rowId, 'name');
return <span>{name || '-'}</span>;
}
Query Deduplication
❌ Don't Add Manual Query Guards
The store handles query deduplication internally:
// ❌ Wrong - unnecessary guards
useEffect(() => {
if (row?.projectId === projectId) return; // Redundant
store.executeQuery({ query: { match: { projectId } } });
}, [projectId, store, row?.projectId]);
// ✅ Correct - just call executeQuery
useEffect(() => {
store.executeQuery({ query: { match: { projectId } } });
}, [projectId, store]);
Multiple useEffects
❌ Don't Have Multiple useEffects Calling executeQuery
// ❌ Wrong - both effects run on mount
useEffect(() => {
if (customerId) store.setSmartSearchFilters([...]);
store.executeQuery();
}, [customerId, store]);
useEffect(() => {
store.executeQuery(); // Duplicate!
}, [includeArchived, store]);
// ✅ Correct - single effect
useEffect(() => {
if (customerId) store.setSmartSearchFilters([...]);
store.executeQuery();
}, [customerId, includeArchived, store]);
Date Handling
❌ Don't Use Local Timezone Methods
// ❌ Wrong - timezone shifts the date
const formatted = new Date(date).toLocaleDateString();
const formatted = format(new Date(date), 'MMM dd, yyyy');
// ✅ Correct - use UTC methods or utilities
const formatted = formatDateDisplay(date);
Click-Outside with Radix UI
❌ Don't Forget Portal Elements
When building click-outside handlers with Radix UI components:
// ❌ Wrong - dropdown closes when clicking options
function handleClickOutside(e: MouseEvent) {
if (containerRef.current && !containerRef.current.contains(e.target as Node)) {
handleCancel(); // Fires when clicking dropdown options!
}
}
// ✅ Correct - check for Radix portals
function handleClickOutside(e: MouseEvent) {
if (containerRef.current?.contains(e.target as Node)) return;
const target = e.target as HTMLElement;
if (target.closest('[data-radix-popper-content-wrapper]') || target.closest('[role="listbox"]')) {
return;
}
handleCancel();
}
Store Invalidation
❌ Don't Invalidate All Stores for a DataSource
// ❌ Wrong - invalidates ALL stores (list AND detail)
invalidateStoresOnSuccess: ['WKProjects']
// ✅ Correct - only invalidate specific store
invalidateStoresOnSuccess: [{ datasourceId: 'WKProjects', alias: 'wk-projects-all' }]
Next Steps
- Best Practices - Follow best practices
- Examples - See working examples