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.

Owners
Owner Name# Active Tasks# Completed Tasks
Active Tasks
Owner NameDescription
Completed Tasks
Owner NameItem 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.ts
import { 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: You can name your store file whatever you like, and you can have as many stores as you want to provide better localization of data in your application
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.ts
import { 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.ts
import { 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>
    )
}
taskOwners.tsx
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>
    )
}
activeTasks.tsx
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>
    )
}
completedTasks.tsx
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>
    )
}
Built with in Halifax, Nova Scotia by JW