The guide: Routing React Apps

The guide: Routing React Apps

Routing being a key aspect of web applications, could not be left out in React. Learn more ...

Routing being a key aspect of web applications (and even other platforms) could not be left out in React. We can make full fleshed single page applications with React if we harness the powers of routing. This does not have to be a manual process, we can make use of React-Router.

In this guide, we will touch almost every aspect related to routing in React and there will be a demo so you will as well have something to play with.

You do not need to have a high experience level to follow along. Basics of React components and how to use them is enough for you to follow along in this tutorial.

Prerequisites

You are not going to only learn how to route a React application but I will also show you basics of tooling React using Babel, NPM and Webpack. Before we start building, let's set that up and see what our folder structure will look like.

First Create a new project:

mkdir scotch-cars

cd scotch-cars

npm init

Follow NPM init wizard then install the following tooling dependencies:

npm install webpack babel-loader babel-preset-es2015 babel-preset-react serve --save-dev

We installed the following tooling dependencies:

  • webpack: Module loader/bundler for JavaScript
  • babel-loader: Webpack loader for Babel. Babel transpiles ES6/ES7 to ES5
  • babel-preset-es2015: ES6 preset for Babel. Babel core cannot handle transpiling task alone
  • babel-preset-react: React preset for Babel. Just for Babel to understand React terms like JSX
  • serve: HTTP server

Next is to configure our loader which is Webpack. Webpack is configure using a config file. So touch the file and update the config content as follows:

touch webpack.config.js


var webpack = require('webpack');
var path = require('path');

var BUILD_DIR = path.resolve(__dirname, 'src/client/public');
var APP_DIR = path.resolve(__dirname, 'src/client/app');

var config = {
entry: APP_DIR + '/index.jsx',
output: {
path: BUILD_DIR,
filename: 'bundle.js'
},
module : {
loaders : [
{
test : /.jsx?/,
include : APP_DIR,
loader : 'babel'
}
]
}
};

module.exports = config;

The most important aspect of a Webpack config is the exported config object. The minimal code above just needs an entry point, entry which is where bundling needs to begin from. It also requires an output, output which is where the bundled result is dumped and then module, which defines what loaders should be used during the bundling process. In our case, babel is the loader we need.

We need to explicitly tell Babel which presets it should make use of. You can do this with package.json or in a .babelrc file. .babelrc file is what you will see in most projects, so let's follow that:

touch .babelrc

Define the presets:

{
"presets" : ["es2015", "react"]
}

To run Webpack we have to use a reference to the bin every time which would cause friction to our dev process. What we can do is setup scripts in the package.json to help us with that:

"scripts": {
"watch" : "webpack -d --watch",
"build" : "webpack",
"serve" : "serve ./public"
}

The public directory will need an entry index.html which is very simple:

<html>
<head>
<!--Stylesheet-->
<link rel="stylesheet" href="style.css">
</head>
<body>
<!--Container for React rendering-->
<div id="container"></div>
<!--Bundled file-->
<script src="bundle.js"></script>
</body>
</html>

Loads the bundle and defines the DOM element to mount our React app.

Let us now define our folder structure so as to have a view of the task at hand before we start building:

# Folder Structure
|---public
|------index.html # App entry
|------style.css # Custom style
|------bundle.js # Generated
|---src # Components live here
|------car
|---------car.component.jsx
|---------car-detail.component.jsx
|------common
|---------about.component.jsx
|---------home.component.jsx
|---------main.component.jsx
|------index.jsx # Build entry
|---.babelrc # Babel config file
|---index.js
|---package.json
|---webpack.config.js # Webpack config gile

The following are wireframes of what we are up to in this tutorial:

Setting Up React for Routing

Now that we have got a simple environment for React to live in, next step is to setup it up for routing.

React likes to keep things as simple as possible and that is why the core library just does exactly what React is about, components. Routing, DOM rendering and other logics are abstracted to a different library. To use routing, we have to pull down React, React Router and React DOM:

npm install react react-dom react-router --save

A basic React component would look like this:

