by

What We Will Be Building

Now that we know how to log users in using Facebook or Google OAuth let’s build a comments component. It’ll show existing comments, and allow users to submit new comments.

There’s going to be two main parts:

  1. Node.js backend. Authenticates users via Facebook or Google OAuth and stores users their comments in MongoDB database.
  2. React Native mobile app. Has Login and Comments screens. The former is to log users in, and the latter is to show user comments and to post new ones.

Let’s Get Started

Let’s start off by pulling the code from Logging Into React Native Apps with Facebook or Google tutorial to use it as a boilerplate and build on top of it.

git clone https://github.com/rationalappdev/react-native-oauth-login-tutorial.git Comments;
cd Comments;
rm -rf .git;

And then install dependencies.

npm install;

Backend

First, let’s modify our backend code so that it can handle user and comment creation using MongoDB.

MongoDB

If you don’t have MongoDB installed yet, open Terminal App and execute:

brew install mongodb

Then create a folder that MongoDB will use to store the database data:

sudo mkdir -p /data/db

And set the correct permissions:

sudo chmod 777 /data/db

Finally, launch MongoDB:

mongod&

Install Dependencies

  • Install existing dependencies.
cd backend;
npm install;
  • Install Mongoose and body-parser.
npm install mongoose body-parser --save;

Models

Now, let’s create Mongoose models for users and comments. We’ll use those to store users and comments in MongoDB.

  • Create a new folder called models within backed folder.

User Model

  • Create a new file called user.js within models folder.
import mongoose, { Schema } from 'mongoose';

// Define model schema
export const schema = new Schema({
  oauth_id: {
    type: String,
    unique: true,
    index: true,
  },
  name: String,
  avatar: String,
});

// Export Mongoose model
export default mongoose.model('User', schema);

Comment Model

  • Create a new file called comment.js within models folder.
import mongoose, { Schema } from 'mongoose';

// Define model schema
export const schema = new Schema({
  // References User model
  user: {
    type: Schema.ObjectId,
    ref: 'User',
  },
  content: String,
  created: Date
});

// Export Mongoose model
export default mongoose.model('Comment', schema);

Controllers

Currently, we have all of the authorization logic in one file server.js. But since our app is getting bigger and we’re adding comments it would be better to break the code down into different controllers to divide responsibilities. Generally, it’s a good idea to follow Single Responsibility Principle.

The single responsibility principle is a computer programming principle that states that every module or class should have responsibility over a single part of the functionality provided by the software, and that responsibility should be entirely encapsulated by the class. – Wikipedia.

  • Create a new folder called controllers within backed folder.

Auth Controller

Let’s move all authorization logic from server.js to auth.js controller.

  • Create a new file called auth.js within controllers folder.
import passport from 'passport';
import FacebookStrategy from 'passport-facebook';
import GoogleStrategy from 'passport-google-oauth20';
import User from '../models/user';
// Import Facebook and Google OAuth apps configs
import { facebook, google } from '../config';

// Transform Facebook profile because Facebook and Google profile objects look different
// and we want to transform them into user objects that have the same set of attributes
const transformFacebookProfile = (profile) => ({
  oauth_id: profile.id,
  name: profile.name,
  avatar: profile.picture.data.url,
});

// Transform Google profile into user object
const transformGoogleProfile = (profile) => ({
  oauth_id: profile.id,
  name: profile.displayName,
  avatar: profile.image.url,
});

// Register Facebook Passport strategy
passport.use(new FacebookStrategy(facebook,
  // Gets called when user authorizes access to their profile
  async (accessToken, refreshToken, profile, done)
    // Return done callback and pass transformed user object
    => done(null, await createOrGetUserFromDatabase(transformFacebookProfile(profile._json)))
));

// Register Google Passport strategy
passport.use(new GoogleStrategy(google,
  async (accessToken, refreshToken, profile, done)
    => done(null, await createOrGetUserFromDatabase(transformGoogleProfile(profile._json)))
));

const createOrGetUserFromDatabase = async (userProfile) => {
  let user = await User.findOne({ 'oauth_id': userProfile.oauth_id }).exec();
  if (!user) {
    user = new User({
      oauth_id: userProfile.oauth_id,
      name: userProfile.name,
      avatar: userProfile.avatar,
    });
    await user.save();
  }
  return user;
};

