A comprehensive step by step tutorial on learning to build CRUD (Create, Read, Update, Delete) using React Native, Elements, Navigation, Apollo Client and GraphQL. We will use existing Node.js, Express.js, MongoDB, and GraphQL that already created in React.js tutorial. So, we will focus on accessing GraphQL from the React Native Mobile Apps. Same exactly as React.js tutorial, we will use Apollo Client GraphQL to integrating with React Native.

Shortcut to the steps:

The flow of CRUD Mobile Apps is very basic. It just the main page that shows the list of data, the details page, the edit, and the create data page. In this tutorial, we will use React Native and React Native Elements components including ScrollView, TextInput, Text, Button, View, etc. The list page contains a list of the book with the details button and a button to add new data on the right corner of the header. The details page contains data details text, the back button on the left corner of the header, an edit button and a delete button. The add-data page contains a React Native form, back button, and save button. The edit-data page contains the React Native form, back data, and update button. Simply as that, we will try to build this React Native Mobile Apps to Android and iOS devices.

The Example of React Native Navigation and Elements wrap together with Apollo Client GraphQL

The following tools, frameworks, and modules are required for this tutorial:

Before start to the main steps, make sure that you have installed Node.js and can run `npm` in the terminal or command line. To check the existing or installed Node.js environment open the terminal/command line then type this command.

node -v v10.15.1 npm -v 6.9.0 yarn -v 1.10.1



Install create-react-native-app and Create App

The create-react-native-app is a tool for creating a React Native App. To install it, type this command in your App projects folder.

sudo npm install -g create-react-native-app

Then create a React Native App using this create-react-native-app command.

create-react-native-app RnGraphql

If there question to install Expo, just type Y and choose a blank template because we will add the navigation later. That command will create a React Native app then install all required modules. The app or project folder will contain these folders and files.

|-- App.js |-- app.json |-- assets | |-- icon.png | `-- splash.png |-- node_modules `-- package.json

Next, go to the newly created React App folder.

cd RnGraphql

This React Native App is running via Expo app, before running on your Android or iOS device, make sure you have to install the Expo App to Android or Expo Client to iOS. Of course, that app is available in the App Store. So, we assume that you have installed the Expo App in your device then type this command to run the app.

npm start

or

yarn start

You will see this barcode and instruction in the terminal or command line.

To open the app in the Android device, open the Expo App first then tap on Scan QR Code button. Scan the barcode in the terminal or command line then you will see the React Native Android App like this after waiting for minutes the build process.

For iOS Device, press `s` from the keyboard to send React Native App URL to your Email or SMS. Enter your phone number or Email address (We use an email address) then press Enter. You will get this email to your mailbox.

Choose open in Expo URL then open in your browser, that will be redirected to Expo App. In Expo App welcome screen, shake your phone to open the React Native App. Now, you will see this screen in your iOS device.

Make sure you have the same network between your device and your development computer.



Add React Navigation Header and required Screen

As you see, the React Native app that you can only show plain text which unlike the standard Android or iOS Mobile Apps. To make a standard Android or iOS Mobile Apps, we need to add React Native header and navigation button with an icon to the React Native view. Type this command to install React Native Navigation (react-navigation).

yarn add react-navigation

Also, add the React Native Gesture Handler module by type this command.

yarn add react-native-gesture-handler

We have to link react-native to react-native-gesture-handler.

react-native link react-native-gesture-handler

Next, create a folder for components and components files in the root of the app folder by type these commands.

mkdir components touch components/BooksScreen.js touch components/BookDetailScreen.js touch components/AddBookScreen.js touch components/EditBookScreen.js

We will fill that files with simple React Native Button, Text, View, props, and navigation. Open and edit `components/BooksScreen.js` then add this React codes that contain React components (Button, View, Text).

import React, { Component } from 'react'; import { Button, View, Text } from 'react-native'; class BooksScreen extends Component { static navigationOptions = { title: 'Books List', }; render() { return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Books List</Text> <Button title="Go to Details" onPress={() => this.props.navigation.navigate('BookDetails')} /> <Button title="Go to Add Book" onPress={() => this.props.navigation.navigate('AddBook')} /> <Button title="Go to Edit Book" onPress={() => this.props.navigation.navigate('EditBook')} /> </View> ); } } export default BooksScreen;