// ./src/index.jsx
import React, { Component } from 'react';
import { render } from 'react-dom';

class Home extends Component {
render(){
return (<h1>Hi</h1>);
}
}

render(<Home />, document.getElementById('container'));

You can start watching and building these now with npm run watch. Then open another terminal and run npm run serve to start the server.

Adding routing features to this app is very simple. Instead of rendering the Home component, we import Router and Route and use them to render the component:

// ./src/index.jsx
import React, { Component } from 'react';
import { render } from 'react-dom';
// Import routing components
import {Router, Route} from 'react-router';

class Home extends Component {
render(){
return (<h1>Hi</h1>);
}
}

render(
<Router>
<!--Each route is defined with Route-->
<Route path="/" component={Home}/>
</Router>,
document.getElementById('container')
);

The path attribute defines the route URL and component attribute defines the component for this route.

This kind of routing is different from what you might have seen in other UI frameworks and it is known as component routing. It is very easy to reason about because routes are also treated the same way components are treated. Routes are first class components.

We discuss the ugly URL later in this article.

More Routes

You do not need routing if the only thing you want is a single path/page as our existing example shows. Let's add more routes to the application:

// ./src/index.jsx
import React, { Component } from 'react';
import { render } from 'react-dom';
// Import routing components
import {Router, Route} from 'react-router';

class Home extends Component {
render(){
return (<h1>Home Page</h1>);
}
}

// More components
class Car extends Component {
render(){
return (<h1>Cars page</h1>);
}
}

class About extends Component {
render(){
return (<h1>About page</h1>);
}
}

render(
<Router>
<Route path="/" component={Home}/>
<Route path="/cars" component={Car}/>
<Route path="/about" component={About}/>
</Router>,
document.getElementById('container')
);

Let us do a little bit refactoring and concern sepration because that is what goes down in a real app:

// ./src/car/car.component.jsx
import React, { Component } from 'react';

class Car extends Component {
render(){
return (<h1>Cars page</h1>);
}
}

export default Car


// ./src/common/home.component.jsx
import React, { Component } from 'react';

class Home extends Component {
render(){
return (<h1>Home Page</h1>);
}
}

export default Home


// ./src/common/about.component.jsx
import React, { Component } from 'react';

class About extends Component {
render(){
return (<h1>About Page</h1>);
}
}

export default About

We just split the codes into separate file while bieng guided by our predefined folder sructure. Let's assemble in the index file:

// ./src/index.jsx
import React, { Component } from 'react';
import { render } from 'react-dom';
// Import routing components
import {Router, Route} from 'react-router';
// Import custom components
import Home from './common/home.component.jsx'
import About from './common/about.component.jsx'
import Car from './car/car.component.jsx'

render(
<Router>
<Route path="/" component={Home}/>
<Route path="/cars" component={Car}/>
<Route path="/about" component={About}/>
</Router>,
document.getElementById('container')
);

Nothing lost and we have a better app.

This might be a better time to invite Bootstrap to the party. Of course our app can't remain that ugly. Import Bootstrap in the ./public/index.html and allow it do it's magic:

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" >
Nesting (Route Ownership)

Component routes are first class components in React, so when it comes to parenting/ownership, the same rule applies. Our app is expected to have a navigation menu that is accessible by all the main routes. We can make another parent route for all the existing routes which will have the nav-bar:

// ./src/common/main.component.jsx
import React, {Component} from 'react';

class Main extends Component {
render(){
return(
<div>
<nav className="navbar navbar-default">
<div className="container-fluid">
<div className="navbar-header">
<a className="navbar-brand" href="#">Scotch Cars</a>
</div>
<div className="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul className="nav navbar-nav">
<li className="active"><a href="#">Link <span className="sr-only">(current)</span></a></li>
<li><a href="#">Link</a></li>
</ul>
</div>
</div>
</nav>
<div className="container">
<!-- Mount child routes -->
{this.props.children}
</div>
</div>
);
}
}

export default Main

In the route setup, add another Route component to render that wraps the rest of the routes;