// Serialize user into the sessions
passport.serializeUser((user, done) => done(null, user));

// Deserialize user from the sessions
passport.deserializeUser((user, done) => done(null, user));

// Facebook
export const facebookLogin = passport.authenticate('facebook');
export const facebookMiddleware = passport.authenticate('facebook', { failureRedirect: '/auth/facebook' });

// Google
export const googleLogin = passport.authenticate('google', { scope: ['profile'] });
export const googleMiddleware = passport.authenticate('google', { failureRedirect: '/auth/google' });

// Callback
export const oauthCallback = async (req, res) => {
  res.redirect('OAuthLogin://login?user=' + JSON.stringify(req.user));
};

Comments Controller

Now, let’s create comments.js controller with two actions: list and create. list returns all existing comments from the database, and create adds new comments.

  • Create a new file called comments.js within controllers folder.
import Comment from '../models/comment';

// User relation for .populate()
const userRelation = {
  path: 'user',
  select: ['name', 'avatar'],
  model: 'User',
};

// List existing comments
export const list = async (req, res, next) => {
  // Get all comments and populate User models
  const comments = await Comment.find()
    .sort({ 'created': -1 })
    .populate(userRelation)
    .exec();

  res.json({
    comments
  });
};

// Create new comment
export const create = async (req, res, next) => {
  const { user_id, content } = req.body;
  // Save comment
  const comment = await new Comment({
    user: user_id,
    content: content,
    created: new Date,
  }).save();

  res.json({
    // Get the comment and populate User model
    comment: await Comment.findById(comment._id)
      .populate(userRelation)
      .exec()
  });
};

Server

In the next step let’s update server.js to import controller actions that we just created and register the routes.

  • Open server.js file and replace its content with the following.
import express from 'express';
import bodyParser from 'body-parser';
import passport from 'passport';
import mongoose from 'mongoose';
import {
  facebookLogin,
  facebookMiddleware,
  googleLogin,
  googleMiddleware,
  oauthCallback,
} from './controllers/auth';
import { list, create } from './controllers/comments';

// Connect to MongoDB
mongoose.Promise = global.Promise;
mongoose.connect('mongodb://localhost/react-native-comments');

// Initialize http server
const app = express();
app.use(bodyParser.json());
// Initialize Passport
app.use(passport.initialize());
app.use(passport.session());

// Set up auth routes
app.get('/auth/facebook', facebookLogin);
app.get('/auth/google', googleLogin);
app.get('/auth/facebook/callback', facebookMiddleware, oauthCallback);
app.get('/auth/google/callback', googleMiddleware, oauthCallback);

// Set up comment routes
app.route('/comments')
  .get(list)
  .put(create);

// Launch the server on the port 3000
const server = app.listen(3000, () => {
  const { address, port } = server.address();
  console.log(`Listening at http://${address}:${port}`);
});

Launch the Backend

We’re done making changes to the backend. Let’s launch it to make sure it still works.

  • Open Terminal App and execute:
npm start

You should see a confirmation that it’s running in your terminal.

Now, let’s open a browser and go to http://127.0.0.1:3000/comments.

There are no comments yet, but the server works, which is great. Keep it running and let’s work on the mobile app.

Mobile App

Now that we’re done with the backend let’s go ahead and make use of it and build a mobile app!

Install Dependencies

  • Install existing dependencies.
cd Comments;
npm install;
  • Install moment that’ll help us out with formatting date times in comments.
npm install moment --save;

Login Component

Since, again, our app is getting bigger and more complicated let’s break everything down into different components. And start off by moving all of the login logic from app.js to its own component called Login.

  • Create a new folder called components.
  • Create a new file called login.js within components folder.
import React, { Component, PropTypes } from 'react';
import {
  Linking,
  StyleSheet,
  Platform,
  Text,
  View
} from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';
import SafariView from 'react-native-safari-view';

export default class Login extends Component {

  static propTypes = {
    onLoggedIn: PropTypes.func.isRequired
  };

