DEV Community

Ajmal Hasan
Ajmal Hasan

Posted on

Building a React Native App with Realm Database

Introduction

When working with React Native applications, managing local data efficiently is crucial. Realm is a high-performance mobile database that offers a seamless experience for data storage and retrieval. In this blog, we will explore how to integrate Realm into a React Native project using @realm/react to create, read, update, and delete data.


Setting Up the Project

To get started, install the necessary dependencies:

yarn add realm @realm/react
Enter fullscreen mode Exit fullscreen mode

Implementing Realm in React Native

1. Wrapping the App with RealmProvider

To integrate Realm, wrap the entire app with RealmProvider. This ensures that the database schema is accessible throughout the application.

import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { RealmProvider } from '@realm/react';
import CreateScreen from './screens/CreateScreen';
import { Person } from './models/Person';
import { Car } from './models/Car';
import { Address } from './models/Address';

const Tab = createBottomTabNavigator();

const App = () => {
  return (
    <RealmProvider deleteRealmIfMigrationNeeded schema={[Person, Car, Address]}>
      <NavigationContainer>
        <Tab.Navigator>
          <Tab.Screen name="Create" component={CreateScreen} />
        </Tab.Navigator>
      </NavigationContainer>
    </RealmProvider>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

2. Understanding deleteRealmIfMigrationNeeded

The deleteRealmIfMigrationNeeded option in Realm is a setting that allows developers to automatically delete the existing Realm database if a schema change is detected. This is useful during development when frequent schema changes occur, preventing migration-related crashes.


3. Defining and Joining Database Models

Realm operates with schemas that define the structure of stored data. Let's create models for Person, Car, and Address and see how they are joined.

Person Model

import { Realm } from 'realm';

export class Person extends Realm.Object {
  static schema = {
    name: 'Person',
    primaryKey: '_id',
    properties: {
      _id: 'objectId',
      name: 'string',
      age: 'int?',
      date: {
        type: 'date',
        default: () => new Date(),
      },
      cars: {
        type: 'list',
        objectType: 'Car',
      },
      address: 'Address',
    },
  };
}
Enter fullscreen mode Exit fullscreen mode

Car Model

import { Realm } from 'realm';

export class Car extends Realm.Object {
  static schema = {
    name: 'Car',
    primaryKey: '_id',
    properties: {
      _id: 'objectId',
      c_name: 'string',
      c_color: 'string',
      date: {
        type: 'date',
        default: () => new Date(),
      },
    },
  };
}
Enter fullscreen mode Exit fullscreen mode

Address Model

import { Realm } from 'realm';

export class Address extends Realm.Object {
  static schema = {
    name: 'Address',
    embedded: true,
    properties: {
      country: 'string?',
    },
  };
}
Enter fullscreen mode Exit fullscreen mode

How Schemas Are Joined in Realm

Realm supports relationships between objects. In our case:

  • The Person model has a one-to-many relationship with Car (cars is an array of Car objects).
  • The Person model has a one-to-one relationship with Address (address is a single Address object).
  • Embedded objects, like Address, are stored directly inside their parent object rather than being separate entries in the database.

When creating a Person entry, you can assign multiple Car objects and an Address object, effectively linking them together.

Example of creating a Person with related Car and Address:

realm.write(() => {
  const car1 = realm.create('Car', { _id: new Realm.BSON.ObjectID(), c_name: 'Toyota Camry', c_color: 'Red' });
  const car2 = realm.create('Car', { _id: new Realm.BSON.ObjectID(), c_name: 'Honda Accord', c_color: 'Blue' });

  realm.create('Person', {
    _id: new Realm.BSON.ObjectID(),
    name: 'John Doe',
    age: 30,
    cars: [car1, car2],
    address: { country: 'USA' },
  });
});
Enter fullscreen mode Exit fullscreen mode

Usage

import { Realm } from 'realm';
import { useObject, useQuery, useRealm } from '@realm/react';
import React, { useState } from 'react';
import {
  View,
  TextInput,
  Button,
  StyleSheet,
  FlatList,
  Text,
  ScrollView,
  TouchableOpacity,
} from 'react-native';
import { Person } from '../models/Person';
import { Car } from '../models/Car';

const CreateScreen = () => {
  const realm = useRealm();
  const persons = useQuery(Person); // to get all list in array
  // const myTask = useObject(Task, _id); // to get specific item.object
  const cars = useQuery(Car);

  const [userName, setUserName] = useState('');
  const [age, setAge] = useState('');

  const handleUserNameChange = (text) => {
    setUserName(text);
  };

  const handleAgeChange = (text) => {
    setAge(+text);
  };

  const addSampleCars = () => {
    realm.write(() => {
      realm.create('Car', {
        _id: new Realm.BSON.ObjectID(),
        c_name: 'Toyota Camry',
        c_color: 'Red',
      });
      realm.create('Car', {
        _id: new Realm.BSON.ObjectID(),
        c_name: 'Honda Accord',
        c_color: 'Blue',
      });
      realm.create('Car', {
        _id: new Realm.BSON.ObjectID(),
        c_name: 'Ford Mustang',
        c_color: 'Yellow',
      });
    });
  };

  const handleSubmit = () => {
    addSampleCars();
    realm.write(() => {
      realm.create('Person', {
        _id: new Realm.BSON.ObjectID(),
        name: userName,
        age,
        cars,
        address: {
          country: 'India',
        },
      });
    });
    setUserName('');
    setAge('');
  };

  const onPressDelete = (item) => {
    realm.write(() => {
      realm.delete(item);
    });
  };

  const onPressItem = (person) => {
    realm.write(() => {
      // Retrieve the person object using the primary key
      const userToUpdate = realm.objectForPrimaryKey('Person', person._id);

      // Increment the age by 1
      if (userToUpdate) {
        userToUpdate.age += 1;
      }
    });

    /* 1. FINDING EXAMPLE BASED ON QUERY
      // filter for tasks that have just-started or short-running progress
  const lowProgressTasks = useQuery(Task, tasks => {
    return tasks.filtered(
      '$0 <= progressMinutes && progressMinutes < $1',
      1,
      10,
    );
  });

    // retrieve the set of Task objects
  const tasks = useQuery(Task);

  // Sort tasks by name in ascending order
  const tasksByName = useQuery(Task, tasks => {
    return tasks.sorted('name');
  });

  // Sort tasks by name in descending order
  const tasksByNameDescending = useQuery(Task, tasks => {
    return tasks.sorted('name', true);
  });

  // Sort tasks by priority in descending order and then by name alphabetically
  const tasksByPriorityDescendingAndName = useQuery(Task, tasks => {
    return tasks.sorted([
      ['priority', true],
      ['name', false],
    ]);
  });

  // Sort Tasks by Assignee's name.
  const tasksByAssigneeName = useQuery(Task, tasks => {
    return tasks.sorted('assignee.name');
  });
    */

    /**
     * 2. UPDATE OR INSERT EG.
     realm.write(() => {
    // Add a new Task to the realm. Since no task with ID 1234
    // has been added yet, this adds the instance to the realm.
    myTask = realm.create(
      'Task',
      {_id: 1234, name: 'Wash the car', progressMinutes: 0},
      'modified',
    );
    // If an object exists, setting the third parameter (`updateMode`) to
    // "modified" only updates properties that have changed, resulting in
    // faster operations.
    myTask = realm.create(
      'Task',
      {_id: 1234, name: 'Wash the car', progressMinutes: 5},
      'modified',
    );
  });
     */
  };

  console.log('personspersonspersons', JSON.stringify(persons));

  return (
    <View style={{ alignItems: 'center', flex: 1, justifyContent: 'center' }}>
      <ScrollView style={styles.container}>
        <View style={styles.inputContainer}>
          <TextInput
            style={styles.input}
            placeholder="Enter your name"
            value={userName}
            onChangeText={handleUserNameChange}
          />
          <TextInput
            style={styles.input}
            placeholder="Enter your age"
            value={age}
            onChangeText={handleAgeChange}
            keyboardType="numeric"
          />
          <Button title="Add User" onPress={handleSubmit} />
        </View>
        <FlatList
          data={persons}
          scrollEnabled={false}
          renderItem={({ item }) => (
            <TouchableOpacity onPress={() => onPressItem(item)} style={styles.userItem}>
              <Text>{item.name}</Text>
              <Text>{item.age}</Text>
              <Text onPress={() => onPressDelete(item)} style={{ fontWeight: 'bold' }}>
                X
              </Text>
            </TouchableOpacity>
          )}
          keyExtractor={(item, index) => index.toString()}
        />
      </ScrollView>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    paddingTop: 10,
  },
  inputContainer: {
    marginBottom: 20,
  },
  input: {
    borderWidth: 1,
    borderColor: '#ccc',
    borderRadius: 5,
    padding: 10,
    marginBottom: 10,
    width: 200,
  },
  list: {
    alignItems: 'center',
  },
  userItem: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    borderWidth: 1,
    borderColor: '#ccc',
    borderRadius: 5,
    padding: 10,
    marginBottom: 10,
    width: '90%',
  },
});

export default CreateScreen;
Enter fullscreen mode Exit fullscreen mode

Summary

In this blog, we covered:

  • Setting up Realm with @realm/react
  • Understanding deleteRealmIfMigrationNeeded for handling schema changes
  • Defining and joining schemas (Person, Car, Address)
  • Using relationships to structure data effectively
  • Implementing CRUD operations to manage local data
  • Understanding embedded objects for optimal schema design

Realm simplifies database management in React Native, making it a powerful choice for applications requiring offline capabilities and real-time performance. Start integrating it into your projects today!

PROJECT REPO->

Top comments (0)