render(
<Router>
<Route component={Main}>
<Route path="/" component={Home}/>
<Route path="/cars" component={Car}/>
<Route path="/about" component={About}/>
</Route>
</Router>,
document.getElementById('container')
);

Just like evey other componets, the contets of the child routes are poured out where ever {this.props.children} is found on the parent route.

Route Prefixing

Routes can be prefixed with React Router. Route prefixing is very common when building API endpoints where we have something like:

https://example.com/api/cars

api/ is the route prefix and we can do this with React Router for nested routes:

<Router>
<Route component={Main} path="app">
<Route path="/" component={Home}/>
<Route path="/cars" component={Car}/>
<Route path="/about" component={About}/>
</Route>
</Router>

The path attribute will prefix all the child routes path with it's value and that gives us:

/app
/app/cars
/app/cars
Index Route

There is another option to defining the root of your app (a.k.a index). IndexRoute is another component in React-Router that handles this:

<Router>
<Route path="/" component={Main} path="app">
<IndexRoute component={Home} />
<Route path="/cars" component={Car}/>
<Route path="/about" component={About}/>
</Route>
</Router>

You need to import the component from React-Router:

import {Router, Route, IndexRoute} from 'react-router';
Browser History

History is a term that covers everything it takes to manage location, history and URL in React-Router.

Up on till now, we have been dealing with an ugly URL. That is not the best React-Router can offer. React-Router offers three ways to manage URLs in React apps:

At the moment our app defaults to hashHistory and that is what is responsible for the ugly URL. browserHistory is the recommended option for user consumption. We just need to tell React-Router to use browserHistoty:

<Router>
<Route path="/" component={Main} history={browserHistory}>
<IndexRoute component={Home} />
<Route path="/cars" component={Car}/>
<Route path="/about" component={About}/>
</Route>
</Router>

Import browserHistory from React-Router:

import {Router, Route, IndexRoute, browserHistory} from 'react-router';

You also need to set the base URL in the head of index.html before it works as expected:

<!-- ./public/index.html -->
<base href="/" />

This works fine until you navigate to another page and reload:

This shouldn't surprise you though because now we are making a request back to server which does not even handle a wildcard route. Let's use express to fix this by creating a backend custom server with a wildcard route URL that takes us back to where we were when a reload happens:

// ./index.js
const express = require('express')
const path = require('path')
const port = process.env.PORT || 3000
const app = express()

// serve static assets normally
app.use(express.static(__dirname + '/public'))

// Handles all routes so you do not get a not found error
app.get('*', function (request, response){
response.sendFile(path.resolve(__dirname, 'public', 'index.html'))
})

app.listen(port)
console.log("server started on port " + port)

Now we can't use serve again so we just run with node:

"scripts": {
"watch" : "webpack -d --watch",
"build" : "webpack",,
"start" : "node index.js"
}
Links

Our application is becoming more interesting and it is time to add some route links. We need to navigate with clicks and not changing URL values. Before we discuss how to add links, let's populate our application with mock cars:

// ./src/car/car.component.jsx
import React, { Component } from 'react';