Open and edit `components/BookDetailScreen.js` then add this React codes.

import React, { Component } from 'react'; import { Button, View, Text } from 'react-native'; class BookDetailScreen extends Component { static navigationOptions = { title: 'Book Details', }; render() { return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Book Details</Text> <Button title="Go to Details... again" onPress={() => this.props.navigation.push('BookDetails')} /> <Button title="Go to Home" onPress={() => this.props.navigation.navigate('Book')} /> <Button title="Go back" onPress={() => this.props.navigation.goBack()} /> </View> ); } } export default BookDetailScreen;

Open and edit `components/AddBookScreen.js` then add this React codes.

import React, { Component } from 'react'; import { Button, View, Text } from 'react-native'; class AddBookScreen extends Component { static navigationOptions = { title: 'Add Book', }; render() { return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Add Book</Text> <Button title="Go to Add Book... again" onPress={() => this.props.navigation.push('AddBook')} /> <Button title="Go to Home" onPress={() => this.props.navigation.navigate('Book')} /> <Button title="Go back" onPress={() => this.props.navigation.goBack()} /> </View> ); } } export default AddBookScreen;

Open and edit `components/EditBookScreen.js` then add this React codes.

import React, { Component } from 'react'; import { Button, View, Text } from 'react-native'; class EditBookScreen extends Component { static navigationOptions = { title: 'Edit Book', }; render() { return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Add Book</Text> <Button title="Go to Edit Book... again" onPress={() => this.props.navigation.push('EditBook')} /> <Button title="Go to Home" onPress={() => this.props.navigation.navigate('Book')} /> <Button title="Go back" onPress={() => this.props.navigation.goBack()} /> </View> ); } } export default EditBookScreen;

Next, open and edit `App.js` then add replace all codes with this created components and routing navigation.

import { createStackNavigator, createAppContainer } from 'react-navigation'; import BooksScreen from './components/BooksScreen'; import BookDetailScreen from './components/BookDetailScreen'; import AddBookScreen from './components/AddBookScreen'; import EditBookScreen from './components/EditBookScreen'; const MainNavigator = createStackNavigator({ Book: { screen: BooksScreen }, BookDetails: { screen: BookDetailScreen }, AddBook: { screen: AddBookScreen }, EditBook: { screen: EditBookScreen }, }); const App = createAppContainer(MainNavigator); export default App;

Next, run again the React Native app then refresh your Expo app. You will see the standard Android or iOS Mobile Apps in your device screen.



Install and Configure Apollo Client GraphQL

Now, we have to install and configure all of the required modules and dependencies to the React Apollo and GraphQL. Type this command to install the modules at once.

yarn add react-apollo apollo-client apollo-cache-inmemory apollo-link-http graphql-tag graphql

Next, open and edit `App.js` then replace all imports with these imports of React Native AppRegistry, CreateStackNavigator, CreateAppContainer (React Navigation), ApolloClient, InMemoryCache, HttpLink, and ApolloProvider.

import React from 'react'; import { AppRegistry } from 'react-native'; import { createStackNavigator, createAppContainer } from 'react-navigation'; import BooksScreen from './components/BooksScreen'; import BookDetailScreen from './components/BookDetailScreen'; import AddBookScreen from './components/AddBookScreen'; import EditBookScreen from './components/EditBookScreen'; import { ApolloClient } from 'apollo-client'; import { InMemoryCache } from 'apollo-cache-inmemory'; import { HttpLink } from 'apollo-link-http'; import { ApolloProvider } from 'react-apollo';

Change the constant name of `createAppContainer` constant variable.

const MyRootComponent = createAppContainer(MainNavigator);

Add these lines of Apollo Client constant variables.

const cache = new InMemoryCache(); const client = new ApolloClient({ cache, link: new HttpLink({ uri: 'http://localhost:3000/graphql', }), }); const App = () => ( <ApolloProvider client={client}> <MyRootComponent /> </ApolloProvider> );

Register the Apollo Provider by adding this line before the export line.

AppRegistry.registerComponent('MyApp', () => App);



