React Native and Couchbase Lite
How to integrate react native with couchbase lite
Introduction
Couchbase is one the most promising NoSQL database available in the market and at Famoco, we like it a lot. One of the things we have been playing around here is with Couchbase Sync Gateway and Couchbase Lite for our Android devices.
In this article, I will write a brief introduction on how to use Couchbase Lite for React Native. I developed an example for the Android platform using the movie sample database that comes with it.
The React Native package usage in this article is located under the couchbaselabs directory:
https://github.com/couchbaselabs/react-native-couchbase-lite
The source code of the app is at my github:
https://github.com/jmn8718/RNCouchbaseExample
Prerequisites
In order to have the app fully functional, we need an instance of Couchbase Server, Sync Gateway, and preload MoviesExample.json(link).
Running couchbase sync gateway
docker run -p 4984:4984 -d --name sync_gateway_rn couchbase/sync-gateway:1.3.1-community https://raw.githubusercontent.com/couchbase/sync_gateway/master/examples/basic-walrus-bucket.jsoncurl http://localhost:4984{“couchdb”:”Welcome”,”vendor”:{“name”:”Couchbase Sync Gateway”,”version”:1.3},”version”:”Couchbase Sync Gateway/1.3.1(16;f18e833)”}%
Feeding data
wget https://raw.githubusercontent.com/couchbaselabs/react-native-couchbase-lite/master/ReactNativeCouchbaseLiteExample/MoviesExample.jsoncurl -H 'Content-Type: application/json' -vX POST 'http://localhost:4984/db/_bulk_docs' -d @MoviesExample.json
Installation
The first step it is to create a new app using the react native cli
react-native init RNCouchbaseExample
When everything is ready, we need to install the Couchbase Lite package
npm install --save react-native-couchbase-lite
If you do not have rnpm installed in your system, run the following command
npm install -g rnpm
And then we link the package to our project running the command
rnpm link react-native-couchbase-lite
Once it is installed we need to modify some files in the android project.
In the file android/app/build.gradle, we need to add in the android section
packagingOptions {
exclude 'META-INF/ASL2.0'
exclude 'META-INF/LICENSE'
exclude 'META-INF/NOTICE'
}
The next step is to register the package in the MainApplication.java
First, we import the component
import me.fraserxu.rncouchbaselite.ReactCBLiteManager;
And then we add the package
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new ReactCBLiteManager() <----- Register the module
);
}
Develop
Now we are going to develop the react native app. For that, we are going to create a new folder called app where we are going to have our project files.
Inside this folder, we create a file index.js and we move the code from the index.android.js, the index.js with the following code:
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View
} from 'react-native';
export default class App extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native!
</Text>
<Text style={styles.instructions}>
To get started, edit index.android.js
</Text>
<Text style={styles.instructions}>
Double tap R on your keyboard to reload,{'\n'}
Shake or press menu button for dev menu
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
And then index.android.js is going to be:
import {
AppRegistry,
} from 'react-native';
import App from './App';
AppRegistry.registerComponent(RNCouchbaseExample, () => App);
The next step is to add the Couchbase Lite package and add the constants with our Sync gateway server
import {
manager,
ReactCBLite,
} from 'react-native-couchbase-lite';
const SG_URL = '<ip_sg_server>:<port_sg_server>';
const DB_NAME = 'db';
We are going to render a react native ListView and add a dataSource to our state to store the movies.
In our constructor we create a dataSource and we add it to the component state
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = { dataSource: ds.cloneWithRows([]) }
The next step is to add the database to our code, we are going to initialize it in the componentDidMount event
componentDidMount() {
ReactCBLite.init(url => {
const database = new manager(url, DB_NAME);
database.createDatabase()
.then(res => {
database.createDesignDocument('main', {
filters: {
year: 'function (doc) { if (doc.year === 2004) { return true;} return false;}'
},
views: {
movies: {
map: 'function (doc) { if (doc.year) { emit(doc._id, null);}}'
}
}
});
const REPLICATION_OPTIONS = {
continuous: true,
};
database.replicate(`http://${SG_URL}/${DB_NAME}`, DB_NAME, REPLICATION_OPTIONS);
database.getInfo()
.then(res => {
database.listen({
since: res.update_seq - 1,
feed: 'longpoll',
});
database.changesEventEmitter.on('changes', function (e) {
this.setState({sequence: e.last_seq});
}.bind(this));
})
.catch(e => console.log('ERROR INFO', e))
})
.then(() => database.queryView('main', 'movies', {include_docs: true}))
.then(res => this.setState({
dataSource: this.state.dataSource.cloneWithRows(res.rows),
}))
.catch(e => console.log('ERROR', e));
})
}
Let’s go over the code, first we instantiate a new manager with the name of the local database, and then we create the database.
const database = new manager(url, DB_NAME);
database.createDatabase()
The createDatabase returns a promise. In the promise, we will start creating a view for the data.
database.createDesignDocument('main', {
filters: {
year: 'function (doc) { if (doc.year === 2004) { return true;} return false;}'
},
views: {
movies: {
map: 'function (doc) { if (doc.year) { emit(doc._id, null);}}'
}
}
});
After that we start the replication connection with our server, we are going to open a connection from our Server to our local database, and we set the flag to keep open the connection and get the updates of the data in the server.
const REPLICATION_OPTIONS = { continuous: true };
database.replicate(`http://${SG_URL}/${DB_NAME}`, DB_NAME, REPLICATION_OPTIONS);
The next step is to listen for the changes that happen in the database.
database.getInfo()
.then(res => {
database.listen({
since: res.update_seq - 1,
feed: 'longpoll',
});
database.changesEventEmitter.on('changes', function (e) {
this.setState({sequence: e.last_seq});
}.bind(this));
})
.catch(e => console.log('ERROR INFO', e))
The last step is to get the data from the database, we are using view to query it, and we store the data in our component state
.then(() => database.queryView('main', 'movies', {include_docs: true}))
.then(res => this.setState({
dataSource: this.state.dataSource.cloneWithRows(res.rows),
}))
Rendering
The last step is to render the data in our app.
First, we import the elements we are going to use in our component.
import {
StyleSheet,
Text,
View,
ListView,
Image,
} from ‘react-native’;
As we mention before, we are going to use a ListView component to render all the data
render() {
return (
<View style={styles.container}>
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderMovie}
style={styles.listView}
enableEmptySections
/>
</View>
);
}
And we define the function to render the movie data
renderMovie(movie) {
var movie = movie.doc;
return (
<View style={styles.movieContainer}>
<Image
source={{uri: movie.posters.thumbnail}}
style={styles.thumbnail}/>
<View style={styles.rightContainer}>
<Text style={styles.title}>{movie.title}</Text>
<Text style={styles.year}>{movie.year}</Text>
</View>
</View>
);
}
And we add the styles for the components
const styles = StyleSheet.create({
container: {
flex: 1
},
movieContainer: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
seqTextLabel: {
textAlign: 'center',
margin: 5
},
listView: {
flex: 1,
backgroundColor: '#F5FCFF',
},
rightContainer: {
flex: 1,
},
title: {
fontSize: 20,
marginBottom: 8,
textAlign: 'center',
},
year: {
textAlign: 'center',
},
thumbnail: {
width: 53,
height: 81,
},
});
Run the App
Now if we run the application we should see the list of movies.
You may need to start the react native server, for that, run the command in a terminal
react-native start
To run the application, in a terminal run
react-native run-android
Notes
If you run the app in an android emulator, you will need to execute the following command to open the port to connect to the sync gateway server
adb reverse tcp:4984 tcp:4984