class Car extends Component {
// Constructor is responsible for setting up props and setting initial stte
constructor(props){
// Pass props to the parent component
super(props);
// Set initial state
this.state = {
// State needed
cars: []
};
}

componentDidMount(){
    // Static data
    const data = [
        {
            id: 1,
            name: 'Honda Accord Crosstour',
            year: '2010',
            model: 'Accord Crosstour',
            make: 'Honda',
            media: 'http://media.ed.edmunds-media.com/honda/accord-crosstour/2010/oem/2010_honda_accord-crosstour_4dr-hatchback_ex-l_fq_oem_4_500.jpg',
            price: '$16,811'

        },
        {
            id: 2,
            name: 'Mercedes-Benz AMG GT Coupe',
            year: '2016',
            model: 'AMG',
            make: 'Mercedes Benz',
            media: 'http://media.ed.edmunds-media.com/mercedes-benz/amg-gt/2016/oem/2016_mercedes-benz_amg-gt_coupe_s_fq_oem_1_717.jpg',
            price: '$138,157'

        },
        {
            id: 3,
            name: 'BMW X6 SUV',
            year: '2016',
            model: 'X6',
            make: 'BMW',
            media: 'http://media.ed.edmunds-media.com/bmw/x6/2016/oem/2016_bmw_x6_4dr-suv_xdrive50i_fq_oem_1_717.jpg',
            price: '$68,999'
        },
        {
            id: 4,
            name: 'Ford Edge SUV',
            year: '2016',
            model: 'Edge',
            make: 'Ford',
            media: 'http://media.ed.edmunds-media.com/ford/edge/2016/oem/2016_ford_edge_4dr-suv_sport_fq_oem_6_717.jpg',
            price: '$36,275'
        },
        {
            id: 5,
            name: 'Dodge Viper Coupe',
            year: '2017',
            model: 'Viper',
            make: 'Dodge',
            media: 'http://media.ed.edmunds-media.com/dodge/viper/2017/oem/2017_dodge_viper_coupe_acr_fq_oem_3_717.jpg',
            price: '$123,890'
        }
    ];
    // Update state
    this.setState({cars: data});
}

render(){
    // Map through cars and return linked cars
    const carNode = this.state.cars.map((car) =&gt; {
        return (
            &lt;a
                href="#"
                className="list-group-item"
                key={car.id}&gt;
                {car.name}
            &lt;/a&gt;
        )
    });
    return (
        &lt;div&gt;
            &lt;h1&gt;Cars page&lt;/h1&gt;
            &lt;div className="list-group"&gt;
                {carNode}
            &lt;/div&gt;
        &lt;/div&gt;
    );
}

}

export default Car

We updated our Car component to present a list of data. The data is a static array, no need for the complexity of request as this article is only about routing.

With some static data available, let's tackle this topic. Links in React routing work well with the anchor tag but this is not recommended. Link is a component that uses anchor internally and is the recommended way for displaying links because it plays nicer with React Router:

<Link to="/">Home</Link>

That is how links are used and the to property defines the path we want to navigate to on click just like href. Let's update our Main component to apply links:

// ./src/common/common.component.jsx
import React, {Component} from 'react';
import { Link } from 'react-router';

class Main extends Component {
render(){
return(
<div>
<nav className="navbar navbar-default">
<div className="container-fluid">
<div className="navbar-header">
<a className="navbar-brand" href="#">Scotch Cars</a>
</div>
<div className="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul className="nav navbar-nav">
{/* Change from a to Link */}
<li><Link to="/">Home</Link></li>
<li><Link to="/cars">Cars</Link></li>
<li><Link to="/about">About</Link></li>
</ul>
</div>
</div>
</nav>
<div className="container">
{this.props.children}
</div>
</div>
);
}
}

export default Main

We first import the Link component from React-Router then use the component for the navigation menu rather than <a>.

Active Links

For better user experience, it is a good practice to let the user know where he/she is at by indicating with a contrasting style on the active link. Let's define a style in our style.css for that:

/* ./public/style.css /
a.active {
color: #000000 !important;
text-decoration: underline !important;
}

Then we use React-Router's activeClassName to active this style every time a respective link is activated:

 <li><Link to="/" activeClassName="active">Home</Link></li>
<li><Link to="/cars" activeClassName="active">Cars</Link></li>
<li><Link to="/about" activeClassName="active">About</Link></li>
Route Parameters

We need route parameters when requesting a single item or resource for a page. Take for instance:

/cars/3
/cars/honda-crosstour

id and honda-crosstour are route parameters and we can use the value to retrieve a single car. During specification, the URLS are represented like this:

/cars/:id
/cars/:name

We will make use of only id in this demo.

First thing to do is define a route that should have a route parameter:

render(
<Router history={browserHistory}>
<Route component={Main}>
<Route path="/" component={Home}/>
<Route path="/cars" component={Car}/>
{/ Parameter route*/}
<Route path="/cars/:id" component={CarDetail}/>
<Route path="/about" component={About}/>
</Route>
</Router>,
document.getElementById('container')
);

