Examples
Complex Workflow Example
Example of a complex workflow with multiple stores, related data, and coordinated operations.
Scenario
A project management system where:
- Projects have multiple tasks
- Tasks have assignments
- Creating a project creates default tasks
- Deleting a project cascade-deletes tasks
Project Store
// hooks/use-project-store.ts
export function useProjectStore() {
return useStore<Project>({
datasourceId: 'WKProjects',
page: 'project-page',
alias: 'project-list',
limit: 20,
includeCount: true,
autoQuery: true,
sort: { projectName: 1 },
});
}
Tasks Store (Related Data)
// hooks/use-project-tasks-store.ts
export function useProjectTasksStore(projectId: string) {
return useStore<WKProjectTasks>({
datasourceId: 'WKProjectTasks',
page: 'project-tasks-page',
alias: `project-tasks-${projectId}`,
limit: 100,
autoQuery: false,
match: { projectId },
sort: { taskName: 1 },
});
}
Coordinated Operations
Create Project with Default Tasks
const handleCreateProject = useCallback(async () => {
// Create project
projectStore.createNew({
partialRecord: {
projectName: 'New Project',
status: 'draft',
},
});
await projectStore.save({ feedback: 'Project created' });
const projectId = projectStore.currentRowId();
if (projectId) {
// Create default tasks
const defaultTasks = [
{ taskName: 'Task 1', projectId },
{ taskName: 'Task 2', projectId },
];
for (const task of defaultTasks) {
tasksStore.createNew({ partialRecord: task });
}
await tasksStore.save({ feedback: 'Default tasks created' });
}
}, [projectStore, tasksStore]);
Cascade Delete
const handleDeleteProject = useCallback(async (project: Project) => {
const confirmed = await confirmWithUser({
title: 'Delete Project',
content: `Are you sure? This will delete all tasks and assignments.`,
});
if (confirmed) {
// Delete project (cascade deletes tasks via afterDelete hook)
projectStore.deleteRow(project.projectId);
await projectStore.save({ feedback: 'Project deleted' });
// Refresh tasks store (tasks were cascade-deleted by DB)
tasksStore.refresh();
}
}, [projectStore, tasksStore]);
Store Invalidation
// When project is saved, refresh related stores
const projectStore = useStore<Project>({
datasourceId: 'WKProjects',
alias: 'project-list',
invalidateStoresOnSave: [
{ datasourceId: 'WKProjectTasks', alias: 'project-tasks-all' },
],
});
Multi-Store Form
function ProjectForm({ projectStore, tasksStore }: Props) {
const project = useCurrentRowSync(projectStore);
const tasks = useDBRows(tasksStore);
return (
<div className="grid gap-6">
{/* Project fields */}
<div className="grid gap-4">
<TextInput
label="Project Name"
value={project?.projectName || ''}
onChange={(value) => projectStore.setValue('projectName', value)}
/>
</div>
{/* Tasks list */}
<div>
<h3>Tasks</h3>
<ul>
{tasks.map((task) => (
<li key={task._id}>{task.taskName}</li>
))}
</ul>
</div>
</div>
);
}
Key Patterns
- Separate stores for related data - Use different aliases
- Store invalidation - Refresh related stores on save
- Cascade operations - Handle in DataSource hooks
- Coordinated saves - Save parent, then children
Next Steps
- Stores - Learn more about store management
- DataSources - Lifecycle hooks