Example
In this example, we will build a basic TODO app to highlight the basic functionality of Trigger. The final product is shown below. Click the green icon to complete a task and the red icon to activate a task.
Owner Name | # Active Tasks | # Completed Tasks |
---|
Owner Name | Description |
---|
Owner Name | Item Description |
---|
CREATE TYPES FOR OUR STORE
First, we import the generic types from the library and then define the types we will use in our application. These types will provide many conveniences when interacting with your store throughout your application. Starting by creating a file for your store (e.g., store.ts) and adding the following types:
store.tsimport { type Store, type Single, type Table } from '@datahook/trigger';
type TaskOwner = {
ownerID: number;
firstName: string;
lastName: string;
};
type Task = {
ownerID: number;
description: string;
};
interface MyStore extends Store {
tables: {
taskOwners: Table<TaskOwner>;
activeTasks: Table<Task>;
completedTasks: Table<Task>;
},
singles: {
initialLoad: Single<boolean>;
}
};
Note: We are using a single called initialLoad to ensure we don't load data into our store multiple times
INSTANTIATE OUR STORE
With our types declared, we now instantitate our store and export it for use throughout our application. You can do this in the same file where you declared your types. Start by importing the CreateTable and extract convenience functions from the library, then creating a property called tables in your store object with empty arrays to hold your data.
store.tsimport { extract, CreateTable, type Single, type Store, type Table } from '@datahook/trigger';
const s: MyStore = {
tables: {
taskOwners: CreateTable<TaskOwner>(['ownerID', 'firstName', 'lastName']),
activeTasks: CreateTable<Task>(['ownerID', 'description']),
completedTasks: CreateTable<Task>(['ownerID', 'description']),
},
singles: {
initialLoad: CreateSingle(true),
}
};
/*** EXTRACT AND EXPORT OUR STORE */
export const { tables, singles } = extract(s);
The final version of our store is shown below, and is now ready for use in our application.
store.tsimport { extract, CreateTable, type Store, type Single, type Table, CreateSingle } from '@datahook/trigger';
type TaskOwner = {
ownerID: number;
firstName: string;
lastName: string;
};
type Task = {
ownerID: number;
description: string;
};
interface MyStore extends Store {
tables: {
taskOwners: Table<TaskOwner>;
activeTasks: Table<Task>;
completedTasks: Table<Task>;
},
singles: {
initialLoad: Single<boolean>;
}
};
const s: MyStore = {
tables: {
taskOwners: CreateTable<TaskOwner>(['ownerID', 'firstName', 'lastName']),
activeTasks: CreateTable<Task>(['ownerID', 'description']),
completedTasks: CreateTable<Task>(['ownerID', 'description']),
},
singles: {
initialLoad: CreateSingle(true),
}
};
/*** EXTRACT AND EXPORT OUR STORE */
export const { tables, singles } = extract(s);
CREATE OUR COMPONENTS
Note: For brevity, not all imports are shown (e.g., React, styling, etc.)app.tsx
import { tables, singles } from './store';
import TaskOwners from './taskOwners';
import ActiveTasks from './activeTasks';
import CompletedTasks from './completedTasks';
export default function App() {
const initialLoad = singles.initialLoad.get();
useLayoutEffect(() => {
if (initialLoad) {
singles.initialLoad.set(false);
// Seed our owners
tables.taskOwners.insertMany([
{
ownerID: 1,
firstName: 'Bill',
lastName: 'Gates',
},
{
ownerID: 2,
firstName: 'Steve',
lastName: 'Jobs',
},
{
ownerID: 3,
firstName: 'Ada',
lastName: 'Lovelace',
},
{
ownerID: 4,
firstName: 'Alan',
lastName: 'Turing',
},
]);
// Create our active tasks
tables.activeTasks.insertMany([
{
ownerID: 1,
description: 'Invent Internet Explorer',
},
{
ownerID: 2,
description: 'Invent iPhone',
},
{
ownerID: 3,
description: 'Invent programming',
},
{
ownerID: 4,
description: 'Invent intelligent machines',
},
]);
}
}, []);
return (
<div className={app}>
<TaskOwners />
<ActiveTasks />
<CompletedTasks />
</div>
)
}
import { tables } from './store';
// Will show how may active and completed tasks for each owner
export default function TaskOwners() {
const activeTasks = tables.activeTasks.use(null, ['onDelete', 'onInsert']);
const completedTasks = tables.completedTasks.use(null, ['onDelete', 'onInsert']);
const owners = tables.taskOwners.use();
return (
<div className={table}>
<div className={tableHeader}>Owners</div>
<div className={tableContainer}>
<table className={tableBody}>
<thead>
<tr>
<th>Owner Name</th>
<th># Active Tasks</th>
<th># Completed Tasks</th>
</tr>
</thead>
<tbody>
{owners.map(o =>
<tr key={o._id}>
<td>{o.firstName} {o.lastName}</td>
<td>{activeTasks.filter(d => d.ownerID === o.ownerID).length}</td>
<td>{completedTasks.filter(d => d.ownerID === o.ownerID).length}</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
)
}
import { tables } from './store';
// Will show the active tasks
export default function ActiveTasks() {
const tasks = tables.activeTasks.use();
const handleCompleteTask = (_id: number) => {
const task = tables.activeTasks.findById(_id);
if (task) {
tables.activeTasks.deleteById(_id);
tables.completedTasks.insertOne({ ownerID: task.ownerID, description: task.description });
}
}
return (
<div className={table}>
<div className={tableHeader}>Active Tasks</div>
<div className={tableContainer}>
<table className={tableBody}>
<thead>
<tr>
<th>Owner Name</th>
<th>Description</th>
<th></th>
</tr>
</thead>
<tbody>
{tasks.map(t =>
<tr key={t._id}>
<td>{tables.taskOwners.findOne({ ownerID: t.ownerID})!.firstName} {tables.taskOwners.findOne({ ownerID: t.ownerID})!.lastName}</td>
<td>{t.description}</td>
<td className={icon} title="Click to complete task" onClick={() => handleCompleteTask(t._id)}><CheckCircle color="success" fontSize="large"/></td>
</tr>
)}
</tbody>
</table>
</div>
</div>
)
}
import { tables } from './store';
// Will show the completed tasks
export default function CompletedTasks() {
const tasks = tables.completedTasks.use();
const handleActivateTask = (_id): number) => {
const task = tables.completedTasks.findById(_id);
if (task) {
tables.completedTasks.deleteById(_id);
tables.activeTasks.insertOne({ ownerID: task.ownerID, description: task.description });
}
}
return (
<div className={table}>
<div className={tableHeader}>Completed Tasks</div>
<div className={tableContainer}>
<table className={tableBody}>
<thead>
<tr>
<th>Owner Name</th>
<th>Item Description</th>
<th></th>
</tr>
</thead>
<tbody>
{tasks.map(t =>
<tr key={t._id}>
<td>{tables.taskOwners.findOne({ ownerID: t.ownerID})!.firstName} {tables.taskOwners.findOne({ ownerID: t.ownerID})!.lastName}</td>
<td>{t.description}</td>
<td className={icon} title="Click to activate task" onClick={() => handleActivateTask(t._id)}><Cancel color="error" fontSize="large"/></td>
</tr>
)}
</tbody>
</table>
</div>
</div>
)
}