React Native

Using the useParseQuery hook to build a real time React Native App

Introduction

In this guide, you will explore the main features of @parse/react-native lib using a ToDO React Native app example. You will use the useParseQuery hook to query tasks in real-time and store results locally on this App. Using various Parse Queries, you will discover how to use the new Parse Lib on your project.

Parse React Native is currently on the Alpha version. The lib is under testing, so we recommend to proceed with caution. Your feedback is very appreciated, so feel free to use the lib and send us your questions and first impressions by dropping an email to [email protected].

Goal

Explore the main use cases for the Parse React Native lib by creating a ToDo App.

Prerequisites

To complete this tutorial, you will need:

Step 1 - Setup the initial project

Before getting started, you will need to get the bootstrap React Native project that we have setup as a starting point for this tutorial. It is a simple react-native init project with all dependencies and styles pre-defined for you to focus on exploring @parse/react-native features. You may download de Zip or clone the project.

1
git clone https://github.com/templates-back4app/react-native-todo-app.git

Next, install the project dependencies:

1
2
3
4
5
6
7
cd react-native-todo-app

# Using yarn
yarn install

# Using npm
npm install

For iOS, install pods:

1
cd ios && npx pod-install

In the previous guide, Getting started, you learned how to use initializeParse to enable connection with Back4app servers. Set up your App Id and JavaScriptKey in the entry point component located at src/index.js:

1
2
3
4
5
6
// src/index.js
initializeParse(
'https://parseapi.back4app.com/',
'APPLICATION_ID',
'JAVASCRIPT_KEY'
);

Go ahead and run the project:

1
2
3
4
5
# For iOS
npx react-native run-ios

# For Android
npx react-native run-android

After that you will have successfully setup the starter project and the App will look like the following:

React Native Back4App

The project’s directory structure:

react-native-todo-app
├── src/
│   ├── config/
│   │   └── ReactotronConfig.js
│   ├── images/
│   │   ├── back4app_logo.png
│   │   └── bg.jpg
│   ├── components/
│   │   └── CardTodoButton/
│   │       └── index.js // card button item component
│   │   └── Menu/
│   │       └── index.js // menu with card buttons
│   │   └── TaskList/
│   │       └── index.js // List of tasks component
│   ├── pages/
│   │   └── AddTodo/
│   │       └── index.js // Add todo page  
│   │   └── Learning/
│   │       └── index.js // learning page  
│   │   └── Main/
│   │       └── index.js // main page
│   │   └── Shopping/
│   │       └── index.js // shopping page
│   │   └── Work/
│   │       └── index.js // work page
│   ├── services/
│   │   └── api.js
│   ├── index.js // App entrypoint
│   └── routes.js // navigation routes config
├── .editorconfig
├── .eslintrc.json
├── .gitignore
├── babel.config.js
├── dependencies.json
├── devDependencies.json
├── index.js
├── jsconfig.js
├── LICENSE
├── package.json
└── README.md

The initial project have main 4 pages:

  • Learning Page: shows tasks that belongs to the learning category
  • Shopping Page: shows tasks that belongs to the shopping category
  • Work Page: shows tasks that belongs to the work category
  • AddTodo Page: basic form to create a new task

Step 2 - Creating a new Task

A commom feature in a ToDo app is allowing users to create new tasks. For that, the create task function will use the Parse Javascript SDK to create a new Parse Object and save it on Back4app. On the AddTodo page from the starter project, you will have a basic form with an input to insert the task description, some check boxes to select the task category and a submit button. In this tutorial, you will create Parse.Object for the tasks which will have the following attributes:

Look at the Parse Javascript SDK Save Data guide for more info on creating Parse objects. {: .blockquote-tip}

1
2
3
4
5
6
{
  description: 'simple string of task descrition',
  author: 'person creating the task',
  completed: false, // or true
  createdAt: Date, // automatically created by back4app
}

Now implement the method to create a task when the user clicks on submit. At the pages/AddTodo/index.js component, let’s implement the handleSubmit method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  async function handleSubmit() {
    try {
      const Task = new Parse.Object.extend('Task');
      // Create a new instance of that class.
      const task = new Task();
      task.set('description', description);
      task.set('category', category);
      task.set('author', 'Anonymous');
      task.set('completed', false);
      await task.save();

      Alert.alert('New Task Created.');
    } catch (error) {
      console.log('Error while creating task: ', error);
    }
  }

After that, you will now be able to create some tasks. Feel free to create as many tasks as you want. In the next steps you will be querying them.

React Native Back4App

Step 2 - Querying & Filtering Tasks

Now that you have created some tasks, it is time to use Parse React Native Lib. You will write some queries and pass them to useParseQuery hook. The queries will list all the non-completed tasks in the learning category. This is the first use case of the hook, you will build a one-time fetch query, by setting enableLiveQuery:false, that runs when the learning page component renders. The enableLiveQuery is true by default, and changing to false will disable the real-time changes subscription.

On the pages/Learning/index.js component, let’s write our Parse.query:

1
2
3
4
  const Task = new Parse.Object.extend('Task');
  const query = new Parse.Query(Task);
  query.equalTo('completed', false);
  query.equalTo('category', 'learning');

Pass the query as argument to the useParseQuery hook:

1
const {results} = useParseQuery(query, {enableLiveQuery: false});