  // Set up Linking
  componentDidMount() {
    // Add event listener to handle OAuthLogin:// URLs
    Linking.addEventListener('url', this.handleOpenURL);
    // Launched from an external URL
    Linking.getInitialURL().then((url) => {
      if (url) {
        this.handleOpenURL({ url });
      }
    });
  };

  componentWillUnmount() {
    // Remove event listener
    Linking.removeEventListener('url', this.handleOpenURL);
  };

  handleOpenURL = ({ url }) => {
    // Extract stringified user string out of the URL
    const [, user_string] = url.match(/user=([^#]+)/);
    // Decode the user string and parse it into JSON
    const user = JSON.parse(decodeURI(user_string));
    // Call onLoggedIn function of parent component and pass user object
    this.props.onLoggedIn(user);
    if (Platform.OS === 'ios') {
      SafariView.dismiss();
    }
  };

  // Handle Login with Facebook button tap
  loginWithFacebook = () => this.openURL('https://localhost:3000/auth/facebook');

  // Handle Login with Google button tap
  loginWithGoogle = () => this.openURL('https://localhost:3000/auth/google');

  // Open URL in a browser
  openURL = (url) => {
    // Use SafariView on iOS
    if (Platform.OS === 'ios') {
      SafariView.show({
        url: url,
        fromBottom: true,
      });
    }
    // Or Linking.openURL on Android
    else {
      Linking.openURL(url);
    }
  };

  render() {
    return (
      <View style={styles.container}>

        <View style={styles.content}>
          <Text style={styles.header}>
            Welcome Stranger!
          </Text>
          <View style={styles.avatar}>
            <Icon name="user-circle" size={100} color="rgba(0,0,0,.09)" />
          </View>
          <Text style={styles.text}>
            Please log in to continue {'\n'}
            to the awesomness
          </Text>
        </View>

        <View style={styles.buttons}>
          <Icon.Button
            name="facebook"
            backgroundColor="#3b5998"
            onPress={this.loginWithFacebook}
            {...iconStyles}
          >
            Login with Facebook
          </Icon.Button>
          <Icon.Button
            name="google"
            backgroundColor="#DD4B39"
            onPress={this.loginWithGoogle}
            {...iconStyles}
          >
            Or with Google
          </Icon.Button>
        </View>

      </View>
    );
  }
}

const iconStyles = {
  borderRadius: 10,
  iconStyle: { paddingVertical: 5 },
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#FFF',
  },
  content: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  avatar: {
    margin: 20,
  },
  avatarImage: {
    borderRadius: 50,
    height: 100,
    width: 100,
  },
  header: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  text: {
    textAlign: 'center',
    color: '#333',
    marginBottom: 5,
  },
  buttons: {
    justifyContent: 'space-between',
    flexDirection: 'row',
    margin: 20,
    marginBottom: 30,
  },
});

API Helpers

Before we get started building comment components, let’s create a couple of helper functions that will help us make API calls.

  • Create a new file called api.js within project root folder.
// Our API backend's URL
const API = 'http://localhost:3000';

const headers = {
  'Accept': 'application/json',
  'Content-Type': 'application/json',
  'X-Requested-With': 'XMLHttpRequest'
};

export const get = async (uri) => await fetch(`${API}/${uri}`, {
  method: 'GET',
  headers,
});

export const put = async (uri, body) => await fetch(`${API}/${uri}`, {
  method: 'PUT',
  body: JSON.stringify(body),
  headers,
});

For now, we just need to make GET and PUT calls to fetch or post new comments. If you decided to add a comment deletion feature, you could easily add new delete function.

Comments Components

For comments we’re going to have three different components:

  1. List. Renders a scrollable list of comments and an input field for posting new comments using Comment and Input components respectively.
  2. Comment. Renders a single comment with an avatar, user name, content and how long ago it was posted.
  3. Input. Renders an input field for posting new comments.
  • Create a new folder called comments within components folder.

Comment Component

First, let’s create Comment component that renders each component.

  • Create a new file called comment.js within comments folder.
import React, { PureComponent, PropTypes } from 'react';
import {
  Image,
  StyleSheet,
  Text,
  View
} from 'react-native';
import moment from 'moment';

export default class Comment extends PureComponent {

