Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

Building a Twitter Clone App in React Native

Getting your hands on new programming languages or frameworks is one of the greatest methods to learn them.

Therefore, we made the decision to build a Twitter clone app while learning React Native app development. In order to demonstrate how to build this app, we will first quickly outline our experience with React Native before getting into the code.

Our Experience with React Native

1. Code Excellence

When using React Native app development services, brute forcing a solution will leave you with a tonne of source code and little understanding of how it functions. If you fix one item, another will break.

Yes, it’s not very encouraging, but on the good side, you’ll have to keep the code quality at a good scale.

2. Ease of Development

A basic understanding of Javascript is required, else, at first glance React Native can appear to be complex science. But be assured, it isn’t. React Native app development framework stands apart from other Javascript frameworks in major part because it doesn’t introduce a tonne of abstraction levels. It keeps you rather near to JavaScript’s fundamentals.

So, if you are comfortable with all fundamental DOM and JavaScript ideas, React Native is for you!

After working on a few projects, you’ll see that React Native gives you a lot of freedom without significantly increasing the complexity of your work.

3. Performance 

React Native compiles JavaScript code into a fully native app, in contrast to previous mobile technologies that offer you a “mobile app” that is actually running in webview.

React Native was found to perform better in a recent performance comparison between an app created using Swift and an identical app created with Swift.

4. Community Support

For their projects, several well-known businesses, start-ups, and independent developers use React Native. The React Native community has grown dramatically over the last several years.

It gives you the support to create something helpful and enhance an open-source project created by another mobile app developer. 

Building a Twitter Clone App in React Native

1. Getting Started

Node.js, yarn, or NPM must all be installed. We used yarn and node v8.1.2 for our project.

A new React Native project can be started in a variety of methods; for this one, we used create-react-native-app. Run the following commands to launch a new project and instal CRNA globally:

$ yarn global add create-react-native-app

$ create-react-native-app twitter-clone

$ cd twitter-clone/

$ yarn start

Additionally, we have utilized the Expo SDK to avoid having to build both Xcode and Android Studio. Additionally, this will give me access to tools that will facilitate the loading of typefaces and the ability for users to add photographs to the app. Please consult the documentation to get Expo up and running.

2. Dependencies

We’ll discuss a few of the most important tools we’ll be using for this project below.

  • With the help of the Native Base component library, we can easily create a cross-platform user interface that looks great.
  • We may move between the various displays of our app with the aid of React Native Router Flux.
  • React-Redux will link the various parts of our program to our store, where we’ll maintain information about its current state.
  • We will use Axios, a promised-based HTTP client, to perform our requests to the Cosmic JS API.

Look at the code below you can make use of your package.json and then run yarn install again. 

{

 "name": "twitter-clone",

 "version": "1.0.0",

 "private": true,

 "devDependencies": {

   "jest-expo": "~1.0.1",

   "react-native-scripts": "0.0.30",

   "react-test-renderer": "16.0.0-alpha.6"

 },

 "main": "./node_modules/react-native-scripts/build/bin/crna-entry.js",

 "scripts": {

   "start": ""react-native-scripts start",

   "eject": "react-native-scripts eject",

   "android": "react-native-scripts android",

   "ios": "react-native-scripts ios",

   "test": "node node_modules/jest/bin/jest.js --watch"

 },

 "jest": {

   "preset": "jest-expo"

 },

 "dependencies": {

   "@expo/vector-icons": "^5.0.0",

   "axios": "^0.16.1",

   "expo": "^17.0.0",

   "form-data": "^2.2.0",

   "native-base": "^2.1.4",

   "react": "16.0.0-alpha.6",

   "react-native": "^0.44.0",

   "react-native-router-flux": "^3.39.2",

   "react-redux": "^5.0.5",

   "redux": "^3.6.0",

   "redux-devtools-extension": "^2.13.2",

   "redux-logger": "^3.0.6",

   "redux-thunk": "^2.2.0",

 }

}

3. Directory Structure

CRNA is far less prescriptive about how we organize the files in our application than some other boilerplates; it just provides us with index.ios.js, index.android.js, and App.js as a starting point.

Our App.js file will refer to the app folder, which will include all of our components, layouts, configuration files, redux store, and reducers. The framework that we have discovered to be most effective for us is as follows. The source code has a complete list of the contents of every file, so let’s not go into it right now.

Here is how our application folder will appear:

4. Application 

Several events will take place in our App.js code. We’ll do:

  • Draw in the paths that lead to our varied layouts.
  • Connect our provider to our store so that our layouts may access the status of our application.
  • Give users access to some of the typefaces used by Native Base
  • With AppRegistry, establish our root component.