Show List of Books using React Native Elements

To show the list of the books in the Books components, open and edit `components/BooksScreen.js` then add/replace these imports of React Native Stylesheet, FlatList, ActivityIndicator, View, Text, React Native Elements ListItem, Button, GraphQL-Tag, and React Apollo Query.

import React, { Component } from 'react'; import { StyleSheet, FlatList, ActivityIndicator, View, Text } from 'react-native'; import { ListItem, Button } from 'react-native-elements'; import gql from 'graphql-tag'; import { Query } from 'react-apollo';

As you can see, there are view elements that use react-native-elements module. For that, install the react-native-elements module first by type this command.

yarn add react-native-elements

Declare a constant before the class name for the query.

const GET_BOOKS = gql` { books { _id title author } } `;

Replace `navigationOptions` with this.

static navigationOptions = ({ navigation }) => { return { title: 'LIST OF BOOKS', headerRight: ( <Button buttonStyle={{ padding: 0, backgroundColor: 'transparent' }} icon={{ name: 'add-circle', style: { marginRight: 0, fontSize: 28 } }} onPress={() => { navigation.push('AddBook') }} /> ), }; };

Add the function to extract and mapping result from GraphQL and render list item from the FlatList that will be added to the render section.

keyExtractor = (item, index) => index.toString() renderItem = ({ item }) => ( <ListItem title={item.title} onPress={() => { this.props.navigation.navigate('BookDetails', { id: `${item._id}`, }); }} chevron bottomDivider /> )

Replace all React Native view render with Apollo Client GraphQL Query with `pollInterval` to make the page always request GraphQL data.

render() { return ( <Query pollInterval={500} query={GET_BOOKS}> {({ loading, error, data }) => { if (loading) return( <View style={styles.activity}> <ActivityIndicator size="large" color="#0000ff"/> </View> ); if (error) return( <View style={styles.activity}> <Text>`Error! ${error.message}`</Text> </View> ); return ( <FlatList keyExtractor={this.keyExtractor} data={data.books} renderItem={this.renderItem} /> ); }} </Query> ); }

Add React Native styles constant variables before the export code.

const styles = StyleSheet.create({ container: { flex: 1, paddingBottom: 22 }, item: { padding: 10, fontSize: 18, height: 44, }, activity: { position: 'absolute', left: 0, right: 0, top: 0, bottom: 0, alignItems: 'center', justifyContent: 'center' } })



Show a Book Details using React Native Elements

To show book details, we have to modify `components/BookDetailScreen.js` then replace all imports with these imports.

import React, { Component } from 'react'; import { ScrollView, StyleSheet, ActivityIndicator, View, Text } from 'react-native'; import { Card, Button } from 'react-native-elements'; import gql from 'graphql-tag'; import { Query, Mutation } from 'react-apollo';

Declare the constant variables of the GraphQL `gql` or Queries.

const GET_BOOK = gql` query book($bookId: String) { book(id: $bookId) { _id isbn title author description published_year publisher updated_date } } `; const DELETE_BOOK = gql` mutation removeBook($id: String!) { removeBook(id:$id) { _id } } `;

Modify the render section of the class to display a book details by implementing Apollo GraphQL Query and React Native ScrollView, Card, View, Button, Text, ActivityIndicator, etc.