  static propTypes = {
    // Comment object shape
    comment: PropTypes.shape({
      content: PropTypes.string.isRequired,
      created: PropTypes.string.isRequired,
      // User object shape
      user: PropTypes.shape({
        name: PropTypes.string.isRequired,
        avatar: PropTypes.string.isRequired,
      }).isRequired,
    }).isRequired,
  };

  render() {
    // Pull comment object out of props
    const { comment } = this.props;
    // Pull data needed to display a comment out of comment object
    const { content, created, user } = comment;
    // Pull user name and avatar out of user object
    const { name, avatar } = user;
    return (
      <View style={styles.container}>
        <View style={styles.avatarContainer}>
          {avatar && <Image
            resizeMode='contain'
            style={styles.avatar}
            source={{ uri: avatar }}
          />}
        </View>
        <View style={styles.contentContainer}>
          <Text>
            <Text style={[styles.text, styles.name]}>{name}</Text>
            {' '}
            <Text style={styles.text}>{content}</Text>
          </Text>
          <Text style={[styles.text, styles.created]}>{moment(created).fromNow()}</Text>
        </View>
      </View>
    );
  }

}

const styles = StyleSheet.create({
  container: {
    flexDirection: 'row',
  },
  avatarContainer: {
    alignItems: 'center',
    marginLeft: 5,
    paddingTop: 10,
    width: 40,
  },
  contentContainer: {
    flex: 1,
    borderBottomWidth: 1,
    borderColor: '#EEE',
    padding: 5,
  },
  avatar: {
    borderWidth: 1,
    borderColor: '#EEE',
    borderRadius: 13,
    width: 26,
    height: 26,
  },
  text: {
    color: '#000',
    fontFamily: 'Avenir',
    fontSize: 15,
  },
  name: {
    fontWeight: 'bold',
  },
  created: {
    color: '#BBB',
  },
});

It’s a pretty basic component that takes in comment object as a prop and renders user’s avatar, name, their comment and how long ago it was posted.

Input Component

Next, let’s build Input component that allows users to post new comments.

  • Create a new file called input.js within comments folder.
import React, { Component, PropTypes } from 'react';
import {
  KeyboardAvoidingView,
  StyleSheet,
  TextInput,
  Text,
  View, TouchableOpacity
} from 'react-native';

export default class Input extends Component {

  static propTypes = {
    onSubmit: PropTypes.func.isRequired,
  };

  state = {
    text: undefined, // user's input
  };

  // Update state when input changes
  onChangeText = (text) => this.setState({ text });

  // Handle return press on the keyboard
  // NOTE: You don't really need it for this example, because
  // we're using a keyboard without return button, but I left it here
  // in case you'd want to switch to a different keyboard
  onSubmitEditing = ({ nativeEvent: { text } }) => this.setState({ text }, this.submit);

  // Call this.props.onSubmit handler and pass the comment
  submit = () => {
    const { text } = this.state;
    if (text) {
      this.setState({ text: undefined }, () => this.props.onSubmit(text));
    } else {
      alert('Please enter your comment first');
    }
  };

  render() {
    return (
      // This moves children view with input field and submit button
      // up above the keyboard when it's active
      <KeyboardAvoidingView
        behavior='position'
      >
        <View style={styles.container}>
          {/* Comment input field */}
          <TextInput
            placeholder="Add a comment..."
            keyboardType="twitter" // keyboard with no return button
            autoFocus={true} // focus and show the keyboard
            style={styles.input}
            value={this.state.text}
            onChangeText={this.onChangeText} // handle input changes
            onSubmitEditing={this.onSubmitEditing} // handle submit event
          />
          {/* Post button */}
          <TouchableOpacity
            style={styles.button}
            onPress={this.submit}
          >
            {/* Apply inactive style if no input */}
            <Text style={[styles.text, !this.state.text ? styles.inactive : []]}>Post</Text>
          </TouchableOpacity>
        </View>
      </KeyboardAvoidingView>
    );
  }

}

const styles = StyleSheet.create({
  container: {
    backgroundColor: '#FFF',
    flexDirection: 'row',
    borderTopWidth: 1,
    borderColor: '#EEE',
    alignItems: 'center',
    paddingLeft: 15,
  },
  input: {
    flex: 1,
    height: 40,
    fontSize: 15,
  },
  button: {
    height: 40,
    paddingHorizontal: 20,
    alignItems: 'center',
    justifyContent: 'center',
  },
  inactive: {
    color: '#CCC',
  },
  text: {
    color: '#3F51B5',
    fontWeight: 'bold',
    fontFamily: 'Avenir',
    textAlign: 'center',
    fontSize: 15,
  },
});

This component has an input field that takes users input and passes that input to onSubmit function passed as a prop when they press Post button.

List Component

Finally, let’s make use of two components we just build in previous steps.