The following may be copied and pasted into your project root’s App.js file:

import React, { Component } from 'react';

import { AppRegistry, View } from 'react-native';

import { Provider, connect } from 'react-redux';

import { Font, AppLoading } from 'expo';

import store from './app/redux/store';

import Router from './app/config/routes';

export default class App extends Component {

 constructor(){

   super();

   this.state = {

     isReady: false,

   }

 }

 async componentWillMount() {

   await Font.loadAsync({

     'Roboto': require('native-base/Fonts/Roboto.ttf'),

     'Roboto_medium': require('native-base/Fonts/Roboto_medium.ttf'),

     'Pacifico': require('./app/assets/fonts/Pacifico.ttf'),

     'Ionicons': require('native-base/Fonts/Ionicons.ttf'),

   });

   this.setState({isReady: true});

 }

 render() {

   if (!this.state.isReady) {

     return ;

   }

   return (

     

       

     

   );

 }

}

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

Next, let’s take a look at our routes.js file:

import React from 'react';

import { Scene, Router, Actions, ActionConst } from 'react-native-router-flux';

import Welcome from '../layouts/welcome';

import Login from '../layouts/login';

import Signup from '../layouts/signup';

import NewPost from '../layouts/newPost';

import Feed from '../layouts/feed';

const scenes = Actions.create(

 

   

   

   

   

   

 

);

export default () => (

 

);

We’ve just built a number of scenes that are accessible from wherever in our app using React Native Router Flux.

The Welcome layout is the opening scene, where users can choose to log in or register a new account. It appears as follows:

import React from 'react';

import {

 Container,

 Content,

 Icon,

 Text,

 Button,

} from 'native-base';

import { View } from 'react-native';

import { Actions } from 'react-native-router-flux';

import styles from './styles';

export default () => (

 

   

     

       

       Welcome

     

     

       

       OR

       

     

   

 

)

Here, we’ve only used Native Base to construct two buttons that will direct users to the Login and Signup layouts.

See what occurs when people create a new account by taking a peek at our Signup layout.

import React, { Component } from 'react';

import { connect } from 'react-redux';

import { ImagePicker } from 'expo';

import { Actions } from 'react-native-router-flux';

import {View} from 'react-native';

import {

 Container,

 Content,

 Button,

 Text,

 Form,

 Thumbnail,

 Icon

} from 'native-base';

import axios from 'axios';

import TextField from '../../components/TextField';

import styles from './styles';

import { addUser } from '../../redux/reducers/users';

import cosmicConfig from '../../config/cosmic';

const mapDispatchToProps = {addUser};

const validate = form => {

 let errorMessage = '';

 if (form.username.includes(" ")){

   errorMessage = "Username cannot contain spaces";

 }

 if (form.password.includes(" ")){

   errorMessage = "Password cannot contain spaces";

 }

 Object.keys(form).slice(0, 5).map(field => {

   if (!form[field]){

     errorMessage = 'All fields must be filled';

   }

 })

 return errorMessage;

}

class Signup extends Component {

 constructor() {

   super();

   this.state = {

     firstName: '',

     lastName: '',

     username: '',

     password: '',

     image: null,

     error: '',

   };

 }

 onSubmit(){

   const error = validate(this.state);

   if (error) {

     this.setState({ error })

   } else {

     this.checkUsername(this.state.username);

   }

 }

 checkUsername(username){

   axios.get(`https://api.cosmicjs.com/v1/${cosmicConfig.bucket.slug}/object-type/users/search?metafield_key=username&metafield_value=${username}`)

   .then(res => res.data)

   .then(data => {

     if (data.objects) {

       this.setState({ error: 'Username not available'})

     } else {

       this.props.addUser(this.state);

     }

   })

 }

 uploadImage = async () => {

   let result = await ImagePicker.launchImageLibraryAsync({

     allowsEditing: true,

     aspect: [4, 3],

   });

   if (!result.cancelled) {

     this.setState({ image: result.uri });

   }

 };

 render(){

   return (

     

       

         
            this.setState({firstName: text})}            />             this.setState({lastName: text})}            />             this.setState({username: text})}            />             this.setState({password: text})}            />          
         Add a profile picture          {            !this.state.image &&                      }          {            this.state.image &&                      }                    {this.state.error}                  
     
   );  } } export default connect(null, mapDispatchToProps)(Signup);

