Building a React Native App with Offline Storage

Imagine having a mobile app that can store data locally, without relying on an internet connection. Sounds appealing, right? In this article, we’ll explore how to build a React Native app that allows users to track their weight and visualize their progress, all while storing data offline.

Getting Started

Before we dive into the project, make sure you have React Native set up on your machine. If you’re new to React Native, follow the official environment setup documentation to get started. Once you’ve set up your machine, create a new React Native project using the command npx react-native init weightress.

Database Setup

For offline storage, we’ll use WatermelonDB, a powerful and easy-to-use database solution for React Native apps. Install WatermelonDB using Yarn by running yarn add @nozbe/watermelondb. Then, create a new folder named data and add a file called schema.ts with the following code:
“`ts
import { schema } from ‘@nozbe/watermelondb’;

const weightsSchema = schema({
name: ‘weights’,
columns: [
{ name: ‘weight’, type: ‘number’ },
{ name: ‘note’, type: ‘tring’, isOptional: true },
{ name: ‘createdat’, type: ‘number’ },
],
indexes: [
{ name: ‘note
index’, columns: [‘note’] },
{ name: ‘createdatindex’, columns: [‘created_at’] },
],
});

export default weightsSchema;

This schema defines a
weightstable with three columns:weight,note, andcreated_at`. We’ll use this schema to store user input data.

Model and Database Instance

Next, create a new file called weight.ts in the data folder with the following code:
“`ts
import { Model } from ‘@nozbe/watermelondb’;
import { weightsSchema } from ‘./schema’;

class Weight extends Model {
static table = ‘weights’;

@date(‘created_at’)
createdAt!: number;

weight!: number;
note?: string;
}

export default Weight;

This file defines a
Weightmodel that maps to theweights` table in our schema.

Create another file called database.ts in the data folder with the following code:
“`ts
import { Database } from ‘@nozbe/watermelondb’;
import { weightsSchema } from ‘./schema’;
import Weight from ‘./weight’;

const database = new Database({
schema: weightsSchema,
models: [Weight],
});

export default database;
“`
This file creates a new database instance using our schema and model.

Data Helpers

Create a new file called helpers.ts in the data folder with the following code:
“`ts
import { database } from ‘./database’;
import Weight from ‘./weight’;

const observeWeights = () => {
return database.collections.get(‘weights’).query();
};

const saveWeight = (weight: number, note?: string) => {
return database.write(() => {
const weightEntry = new Weight({
weight,
note,
});
database.collections.get(‘weights’).create(weightEntry);
});
};

export { observeWeights, saveWeight };

This file defines two helper functions:
observeWeightsreturns a query that observes theweightstable, andsaveWeight` saves a new weight entry to the database.

User Interface

Now that we have our database set up, let’s create the user interface for our app. Create a new folder called components and add three files: header.tsx, creator.tsx, and chart.tsx.

Header Component

In header.tsx, add the following code:
“`jsx
import React from ‘eact’;
import { View, Text, TouchableOpacity } from ‘eact-native’;

const Header = () => {
return (




);
};

const styles = {
container: {
flex: 1,
justifyContent: ‘pace-between’,
alignItems: ‘center’,
padding: 16,
},
title: {
fontSize: 24,
fontWeight: ‘bold’,
},
addButton: {
fontSize: 24,
color: ‘#007AFF’,
},
};

export default Header;
“`
This component displays a header with a title and an add button.

Creator Component

In creator.tsx, add the following code:
“`jsx
import React, { useState } from ‘eact’;
import { View, Text, TextInput, TouchableOpacity } from ‘eact-native’;

const Creator = () => {
const [weight, setWeight] = useState(”);
const [note, setNote] = useState(”);
const [isSaving, setIsSaving] = useState(false);

const handleSavePress = async () => {
setIsSaving(true);
try {
await saveWeight(weight, note);
setIsSaving(false);
} catch (error) {
console.error(error);
}
};

return (






);
};

const styles = {
container: {
flex: 1,
padding: 16,
},
header: {
fontSize: 24,
fontWeight: ‘bold’,
},
input: {
height: 40,
borderColor: ‘#ccc’,
borderWidth: 1,
padding: 10,
marginBottom: 16,
},
saveButton: {
fontSize: 24,
color: ‘#007AFF’,
},
};

export default Creator;
“`
This component displays a form with two input fields and a save button.

Chart Component

In chart.tsx, add the following code:
“`jsx
import React from ‘eact’;
import { View, Text } from ‘eact-native’;
import { LineChart } from ‘eact-native-chart-kit’;

const Chart = ({ weights }) => {
const data = weights.map((weight) => weight.weight);
const labels = weights.map((weight) => weight.createdAt);

return (
rgba(0, 0, 0, ${opacity}),
}}
/>

);
};

const styles = {
container: {
flex: 1,
padding: 16,
},
};

export default Chart;

This component displays a line chart using the
react-native-chart-kit` library.

Putting it all Together

Finally, open up App.tsx and add the following code:
“`jsx
import React from ‘eact’;
import { View, Text, ScrollView } from ‘eact-native’;
import Header from ‘./components/Header’;
import Creator from ‘./components/Creator’;
import Chart from ‘./components/Chart’;
import { observeWeights } from ‘./data/helpers’;

const App = () => {
const [showCreator, setShowCreator] = useState(false);
const weights = observeWeights();

return (

{showCreator && }


);
};

const styles = {
container: {
flex: 1,
padding: 16,
},
};

export default App;

This component renders the header, creator, and chart components, and observes the
weightstable using theobserveWeights` helper function.

That’s it! You now have a React Native app that allows users to track their weight and visualize their progress, all while storing data offline using WatermelonDB.

Leave a Reply

Your email address will not be published. Required fields are marked *