  • Create a new file called list.js within comments folder.
import React, { Component, PropTypes } from 'react';
import {
  RefreshControl,
  ScrollView,
  StyleSheet,
  View
} from 'react-native';
import { get, put } from '../../api';
import Comment from './comment';
import Input from './input';

export default class List extends Component {

  static propTypes = {
    // User object shape
    user: PropTypes.shape({
      _id: PropTypes.string.isRequired,
    }).isRequired,
  };

  state = {
    comments: [], // array for comments fetched from the API backend
    refreshing: true, // whether comments list is being refreshed or not
  };

  // Fetch comments when component is about to mount
  componentWillMount = () => this.fetchComments();

  // Re-fetch comments when user pulls the list down
  onRefresh = () => this.fetchComments();

  // Call API to fetch comments
  fetchComments = async () => {
    this.setState({ refreshing: true });
    try {
      // Make API call
      const response = await get('comments');
      // Convert response to JSON
      const json = await response.json();
      this.setState({
        refreshing: false,
        comments: json.comments
      });
    }
    catch (error) {
      alert(error);
    }
  };

  // Call API to submit a new comment
  submitComment = async (comment) => {
    const { user } = this.props;
    this._scrollView.scrollTo({ y: 0 });
    try {
      // Make API call
      const response = await put('comments', {
        user_id: user._id,
        content: comment,
      });
      // Convert response to JSON
      const json = await response.json();
      this.setState({
        // Push new comment to state before existing ones
        comments: [json.comment, ...this.state.comments]
      });
    }
    catch (error) {
      alert(error);
    }
  };

  render() {
    // Pull comments out of state
    const { comments } = this.state;
    return (
      <View style={styles.container}>
        {/* Scrollable list */}
        <ScrollView
          ref={(scrollView) => { this._scrollView = scrollView; }}
          refreshControl={
            <RefreshControl
              refreshing={this.state.refreshing}
              onRefresh={this.onRefresh}
            />
          }
        >
          {/* Render each comment with Comment component */}
          {comments.map((comment, index) => <Comment comment={comment} key={index} />)}
        </ScrollView>
        {/* Comment input box */}
        <Input onSubmit={this.submitComment} />
      </View>
    );
  }

}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#FFF',
    paddingTop: 20,
  }
});

This component makes an API call to fetch existing comments when being loaded for the first time. Then it renders a scrollable list with comments rendered as Comment components and an input field rendered by Input component. It also allows users to refresh comments by pulling down the list.

App Component

And in the final step let’s update App component to use our new Login and Comments components.

  • Open app.js within project root folder.
import React, { Component } from 'react';
import Login from './components/login';
import Comments from './components/comments/list';

export default class App extends Component {

  state = {
    user: undefined, // not logged in yet
  };

  // Gets called after user logs in with Facebook or Google
  onLoggedIn = (user) => {
    this.setState({ user });
  };

  render() {
    const { user } = this.state;
    return user
      // Show comments if user is logged in
      ? <Comments user={user} />
      // Show login screen otherwise
      : <Login onLoggedIn={this.onLoggedIn} />;
  }
}

All Set

And we’re all done. Let’s launch our app to make sure that everything is working as expected.

  1. Make sure our Node.js backend is still up and running.
  2. Launch the mobile app by executing react-native run-ios in the terminal.

Wrapping Up

Hopefully, you’ve learned a lot and will be able to use that knowledge when building your apps! Subscribe to get notified about new tutorials. And if you have any questions or ideas for new tutorials, just leave a comment below the post.

Recommended Reading