The spotlight is on:

<Route path="/cars/:id" component={CarDetail}/>

The path shows that a dynamic value is expected at the id placeholder. The CarDetail does not exist yet so let's make that:

// ./src/car/car-detail.component.jsx
import React, { Component } from 'react';

class CarDetail extends Component {
render(){
return (<h1>{this.props.params.id}</h1>);
}
}

export default CarDetail

Like every other component but the parameter is accessed via props:

this.props.params.id

Don't forget to import CarDetail in the root index

Let's use this ID to filter the cars array. Before we can do that we need to move the cars data array to a file that both Car and CarDetail component can have access to it. That should be the root then we can pass it down to the components as route props:

import React, { Component } from 'react';
import { render } from 'react-dom';
// Import routing components
import {Router, Route, IndexRoute, browserHistory} from 'react-router';

import Main from './common/main.component.jsx'
import Home from './common/home.component.jsx'
import About from './common/about.component.jsx'
import Car from './car/car.component.jsx'
import CarDetail from './car/car-detail.component.jsx'

//
const data = [
{
id: 1,
name: 'Honda Accord Crosstour',
year: '2010',
model: 'Accord Crosstour',
make: 'Honda',
media: 'http://media.ed.edmunds-media.com/honda/accord-crosstour/2010/oem/2010_honda_accord-crosstour_4dr-hatchback_ex-l_fq_oem_4_500.jpg',
price: '$16,811'

},
{
    id: 2,
    name: 'Mercedes-Benz AMG GT Coupe',
    year: '2016',
    model: 'AMG',
    make: 'Mercedes Benz',
    media: 'http://media.ed.edmunds-media.com/mercedes-benz/amg-gt/2016/oem/2016_mercedes-benz_amg-gt_coupe_s_fq_oem_1_717.jpg',
    price: '$138,157'

},
{
    id: 3,
    name: 'BMW X6 SUV',
    year: '2016',
    model: 'X6',
    make: 'BMW',
    media: 'http://media.ed.edmunds-media.com/bmw/x6/2016/oem/2016_bmw_x6_4dr-suv_xdrive50i_fq_oem_1_717.jpg',
    price: '$68,999'
},
{
    id: 4,
    name: 'Ford Edge SUV',
    year: '2016',
    model: 'Edge',
    make: 'Ford',
    media: 'http://media.ed.edmunds-media.com/ford/edge/2016/oem/2016_ford_edge_4dr-suv_sport_fq_oem_6_717.jpg',
    price: '$36,275'
},
{
    id: 5,
    name: 'Dodge Viper Coupe',
    year: '2017',
    model: 'Viper',
    make: 'Dodge',
    media: 'http://media.ed.edmunds-media.com/dodge/viper/2017/oem/2017_dodge_viper_coupe_acr_fq_oem_3_717.jpg',
    price: '$123,890'
}

];

render(
<Router history={browserHistory}>
<Route component={Main}>
<Route path="/" component={Home}/>
<Route path="/cars" component={Car} data={data}/>
<Route path="/cars/:id" component={CarDetail} data={data}/>
<Route path="/about" component={About}/>
</Route>
</Router>,
document.getElementById('container')
);

We now have the data array in the index.jsx then we pass it down as a route prop:

<Route path="/cars" component={Car} data={data}/>
<Route path="/cars/:id" component={CarDetail} data={data}/>

Finally we update Car component to use this data. The state is no longer needed so we can get rid of it and fetch the data from route props:

// ./src/car/car.component.jsx
import React, { Component } from 'react';
import { Link } from 'react-router';

class Car extends Component {
render(){
// Get data from route props
const cars = this.props.route.data;
// Map through cars and return linked cars
const carNode = cars.map((car) => {
return (
<Link
to={"/cars/"+car.id}
className="list-group-item"
key={car.id}>
{car.name}
</Link>
)
});
return (
<div>
<h1>Cars page</h1>
<div className="list-group">
{carNode}
</div>
</div>
);
}
}

