Skip to main content

On-Device Storage Example

This example demonstrates how to use RADFish's storage system with IndexedDB for on-device data persistence. It showcases the Application and Collection patterns for managing structured data with schema validation.

Key RADFish Concepts

  • Application Instance: Configured with stores and collections
  • Schema-based Storage: Type-safe data validation
  • Collection API: Consistent CRUD operations
  • IndexedDB Integration: Automatic persistence with offline support

Use cases include:

  • Offline on-device data storage (most cases)
  • Local non-relational database (online or offline use)
  • Form data persistence across sessions
  • Structured data with validation

Learn more about RADFish examples at the official documentation. Refer to the RADFish GitHub repo for more information and code samples.

Preview

This example will render as shown in this screenshot:

On-Device Storage

Steps

1. Configure RADFish Application with Schema

In the index.jsx file, define your Application with stores and schemas:

import { Application } from "@nmfs-radfish/radfish";
import { IndexedDBConnector } from "@nmfs-radfish/radfish/storage";

const app = new Application({
stores: {
fishingData: {
connector: new IndexedDBConnector("on-device-storage-app"),
collections: {
formData: {
schema: {
fields: {
id: { type: "string", primaryKey: true },
fullName: { type: "string" },
email: { type: "string" },
phoneNumber: { type: "string" },
numberOfFish: { type: "number" },
species: { type: "string" },
computedPrice: { type: "number" },
isDraft: { type: "boolean" },
},
},
},
species: {
schema: {
fields: {
id: { type: "string", primaryKey: true },
name: { type: "string" },
price: { type: "number" },
},
},
},
},
},
},
});

Key schema concepts:

  • Primary Key: Each collection must have a field marked as primaryKey: true
  • Field Types: Ensures data validation (string, number, boolean)
  • Multiple Collections: Organize related data in separate collections

2. Initialize and Provide the Application Instance

The Application waits for stores to be ready before rendering:

const root = ReactDOM.createRoot(document.getElementById("root"));

app.on("ready", () => {
root.render(
<ErrorBoundary>
<React.StrictMode>
<App application={app} />
</React.StrictMode>
</ErrorBoundary>,
);
});

The App component wraps children with the Application context provider:

const App = ({ application }) => {
return (
<Application application={application}>
<div className="App grid-container">
<Router>
<Routes>
<Route path="/" element={<HomePage />} />
</Routes>
</Router>
</div>
</Application>
);
};

Using Collections in Components

Access Collections with useApplication Hook

In any component within the Application context, access collections using the useApplication hook:

import { useApplication } from "@nmfs-radfish/react-radfish";

const HomePage = () => {
const application = useApplication();
const formDataCollection = application.stores.fishingData.getCollection("formData");

// Use collection methods...
};

Collection API Methods

Collections provide the following methods for data operations:

Create

const newData = {
id: crypto.randomUUID(),
fullName: "John Doe",
species: "Tuna",
numberOfFish: 3,
computedPrice: 150,
isDraft: false
};

await formDataCollection.create(newData);

Find

// Find all records
const allData = await formDataCollection.find();

// Find with criteria
const draftRecords = await formDataCollection.find({ isDraft: true });

Update

const updatedData = {
id: "existing-id",
fullName: "Jane Doe",
numberOfFish: 5,
// ... other fields
};

await formDataCollection.update(updatedData);

Delete

// Delete by ID
await formDataCollection.delete({ id: "record-id" });

All methods are type-safe and validate data against the schema defined in your Application configuration.