Spread the Word
  • iwebworld info

    this site providing good information about NodeJS, To learn NodeJS visit http://iwebworld.info contact: [email protected]

  • Juan felipe Ortega Lugo

    This is a good stuff, but when i used this in ios doesn’t works the KeyboardAvoidingView, dont u
    know what happend with this? thanks!

  • Muhd Wasil Ong

    Hi, I’m a beginner to react native and javascript. I tried following this tutorial but got stuck at the beginning part in trying to launch the backend. I seems to betting the error of connot find module “../config” when i try to start the backend…can you help me to explain what could i be doing wrong? I followed the turorial like how it was shown…thank you in advance

  • Being new to the blogging world I feel like there is still so much to learn. Your tips helped to clarify a few things for me as well on React.js

  • Arktix (Arktix)

    How would I go about adding photos to the comment, I assume it would require some sloud storage to hold them and a variable for the image including function for taking the photo

  • chandu chinnu

    this is a great stuff about ReactJS
    to me thank you

  • Mahesh Chemmala

    nice blog. thanks for sharing React JS Tutorials. It’s really Helpful for me
    Fabulous Information.React JS Online Training

  • Mahesh Chemmala

    Thank you for posting the valuable information about the Node JS Blogs .And every people easily understand about your posting, and I am learning a lot of things from your posts,Keep it up.
    Node JS Online Training

  • Nice Blog! Thanks for sharing very useful post. keep it up.
    For Best Node JS Training click on the text

  • It’s very informative article about Implementing Comments with React Native and Node.js. Thank you so much for this great content.
    If we take Node JS Training then we can improve our skills on Node JS technologies.

  • SARDAR USAMA

    how to set up the server address in real time? means at deploy
    t have been searching it for a long time but could not find at all, everyone is using localhost, will u please tell me how to solve this?

  • Its good and Informative.Thank you for posting this article.
    Reactjs Online Training

  • Rupinder Netset

    Wonderful blog & good post. It’s really helpful for me, Your blog is easily understandable and gives complete information. Thanks for sharing!

  • Great tutorial on react and node. Keep casting bro.
    Check my 5 hours tutorial on React and Node https://youtu.be/Fy9SdZLBTOo

  • Good post and a comprehensive, balanced selection of content for the blog.
    https://www.gologica.com/course/nodejs/
    https://www.gologica.com/course/data-science/

  • Wonderful post! We are linking to this great post on our website. Keep up the great writing.

    http://infocampus.co.in/react-js-training-in-bangalore

  • kulmohan singh

    A great piece that sheds much needed light on merging technology and its impact on business as there are many new details you posted here. Sometimes it is not so easy to build an React Native App development service without custom knowledge; here you need proper development skills and experience. However, the details you mention here would be very much helpful for the beginner. Here is yet another top-notch solution provider “X-Byte Enterprise Solutions” who render feasible and credible solutions to global clients.

    Know more here: React Native App development Company

  • infocampus logics

    Greetings! Very helpful advice within this article! It is the little changes that produce the largest changes. Many thanks for sharing!

    Angular 9 Training in Bangalore
    Angular Online Training
    Angular Training in Bangalore
    Angular 7 Training in Bangalore

  • Pyramidions

    Very useful article. As mentioned above, React Native is a very useful, efficient, and most cost-efficient way of developing apps for any MNC’s and especially startups. Indeed companies have to choose the best react native app development company in order to produce their best results.

  • Pyramidions

    Great article. I will share this to my network of developers who work on similar kinds of projects and articles.Anyways, keep going champ.

  • Pyramidions

    Great article. Every React Native Development company needs to read this. Thanks for the article. Will share it with peers and friends.

  • Apoorva

    Nice article.
    power bi training

  • Apoorva

    Nice article.
    devops online training

  • Ritik Visapurkar

    really liked this article want more like this. learn salesforce training in mumbai

  • Nikhil Sharma

    Thank you for sharing such good information. Very informative and effective post. Keep it up!
    power bi training

  • Pankaj Nagla

    Website is so easy to use – I am impressed with it. Thankyou for sharing.
    Devops Course

  • kritishah1

    Thank you for sharing such a good information. Very informative and effective post. Keep it up!
    Training for devops

  • Apoorva

    Nice article, thanks for sharing informative content. I like the content of the post.
    power bi course

  • vishesh paliwal

    Write Great article. You are such a Great blogger and your website is inspiration for me to start a blog. This article is very helpful for us.
    DevOps Training

  • Nice content on react native, so much convincing and easy to grasp. DevOps Certification Thank you for sharing.

  • Ankita Garg

    Thank you for sharing such good information. Very informative and effective post. Keep it up!
    Power BI Certification

  • Nice content on Implementing Comments with React Native and Node.js, so much convincing and easy to grasp. DevOps Certification Thank you for sharing.

  • Prakash Kumar

    Very good blog for React native developers . Thanks

  • Apoorva Gupta

    Nice article, thanks for sharing informative content. I like the content of the post.
    power bi course

  • smita

    learned a lot and will be able to use that knowledge when building apps!
    Training for devops

  • vishesh p

    This is an introductory coverage for the beginer

    DevOps Training

    Thanks for sharing the information

  • Nice content on Implementing Comments with React Native and Node.js, so much convincing and easy to grasp. power bi training Thanks

  • Glutanex Snow White Cream (Instantly Whitening Glutahtione, ALA, Vitamin C Day Cream) Order Now: +91-9980881230

    Glutenex within a fraction of seconds can answer many fairness-related questions. What else you want to be remembered amongst your people for your fairness? Go out and Buy fairness cream for women and men for a decent quality of skin-glow.

    For More Information Call us: +91-9980881230

    Flipkart url: https://bit.ly/2W0Fy9e

  • It is a lot easier to use! its great. Thank you for sharing.
    DevOps Course

  • Simran Bano

    This article has alot of valuable information. Great work. Thank you for sharing this with us. Learn more about machine learning course here and build your Career.

  • niharika das

    Nice article. This has a lot of information. Thank you. Also learn about python course and other courses to gain more knowledge and build your career.

  • rebusarkar

    Really helpful blog, loved a lot. splunk training thank you for sharing the content

  • work soft stings

    Thanks for sharing this. This would be a big help but I think writing an essay for college application
    needs only determination. If you are determined enough to enter college.
    Soft Stings

  • Prasanjit Das

    nice and valuable words with great information loved it. thanks uipath training uipath training for sending this content.

  • Apoorva Gupta

    Nice article, thanks for sharing informative content. I like the content of the post.
    power bi course

  • Nice content, so much convincing and easy to grasp. Thank you for sharing.sap analytics cloud training

  • Apoorva Gupta

    Nice article, thanks for sharing informative post.
    power bi course

  • Pankaj Nagla

    I have seen the post. Firstly, I wanted to say thank you for sharing this good writing content and it’s useful to me.
    DevOps Course

  • smita

    Very informative.
    DevOps Training

  • Apoorva Gupta

    Nice article, thanks for sharing informative content. I like the content of the post.
    power bi course

  • Really awesome blog. Useful information and knowledge. Thanks for posting this blog. Keep sharing more blogs again soon.
    UI Training in Hyderabad
    RPA Training in Hyderabad
    Mean Stack Training in Hyderabad
    Python Training in Hyderabad

  • VigneshW

    Thank you for contributing to our vision by sharing this useful information. Keep sharing what you’ve written because it’s a trustworthy blog.
    UI/UX Design Company in Chennai
    Web design company in Chennai
    Best UI/UX Design Companies in Chennai
    website Re-design in chennai

  • your article was excellent am seeing a long time one of the good blogs
    https://www.oceansoftwares.com/android-development-companies-in-chennai.html

  • Your essay was amazing, and I’ve been following one of the good blogs for a long time. Oracle Fusion HCM Online Training

  • Get the best flipkart clone app development services to build your own online marketplace. DMS Infosystem provide a range of services to suit your needs.

  • breekelly vicki

    The full form of JIT is a Just in Time (JIT) payment. jit portal aims to facilitate timely and quick payment to different users including the farmers, automatic e-Payment of commission, labor charges to societies, transportation charges, and warehouse payments for various operations including loading and unloading of parcels and other consignments. Farmers can track payments quickly and easily

  • Great post, keep posting Software Testing Classes in Pune

  • Vocejot367

    clickbank is a well-established company that has earned the trust of marketers over the years. What sets ClickBank apart from other affiliate marketing networks are the following positive reasons.

  • Thanks for exploring about react native and nodejs. Kindly visit us on Software development solutions