export default Car

The fresh thing to learn is that we access data differently because the data was passed on to a route not a component. Instead of:

this.props.data

we have:

this.props.route.data

We also used the opportunity to use Link instead of anchor tags for navigation which points:

<Link
to={"/cars/"+car.id}
className="list-group-item"
key={car.id}>
{car.name}
</Link>

We can filter this data now with the parameter in CarDetail:

import React, { Component } from 'react';

class CarDetail extends Component {
render(){
// Car array
const cars = this.props.route.data;
// Car Id from param
const id = this.props.params.id;
// Filter car with ID
const car = cars.filter(car => {
if(car.id == id) {
return car;
}
});

    return (
        &lt;div&gt;
            &lt;h1&gt;{car[0].name}&lt;/h1&gt;
            &lt;div className="row"&gt;
                &lt;div className="col-sm-6 col-md-4"&gt;
                    &lt;div className="thumbnail"&gt;
                        &lt;img src={car[0].media} alt={car[0].name} /&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
                &lt;div className="col-sm-6 col-md-4"&gt;
                   &lt;ul&gt;
                       &lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt;: {car[0].model}&lt;/li&gt;
                       &lt;li&gt;&lt;strong&gt;Make&lt;/strong&gt;: {car[0].make}&lt;/li&gt;
                       &lt;li&gt;&lt;strong&gt;Year&lt;/strong&gt;: {car[0].year}&lt;/li&gt;
                       &lt;li&gt;&lt;strong&gt;Price&lt;/strong&gt;: {car[0].price}&lt;/li&gt;
                   &lt;/ul&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    );
}

}

export default CarDetail

Redirects

Redirecting is quite an easy one. We can make use of browserHistory's push method to redirect. For example, we can add a button to details page that redirects to list page on click:

import React, { Component } from 'react';
import { browserHistory } from 'react-router';

class CarDetail extends Component {
handleRedirect(){
browserHistory.push('/cars');
}
render(){
return(
// ... preceding codes
<div className="col-md-12">
<button className="btn btn-default" onClick={this.handleRedirect.bind(this)}>Go to Cars</button>
</div>
// ... succeeding codes
)
}
}

Bonus: Protecting Routes

It is a common practice to restrict users from accessing a particular resource because of limitations placed on there roles in the given app. We can't afford to allow a buyer have access to admin dashboard where prices can be changed. Though this logic is something that MUST be handled backend but for a better user experience, it is also important on the frontend:

const requireAuth = (nextState, replace) => {
if (!auth.isAdmin()) {
// Redirect to Home page if not an Admin
replace({ pathname: '/' })
}
}
export const AdminRoutes = () => {
return (
<Route path="/admin" component={Admin} onEnter={requireAuth} />
)
}

We are using the onEnter lifecycle event to listen to when this route will be hit. Once that happens, a check is run to determine if the authenticated user is an administrator or not.

Conclusion

This was a long read but if you followed along, you have the basics of what you need to get going with React. 

Thank for reading !

Originally published on https://scotch.io

React Native and Apollo GraphQL Tutorial: Build Mobile Apps

React Native and Apollo GraphQL Tutorial: Build Mobile Apps

A comprehensive step by step tutorial on learn to build CRUD (Create, Read, Update, Delete) using React Native, Elements, Navigation, Apollo Client and GraphQL