Several events might happen in this situation:

  • As users fill out the form, the contents of the fields are kept in state.
  • We perform some basic validation when users submit to make sure they have entered proper data into the fields.
  • In order to confirm that the username they have chosen is not already in use, we then make our first call to the Cosmic JS API.
  • Finally, we utilize our addUserfunction to submit the form as a new user to the Cosmic JS API after all of the fields have valid input.

Our usersreducer contains a definition for the addUser method that looks like this:

export const addUser = user => dispatch => {

 let data = new FormData();

 data.append('media', {

       uri: user.image,

       type: 'image/jpeg',

       name: 'image'

     });

 return axios.post(`https://api.cosmicjs.com/v1/${cosmicConfig.bucket.slug}/media`, data)

 .then(res => res.data.media)

 .then(media => {

   return axios.post(`https://api.cosmicjs.com/v1/${cosmicConfig.bucket.slug}/add-object`, {

     title: user.firstName + ' ' + user.lastName,

     type_slug: 'users',

     metafields: [

       {

         key: 'name',

         type: 'text',

         value: user.firstName + ' ' + user.lastName,

       },

       {

         key: 'username',

         type: 'text',

         value: user.username,

       },

       {

         key: 'password',

         type: 'text',

         value: user.password,

       },

       {

         key: 'profile_picture',

         type: 'file',

         value: media.name,

             }

           ]

         }

       )}

     )

     .then(res => formatUser(res.data))

     .then(formattedUser => dispatch(createUser(formattedUser)))

     .then(() => Actions.feed())

     .catch(err => console.error(`Creating user unsuccessful`, err))

}

Here, we use the Cosmic.js API for more than one time. The user’s profile photo will be added to our bucket as media in response to the first request, and the user’s complete information will be added as a new user in reaction to the second call using a reference to the picture we receive back.

The user can log in if they have previously registered an account by: 

import React, { Component } from 'react';

import { connect } from 'react-redux';

import {

 Container,

 Content,

 Icon,

 Text,

 Button,

} from 'native-base';

import { View } from 'react-native';

import { Actions } from 'react-native-router-flux';

import TextField from '../../components/TextField';

import styles from './styles';

import { authenticate } from '../../redux/reducers/users';

const mapDispatchToProps = {authenticate};

const validate = form => {

 let errorMessage = '';

 if (form.username.includes(' ') || form.password.includes(' ')){

   errorMessage = 'Username and password cannot contain spaces';

 }

 if (form.username === '' || form.password === ''){

   errorMessage = 'All fields must be filled';

 }

 return errorMessage;

}

class Login extends Component {

 constructor(props) {

   super(props);

   this.state = {

     username: '',

     password: '',

     error: '',

   };

 }

 onSubmit(){

   const error = validate(this.state);

   if (error) {

     this.setState({ error })

   } else {

   this.login();

   }

 }

 login(){

   this.props.authenticate(this.state)

     .then(res => {

       if (res === 'Username invalid' || res === 'Password invalid'){

         this.setState({

           error: res,

           username: '',

           password: '',

         })

       } else {

         Actions.feed();

       }

     });

 }

 render(){

   return (

     

       

         {this.state.error}

         

         

            this.setState({username: text})}

           />

            this.setState({password: text})}

           />

         

         

         

       

     

   );

 }

}

export default connect(null, mapDispatchToProps)(Login);

Once more, we verify that the fields contain correct input before using our authenticatefunction to compare the login information to what is in our bucket:

export const authenticate = user => dispatch => {

 return axios.get(`https://api.cosmicjs.com/v1/${cosmicConfig.bucket.slug}/object-type/users/search?metafield_key=username&metafield_value=${user.username}`)

   .then(res => res.data)

   .then(data => {

     console.log('RESPONSE: ', data);

     if (data.objects) {

       const userData = data.objects[0];

       return {

         password: userData.metadata.password,

         username: userData.metadata.username,

         name: userData.metadata.name,

         profilePicture: userData.metadata.profile_picture,

         slug: userData.slug,

         id: userData._id,

       }

     } else {

       return 'Username invalid';

     }

   })

   .then(data => {

     if (data === 'Username invalid'){

       return data;

     } else if (data.password === user.password){

       dispatch(login({

         name: data.name,

         username: data.username,

         profilePicture: data.profilePicture,

         slug: data.slug,

         id: data.id,

       }))

     } else {

       return 'Password invalid';

     }

   })

   .catch(error => console.error('Login unsuccessful', error))

}

As a side note, we shouldn’t ordinarily store user credentials in our database directly without using encryption, but we’ll keep it that way for the time being as a quick example of how we may handle our data using the Cosmic API.

After logging in, users are sent immediately to the Feed layout, which is as follows:

import React, { Component } from 'react';

import { connect } from 'react-redux';

import { Actions } from 'react-native-router-flux';

import {

 Container,

 Content,

 List,

 Button,

 Icon,

 Text,

} from 'native-base';

import SinglePost from '../../components/SinglePost';

import FeedNavbar from '../../components/FeedNavbar';

import { loadPosts } from '../../redux/reducers/posts';

import { logoutUser } from '../../redux/reducers/users';

import styles from './styles';

const mapStateToProps = ({ posts }) => ({ posts });

const mapDispatchToProps = { loadPosts, logoutUser };

const renderPost = (post, index) => (

 

)

class Feed extends Component {

 componentDidMount(){

   this.props.loadPosts();

 }

 render(){

   const endMsg = this.props.posts.length === 0 ? "There aren't any posts yet!" : "That's all the posts for now!"

   return (

     

       

       

         

           {

             !!this.props.posts.length && this.props.posts.map(renderPost)

           }

         

         {endMsg}

       

       

     

   );

 }

}

export default connect(mapStateToProps, mapDispatchToProps)(Feed);

We send a request to the Cosmic API when the Feed layout mounts in order to load all of the posts in our bucket into our app state. In our posts reduction, the loadPostsfunction looks like this:

export const loadPosts = () => dispatch => {

 return axios.get(`https://api.cosmicjs.com/v1/${cosmicConfig.bucket.slug}/object-type/posts`)

   .then(res => res.data.objects ? formatPosts(res.data.objects) : [])

   .then(formattedPosts => formattedPosts.sort(postSorter))

   .then(sortedPosts => dispatch(init(sortedPosts)))

   .catch(err => console.error(`Could not load posts`, err));

};

We extract all of the posts from our bucket, prepare them so we can easily access the information we need, and then put them into the state. They are then shown in the stream.

Users may create new posts by clicking a button on the feed. After that, they are sent to the NewPostlayout:

import React, { Component } from 'react';

import { connect } from 'react-redux';

import {

 Container,

 Content,

 Text,

 Button,

} from 'native-base';

import { View } from 'react-native';

import TextField from '../../components/TextField';

import styles from './styles';

import { createPost } from '../../redux/reducers/posts';

const mapStateToProps = state => ({

 user: state.user,

})

const mapDispatchToProps = { createPost };

class NewPost extends Component {

 constructor(){

   super();

   this.state = {

     content: '',

     error: '',

   }

 }

 onSubmit() {

   if (this.state.content){

     this.props.createPost({

       user: this.props.user,

       content: this.state.content,

     })

   } else {

     this.setState({error: 'You have to write something!'});

   }

 }

 render(){

   return (

     

       

         {this.state.error}

         

            this.setState({content: text})}

           />

           

         

       

     

   );

 }

}

export default connect(mapStateToProps,
mapDispatchToProps)(NewPost);

We’ll add their post to our bucket as soon as they submit it:

return axios.post(`https://api.cosmicjs.com/v1/${cosmicConfig.bucket.slug}/add-object`, {

     title: post.user.username + ' post',

     type_slug: 'posts',

     content: post.content,

     metafields: [

       {

         type: 'object',

         title: 'User',

         key: 'user',

         object_type: 'users',

         value: post.user.id

       },

     ]

   })

     .then(res => formatPost(res.data, post))

     .then(formattedPost => dispatch(create(formattedPost)))

     .then(() => Actions.feed({type: 'popAndReplace'}))

     .catch(error => console.error('Post unsuccessful', error))

}

And now we’ll reroute to the feed once more, bringing up the most recent collection of posts. Users can log out of the Feed and refresh their feed to see any new entries.

Hurray! Our Twitter clone app using React Native app development framework is ready to launch!

Conclusion

We used React Native to create a Twitter-like app that made use of the Cosmic JS API to make it simple to manage all of the user and post data. With a few straightforward operations that POST and Be our data to/from our Cosmic JS Bucket, we were able to get up and running quickly.

It may not be easy to create a Twitter-like app alone, therefore, you need an experienced React Native app development company to help you match your app ideation. Need to hire react native developer? Your search ends here! Get in touch with us to get all you have been looking for in one platform itself. 

The post Building a Twitter Clone App in React Native appeared first on Learn About Digital Transformation & Development | DianApps Blog.



This post first appeared on What Is Metaverse And Where Is It Headed?, please read the originial post: here

Share the post

Building a Twitter Clone App in React Native

×

Subscribe to What Is Metaverse And Where Is It Headed?

Get updates delivered right to your inbox!

Thank you for your subscription

×