class BookDetailScreen extends Component { static navigationOptions = { title: 'Book Details', }; render() { const { navigation } = this.props; return ( <Query pollInterval={500} query={GET_BOOK} variables={{ bookId: navigation.getParam('id') }}> {({ loading, error, data }) => { if (loading) return(<View style={styles.activity}> <ActivityIndicator size="large" color="#0000ff" /> </View>); if (error) return(<Text>`Error! ${error.message}`</Text>); return ( <ScrollView> <Card style={styles.container}> <View style={styles.subContainer}> <View> <Text style={{fontSize: 16, fontWeight: 'bold'}}>ISBN:</Text> <Text style={{fontSize: 18, marginBottom: 10}}>{data.book.isbn}</Text> </View> <View> <Text style={{fontSize: 16, fontWeight: 'bold'}}>Title: </Text> <Text style={{fontSize: 18, marginBottom: 10}}>{data.book.title}</Text> </View> <View> <Text style={{fontSize: 16, fontWeight: 'bold'}}>Author: </Text> <Text style={{fontSize: 18, marginBottom: 10}}>{data.book.author}</Text> </View> <View> <Text style={{fontSize: 16, fontWeight: 'bold'}}>Description: </Text> <Text style={{fontSize: 18, marginBottom: 10}}>{data.book.description}</Text> </View> <View> <Text style={{fontSize: 16, fontWeight: 'bold'}}>Published Year: </Text> <Text style={{fontSize: 18, marginBottom: 10}}>{data.book.published_year}</Text> </View> <View> <Text style={{fontSize: 16, fontWeight: 'bold'}}>Publisher: </Text> <Text style={{fontSize: 18, marginBottom: 10}}>{data.book.publisher}</Text> </View> <View> <Text style={{fontSize: 16, fontWeight: 'bold'}}>Updated Date: </Text> <Text style={{fontSize: 18}}>{data.book.updated_date}</Text> </View> </View> <Mutation mutation={DELETE_BOOK} key={data.book._id} onCompleted={() => navigation.goBack()}> {(removeBook, { loading2, error2 }) => ( <View style={styles.subContainer}> <Button style={styles.detailButton} large backgroundColor={'#CCCCCC'} leftIcon={{name: 'edit'}} title='Edit' onPress={() => { navigation.navigate('EditBook', { id: `${data.book._id}`, }); }} /> <Button style={styles.detailButton} large backgroundColor={'#999999'} color={'#FFFFFF'} leftIcon={{name: 'delete'}} title='Delete' onPress={() => { removeBook({ variables: { id: data.book._id } }) .then(res => res) .catch(err => <Text>{err}</Text>); }} /> {loading2 && <View style={styles.activity}> <ActivityIndicator size="large" color="#0000ff" /> </View>} {error2 && <Text>`Error! ${error2.message}`</Text>} </View> )} </Mutation> </Card> </ScrollView> ); }} </Query> ); } }

Modify or add the React Native style to match the modified details page.

const styles = StyleSheet.create({ container: { flex: 1, padding: 20 }, subContainer: { flex: 1, paddingBottom: 20, borderBottomWidth: 2, borderBottomColor: '#CCCCCC', }, activity: { position: 'absolute', left: 0, right: 0, top: 0, bottom: 0, alignItems: 'center', justifyContent: 'center' }, detailButton: { marginTop: 10 } })



Add a New Book using React Native Elements

To add a new book, we have an add button in the List of Book page that will push the Add Page. This Add Page will use Apollo GraphQL Mutation instead of React Form. The React Native TextInput change will use a function that triggers the OnChange event. Open and edit `components/AddBookScreen.js` then replace all imports with these imports.

import React, { Component } from 'react'; import { StyleSheet, ScrollView, ActivityIndicator, View, TextInput, Text } from 'react-native'; import { Button } from 'react-native-elements'; import gql from "graphql-tag"; import { Mutation } from "react-apollo";

Declare a constant variable of the Apollo GraphQL `gql` or Query before the class name.

const ADD_BOOK = gql` mutation AddBook( $isbn: String!, $title: String!, $author: String!, $description: String!, $publisher: String!, $published_year: Int!) { addBook( isbn: $isbn, title: $title, author: $author, description: $description, publisher: $publisher, published_year: $published_year) { _id } } `;

Add an object of the state after the `navigationOptions` that consists of required fields that match the GraphQL data.

state = { isbn: '', title: '', author: '', description: '', published_year: '', publisher: '', }

Add a function to update the React Native TextInput value and state object values.

updateTextInput = (text, field) => { const state = this.state state[field] = text; this.setState(state); }

Modify the React Native render section to add Apollo GraphQL Mutation, React Native TextInput, ScrollView, View, ActivityIndicator, etc.