Originally published by Didin J at djamware.com

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:

  • Install create-react-native-app and Create App
  • Add React Navigation Header and required Screen
  • Install and Configure Apollo Client GraphQL
  • Show List of Books using React Native Elements
  • Show a Book Details using React Native Elements
  • Add a New Book using React Native Elements
  • Edit a Book using React Native Elements
  • Run and Test the React Native Apollo Client GraphQL Mobile Apps

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:

  1. Node.js (NPM or Yarn)
  2. React Native
  3. Express.js and GraphQL backend
  4. Apollo GrapQL
  5. React Native Elements
  6. React Native Navigation
  7. Android Studio or SDK for Android
  8. XCode for iOS
  9. Terminal (OSX/Linux) or Command Line (Windows)
  10. Text Editor or IDE (We are using Atom)

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&nbsp; { &nbsp; &nbsp; books { &nbsp; &nbsp; &nbsp; _id &nbsp; &nbsp; &nbsp; title &nbsp; &nbsp; &nbsp; author &nbsp; &nbsp; } &nbsp; };

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&nbsp; &nbsp; query book($bookId: String) { &nbsp; &nbsp; &nbsp; &nbsp; book(id: $bookId) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _id &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; isbn &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; title &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; author &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; description &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; published_year &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; publisher &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; updated_date &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; };

const DELETE_BOOK = gql&nbsp; mutation removeBook($id: String!) { &nbsp; &nbsp; removeBook(id:$id) { &nbsp; &nbsp; &nbsp; _id &nbsp; &nbsp; } &nbsp; };


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&nbsp; &nbsp; mutation AddBook( &nbsp; &nbsp; &nbsp; &nbsp; $isbn: String!, &nbsp; &nbsp; &nbsp; &nbsp; $title: String!, &nbsp; &nbsp; &nbsp; &nbsp; $author: String!, &nbsp; &nbsp; &nbsp; &nbsp; $description: String!, &nbsp; &nbsp; &nbsp; &nbsp; $publisher: String!, &nbsp; &nbsp; &nbsp; &nbsp; $published_year: Int!) { &nbsp; &nbsp; &nbsp; &nbsp; addBook( &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; isbn: $isbn, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; title: $title, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; author: $author, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; description: $description, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; publisher: $publisher, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; published_year: $published_year) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _id &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; };

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&nbsp; &nbsp; query book($bookId: String) { &nbsp; &nbsp; &nbsp; &nbsp; book(id: $bookId) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _id &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; isbn &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; title &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; author &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; description &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; published_year &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; publisher &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; updated_date &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; };

const UPDATE_BOOK = gql&nbsp; &nbsp; mutation updateBook( &nbsp; &nbsp; &nbsp; &nbsp; $id: String!, &nbsp; &nbsp; &nbsp; &nbsp; $isbn: String!, &nbsp; &nbsp; &nbsp; &nbsp; $title: String!, &nbsp; &nbsp; &nbsp; &nbsp; $author: String!, &nbsp; &nbsp; &nbsp; &nbsp; $description: String!, &nbsp; &nbsp; &nbsp; &nbsp; $publisher: String!, &nbsp; &nbsp; &nbsp; &nbsp; $published_year: Int!) { &nbsp; &nbsp; &nbsp; &nbsp; updateBook( &nbsp; &nbsp; &nbsp; &nbsp; id: $id, &nbsp; &nbsp; &nbsp; &nbsp; isbn: $isbn, &nbsp; &nbsp; &nbsp; &nbsp; title: $title, &nbsp; &nbsp; &nbsp; &nbsp; author: $author, &nbsp; &nbsp; &nbsp; &nbsp; description: $description, &nbsp; &nbsp; &nbsp; &nbsp; publisher: $publisher, &nbsp; &nbsp; &nbsp; &nbsp; published_year: $published_year) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; updated_date &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; };

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:


Originally published by Didin J at djamware.com

=======================================================

Thanks for reading :heart: If you liked this post, share it with all of your programming buddies! Follow me on Facebook | Twitter

React Native Vs Native : Which Is The Best For Your Mobile App?

Hence, Native app development needs more time, money, & resources to build an app.


In this article, we are going to find out which is better between React native vs native.

Why Mobile App Developer prefer React Native?

React Native is the latest but very popular technology platform which allows developers to create real mobile apps using only JavaScript. At the same time. Using React Native framework, developers can build cross-platform applications for both Cross-platforms.

React Native is the latest but very popular technology platform which allows developers to create real mobile apps using only JavaScript. At the same time. Using React Native framework, developers can build cross-platform applications for both Cross-platforms.