The above code shows a basic use for the Parse hook. The useParseQuery hook is a new resource that you can use with any Parse.Query. Use all Parse.Query capabitilies to retrieve your data objects and the hook will make this experience even better. After getting the results, pass them down to the TaskList component to display tasks on the App:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//Learning/index.js
import React, {useEffect} from 'react';
import {ActivityIndicator} from 'react-native';
import TaskList from '../../components/TaskList';
import Parse from 'parse/react-native.js';
import {useParseQuery} from '@parse/react-native';

const Learning = () => {
  const Task = new Parse.Object.extend('Task');
  const query = new Parse.Query(Task);
  query.equalTo('completed', false);
  query.equalTo('category', 'learning');

  const {results, isLoading} = useParseQuery(query, {enableLiveQuery: false});

  if (isLoading) {
    return <ActivityIndicator/>;
  }

  return <TaskList todos={results} />;
};

export default Learning;

Your App should successfuly show the list of tasks like this:

React Native Back4App

Step 3 - Realtime changes

The second usage you are going to explore is real-time updates. The useParseQuery hook encapsulates the Parse Live Query and provides out of the box support for real-time changes. When passing a query to the hook, it creates a WebSocket connection to communicate with the Back4app LiveQuery server, which synchronizes automatically. You will add this feature to the tasks in the shopping category.

It is important to note that Live Query and Back4App subdomain must be enabled on your Back4app Dashboard App. Once you do that, add your subdomain url to initializeParse and the results from the Parse React Native hook will always have updated data. If you do not configure the subdomain, useParseQuery hook will not be able to retrieve data in real-time.

1
2
3
4
5
6
// src/index.js
initializeParse(
'<yoursubdomain>.b4a.io',
'APPLICATION_ID',
'JAVASCRIPT_KEY'
);

On the pages/Shopping/index.js component, let’s write our Parse.query:

1
2
3
4
  const Task = new Parse.Object.extend('Task');
  const query = new Parse.Query(Task);
  query.equalTo('completed', false);
  query.equalTo('category', 'shopping');

Then, pass the query as argument to the useParseQuery hook:

1
const {results, isLoading, isSyncing} = useParseQuery(query);

Note that there is no need for extra parameters since the real-time changes is enabled by default. After getting the results, pass them down to the TaskList component to display tasks on the App:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import React from 'react';
import {ActivityIndicator} from 'react-native';
import TaskList from '../../components/TaskList';
import Parse from 'parse/react-native.js';
import {useParseQuery} from '@parse/react-native';

const Shopping = () => {
  const Task = new Parse.Object.extend('Task');
  const query = new Parse.Query(Task);
  query.equalTo('completed', false);
  query.equalTo('category', 'shopping');

  const {results, isLoading, isSyncing} = useParseQuery(query);

  if (isLoading || isSyncing) {
    return <ActivityIndicator />;
  }
  return <TaskList todos={results || []} />;
};

export default Shopping;

Step 4 - Offline Support

The third use case for @parse/react-native is using offline caching of query results. This is useful in case your React Native App needs to work when users have network latency or Internet connectivity issues. Offline support improves your React Native Apps responsiveness and user expirience. The great news is that no extra steps are required! The offline-first approach and real-time subscriptions are enabled by default.

In short, simply using useParseQuery hook assures that your app will be caching query results for offline support combined with Live Query subscriptions for when your user goes back online.

Step 5 - Limiting & Sorting queries

Supose that the task list from the work category is too much for a person to handle and you want to show only a subset of them for the day. Also, order by date of creation.

On the pages/Shopping/index.js component, let’s write our Parse.query:

1
2
3
4
5
6
  const Task = new Parse.Object.extend('Task');
  const query = new Parse.Query(Task);
  query.equalTo('completed', false);
  query.equalTo('category', 'work');
  query.ascending('createdAt'); // order by creation date
  query.limit(5); // limit to 5 tasks

Then, pass the query as argument to the useParseQuery hook and pass them down to the TaskList component to display tasks on the App:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import React from 'react';
import {ActivityIndicator} from 'react-native';
import Parse from 'parse/react-native.js';
import {useParseQuery} from '@parse/react-native';
import TaskList from '../../components/TaskList';
// import { Container } from './styles';

const Work = () => {
  const Task = new Parse.Object.extend('Task');
  const query = new Parse.Query(Task);
  query.equalTo('completed', false);
  query.equalTo('category', 'work');
  query.ascending('createdAt');
  query.limit(5);

  const {results, isLoading} = useParseQuery(query, {
    enableLiveQuery: false,
  });

  if (isLoading) {
    return <ActivityIndicator />;
  }
  return <TaskList todos={results} />;
};

export default Work;

Step 6 - Specifying useParseQuery arguments

You used @parse/react-native to retrive data from Back4app with features such Live Query in the previous steps. Therefore, an explanation of the interface exported is required. The useParseQuery hook accepts a Parse.Query and an UseParseQueryOptions object as its arguments. The default optional configuration object is the following:

1
2
3
4
5
{
  enableLiveQuery: true,
  enableLocalDatastore: true,
  initialLoad: []
}
  • enableLiveQuery: Realtime Live Query feature is enabled by default
  • enableLocalDatastore: Enables local caching of data results, default is true but you can turn off by setting to false
  • initialLoad: If you already have some data loaded in memory then you can set them to show a preview of the data to users.

It’s Done!

At this point, you’ve learned how to use the @parse/react-native lib by creating a React Native todo App on Back4App.