render() { const { isbn, title, author, description, published_year, publisher } = this.state; return ( <Mutation mutation={ADD_BOOK} onCompleted={() => this.props.navigation.goBack()}> {(addBook, { loading, error }) => ( <ScrollView style={styles.container}> <View style={styles.subContainer}> <TextInput style={styles.textInput} placeholder={'ISBN'} value={this.state.isbn} onChangeText={(text) => this.updateTextInput(text, 'isbn')} /> </View> <View style={styles.subContainer}> <TextInput style={styles.textInput} placeholder={'Title'} value={this.state.title} onChangeText={(text) => this.updateTextInput(text, 'title')} /> </View> <View style={styles.subContainer}> <TextInput style={styles.textInput} placeholder={'Author'} value={this.state.author} onChangeText={(text) => this.updateTextInput(text, 'author')} /> </View> <View style={styles.subContainer}> <TextInput style={styles.textInput} placeholder={'Description'} multiline={true} numberOfLines={4} value={this.state.description} onChangeText={(text) => this.updateTextInput(text, 'description')} /> </View> <View style={styles.subContainer}> <TextInput style={styles.textInput} placeholder={'Published Year'} value={this.state.published_year} keyboardType='numeric' onChangeText={(text) => this.updateTextInput(text, 'published_year')} /> </View> <View style={styles.subContainer}> <TextInput style={styles.textInput} placeholder={'Publisher'} value={this.state.publisher} onChangeText={(text) => this.updateTextInput(text, 'publisher')} /> </View> <View> <Button large leftIcon={{name: 'save'}} title='Save' onPress={() => { addBook({ variables: { isbn: this.state.isbn, title: this.state.title, author: this.state.author, description: this.state.description, publisher: this.state.publisher, published_year: parseInt(this.state.published_year), } }) .then(res => this.setState({ isbn: '', title: '', author: '', description: '', published_year: '', publisher })) .catch(err => <Text>{err}</Text>); }} /> </View> {loading && <View style={styles.activity}> <ActivityIndicator size="large" color="#0000ff" /> </View>} {error && <Text>`Error! ${error.message}`</Text>} </ScrollView> )} </Mutation> ); } }

Also, modify the React Native style to match the required views.

const styles = StyleSheet.create({ container: { flex: 1, padding: 20 }, subContainer: { flex: 1, marginBottom: 20, padding: 5, borderBottomWidth: 2, borderBottomColor: '#CCCCCC', }, activity: { position: 'absolute', left: 0, right: 0, top: 0, bottom: 0, alignItems: 'center', justifyContent: 'center' }, textInput: { fontSize: 18, margin: 5, }, })



Edit a Book using React Native Elements

The Edit Book Screen pushed from the Book Details Screen. Same as the previous Add Book Page, this page will contain the Apollo GraphQL Mutation and React Native TextInput with additional Apollo GraphQL Query. All React Native TextInput will fill with the data from GraphQL Query. Open and edit `components/EditBookScreen.js` then replace all imports with these imports.

import React, { Component } from 'react'; import { StyleSheet, ScrollView, ActivityIndicator, View, TextInput, Text } from 'react-native'; import { Button } from 'react-native-elements'; import gql from "graphql-tag"; import { Query, Mutation } from "react-apollo";

Declare the constant variables of the Apollo GraphQL Query or `gql` after the imports.

const GET_BOOK = gql` query book($bookId: String) { book(id: $bookId) { _id isbn title author description published_year publisher updated_date } } `; const UPDATE_BOOK = gql` mutation updateBook( $id: String!, $isbn: String!, $title: String!, $author: String!, $description: String!, $publisher: String!, $published_year: Int!) { updateBook( id: $id, isbn: $isbn, title: $title, author: $author, description: $description, publisher: $publisher, published_year: $published_year) { updated_date } } `;

The above constant variables are Apollo GraphQL Query of the GET and the UPDATE Book data. Next, inside the main class, add an object of the state that contains required fields.

state = { isbn: '', title: '', author: '', description: '', published_year: '', publisher: '', }

Add a function to update only the state instead of the state and the fields because the fields will fill with the data from GraphQL. In this case, you can't call `setState` twice to fill the state, so, we fill the state and the fields separately.

updateTextInput = (text, field) => { const state = this.state state[field] = text; this.setState(state); }

Modify the React Native render section to Apollo GraphQL Query, Mutation, React Native ScrollView, View, TextInput, Button, etc.

render() { const { navigation } = this.props; const { isbn, title, author, description, published_year, publisher } = this.state; return ( <Query query={GET_BOOK} variables={{ bookId: navigation.getParam('id') }}> {({ loading, error, data }) => { if (loading) return(<View style={styles.activity}> <ActivityIndicator size="large" color="#0000ff" /> </View>); if (error) return(<Text>`Error! ${error.message}`</Text>); return ( <Mutation mutation={UPDATE_BOOK} key={data.book._id} onCompleted={() => navigation.goBack()}> {(updateBook, { loading2, error2 }) => ( <ScrollView style={styles.container}> <View style={styles.subContainer}> <TextInput style={styles.textInput} placeholder={'ISBN'} defaultValue={data.book.isbn} onChangeText={(text) => this.updateTextInput(text, 'isbn')} /> </View> <View style={styles.subContainer}> <TextInput style={styles.textInput} placeholder={'Title'} defaultValue={data.book.title} onChangeText={(text) => this.updateTextInput(text, 'title')} /> </View> <View style={styles.subContainer}> <TextInput style={styles.textInput} placeholder={'Author'} defaultValue={data.book.author} onChangeText={(text) => this.updateTextInput(text, 'author')} /> </View> <View style={styles.subContainer}> <TextInput style={styles.textInput} placeholder={'Description'} multiline={true} numberOfLines={4} defaultValue={data.book.description} onChangeText={(text) => this.updateTextInput(text, 'description')} /> </View> <View style={styles.subContainer}> <TextInput style={styles.textInput} placeholder={'Published Year'} defaultValue={data.book.published_year.toString()} keyboardType='numeric' onChangeText={(text) => this.updateTextInput(text, 'published_year')} /> </View> <View style={styles.subContainer}> <TextInput style={styles.textInput} placeholder={'Publisher'} defaultValue={data.book.publisher} onChangeText={(text) => this.updateTextInput(text, 'publisher')} /> </View> <View> <Button large leftIcon={{name: 'save'}} title='Save' onPress={() => { if (this.state.isbn === '') this.state.isbn = data.book.isbn; if (this.state.title === '') this.state.title = data.book.title; if (this.state.author === '') this.state.author = data.book.author; if (this.state.description === '') this.state.description = data.book.description; if (this.state.publisher === '') this.state.publisher = data.book.publisher; if (this.state.published_year === '') this.state.published_year = data.book.published_year; updateBook({ variables: { id: data.book._id, isbn: this.state.isbn, title: this.state.title, author: this.state.author, description: this.state.description, publisher: this.state.publisher, published_year: parseInt(this.state.published_year), } }) .then(res => res) .catch(err => <Text>{err}</Text>); }} /> </View> {loading2 && <View style={styles.activity}> <ActivityIndicator size="large" color="#0000ff" /> </View>} {error2 && <Text>`Error! ${error2.message}`</Text>} </ScrollView> )} </Mutation> ); }} </Query> ); } }

Modify the React Native style to match the view requirements.

const styles = StyleSheet.create({ container: { flex: 1, padding: 20 }, subContainer: { flex: 1, marginBottom: 20, padding: 5, borderBottomWidth: 2, borderBottomColor: '#CCCCCC', }, activity: { position: 'absolute', left: 0, right: 0, top: 0, bottom: 0, alignItems: 'center', justifyContent: 'center' }, textInput: { fontSize: 18, margin: 5, }, })



Run and Test the React Native Apollo Client GraphQL Mobile Apps

Before running the React Native apps, we have to download and run the Node.js, Express.js, MongoDB GraphQL server. After download and install the required Node modules, run first the MongoDB server in the different terminal tab.

mongod

Run the Express.js GraphQL server in another terminal tab.

nodemon

In the current React Native app terminal tab, run the React Native app.

yarn start

Open again the Expo app on your iOS or Android device then refresh the current running React Native application. And here they are the full React Native Mobile Apps running with Apollo GraphQL data.









That it's, the React Native and Apollo GraphQL Tutorial: Build Mobile Apps with React Native Elements and Navigation. You can find the full source code from our GitHub.

That just the basic. If you need more deep learning about React.js, React Native or related you can take the following cheap course:

Thanks!