React Native Practical Interview Questions and Answers

React Native vs. React
What is React Native?

React Native is a framework developed by Meta (formerly Facebook) for building mobile applications using JavaScript and React. It allows developers to create cross-platform apps that work on iOS and Android using a single codebase.

What is React?

React (or React.js) is a JavaScript library used for building web applications with a component-based architecture. It focuses on building interactive user interfaces (UIs) efficiently.


Key Differences Between React and React Native
Feature React (React.js) React Native
Platform Web applications Mobile applications (iOS, Android)
Rendering Uses the DOM (Document Object Model) Uses Native Components (e.g., <View> instead of <div>)
Styling Uses CSS, SCSS, styled-components Uses StyleSheet API (similar to inline styles in JS)
Navigation Uses React Router for routing Uses React Navigation or Native Navigation
Performance Renders using the browser engine Uses native components, making it faster than web apps
APIs & Libraries Uses browser APIs (e.g., fetch, localStorage) Has access to mobile-specific APIs (e.g., camera, geolocation, push notifications)
Compilation Runs in the browser without additional compilation Uses a bridge to convert JavaScript into native code

Should You Use React or React Native?
  • Use React if you're building a web application.
  • Use React Native if you're building a mobile app that needs to work on both iOS and Android.

You can create a new React Native project using Expo CLI (easier setup) or React Native CLI (more control). Here’s how:

1. Using Expo CLI (Recommended for Beginners)

Expo is a framework that simplifies React Native development by handling configurations.

Step 1: Install Node.js and npm

Make sure you have Node.js installed. You can check by running:

node -v
npm -v

If not installed, download it from nodejs.org.

Step 2: Install Expo CLI

Run the following command to install Expo CLI globally:

npm install -g expo-cli
Step 3: Create a New Expo Project

Run:

npx create-expo-app MyNewApp

Replace MyNewApp with your project name.

Step 4: Navigate to Your Project
cd MyNewApp
Step 5: Start the Development Server
npm start

or

expo start

This will open the Expo Developer Tools in your browser. You can scan the QR code using the Expo Go app on your phone to run the app.


2. Using React Native CLI (For Advanced Users)

If you need more control (e.g., native modules, deeper customization), use React Native CLI.

Step 1: Install Node.js, npm, and Watchman
  • Install Node.js (from nodejs.org)
  • Install Watchman (macOS only):
    brew install watchman
    
Step 2: Install React Native CLI

You don't need to install it globally. Just use:

npx react-native init MyNewApp
Step 3: Navigate to Your Project
cd MyNewApp
Step 4: Run the App

For Android:

npx react-native run-android

For iOS (Mac + Xcode required):

npx react-native run-ios

Note: You need to have Android Studio (for Android) or Xcode (for iOS) set up properly.


Which One Should You Use?
  • Expo CLI → Best for quick development, no native code modifications.
  • React Native CLI → Best for full control over the native code.
React Native CLI vs. Expo: Key Differences
Feature React Native CLI Expo
Setup Complexity More complex (requires installing Xcode, Android Studio) Easier (no need for native dependencies)
Native Code Access Full access to native code (Objective-C, Swift, Java, Kotlin) Limited access (some native modules not supported)
App Size Smaller (can optimize native dependencies) Larger (includes extra dependencies for ease of use)
Performance Faster (optimized for production) Slightly slower (due to extra libraries)
Third-Party Libraries Supports all third-party native modules Limited support for some native libraries
OTA (Over-the-Air) Updates Manual (requires app store updates) Built-in with Expo Updates
Ejecting Not needed (full control from the start) Requires Ejecting to use custom native code
Best for Advanced users, production apps with custom native modules Beginners, rapid prototyping, simple apps

When to Use React Native CLI

* You need full control over native code.
* You are integrating third-party native libraries that Expo doesn’t support.
* You need optimized performance for production apps.

Example : Apps with complex native integrations like custom video processing, Bluetooth, background services, or AR/VR.


When to Use Expo

* You want a quick and easy setup.
* You don’t need deep native code modifications.
* You need over-the-air updates without app store approvals.

Example : Prototyping, MVPs, internal tools, or small to medium-sized apps with no custom native code requirements.


What If You Start with Expo But Need More?

You can eject from Expo using:

npx expo prebuild

This converts your Expo project into a standard React Native CLI project.


Final Verdict
  • If you're new to React Native, go with Expo.
  • If you need full control or advanced features, go with React Native CLI.
Styling in React Native

React Native does not use traditional CSS like web development. Instead, it uses a JavaScript-based StyleSheet API similar to inline styles.

1. Inline Styles (Basic)

You can apply styles directly to components using JavaScript objects.

import React from 'react';
import { Text, View } from 'react-native';

const App = () => {
  return (
    <View>
      <Text style={{ color: 'blue', fontSize: 20 }}>Hello, React Native!</Text>
    </View>
  );
};

export default App;

* Pros: Quick and easy for small styles.
* Cons: Hard to maintain for larger projects.


2. Using the StyleSheet API (Recommended)

The StyleSheet.create method helps optimize styles by preventing unnecessary re-renders.

import React from 'react';
import { Text, View, StyleSheet } from 'react-native';

const App = () => {
  return (
    <View style={styles.container}>
      <Text style={styles.text}>Styled with StyleSheet!</Text>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#f0f0f0',
  },
  text: {
    fontSize: 20,
    color: 'blue',
    fontWeight: 'bold',
  },
});

export default App;

* Pros: Improves performance, easier to maintain.
* Cons: Still lacks CSS features like media queries.


3. Using Styled Components (CSS-in-JS)

Styled-components allow writing styles similarly to CSS but in JavaScript.

Installation :
npm install styled-components

or

yarn add styled-components
Usage
import React from 'react';
import { Text, View } from 'react-native';
import styled from 'styled-components/native';

const Container = styled(View)`
  flex: 1;
  justify-content: center;
  align-items: center;
  background-color: #f0f0f0;
`;

const StyledText = styled(Text)`
  font-size: 20px;
  color: blue;
  font-weight: bold;
`;

const App = () => {
  return (
    <Container>
      <StyledText>Styled with Styled Components!</StyledText>
    </Container>
  );
};

export default App;

* Pros: Looks like CSS, easy to manage styles.
* Cons: Can affect performance slightly.


4. Platform-Specific Styling (Platform API)

Use Platform API to style differently based on iOS or Android.

import { Platform, StyleSheet } from 'react-native';

const styles = StyleSheet.create({
  text: {
    fontSize: 20,
    color: Platform.OS === 'ios' ? 'blue' : 'green',
  },
});

* Pros: Customize styles for each platform.
* Cons: Adds complexity.


5. Global Styles Using Context or Themes

For global styles, you can use:

  • React Context API
  • Theme providers (like in styled-components)
  • External libraries like react-native-paper or tailwind-rn

Example using styled-components theme:

import { ThemeProvider } from 'styled-components/native';

const theme = {
  colors: {
    primary: 'blue',
    background: '#f0f0f0',
  },
};
<ThemeProvider theme={theme}>
  <App />
</ThemeProvider>;

Which Styling Method Should You Use?
Method Best For
Inline Styles Quick styling, small components
StyleSheet API Most recommended, optimized
Styled Components Better readability, theme-based apps
Platform API Platform-specific styles
Global Themes Large apps with consistent design
Primary points to note to integrating React Native components into your Android application are to:

* Set up React Native dependencies and directory structure.
* Develop your React Native components in JavaScript.
* Add a ReactRootView to your Android app. This view will serve as the container for your React Native component.
* Start the React Native server and run your native application.
* Lastly, we need to Verify that the React Native aspect of your application works as expected.
Flexbox in React Native

React Native uses Flexbox for layout, just like CSS, but with some differences. Flexbox helps create responsive UIs that work on different screen sizes.


1. Main Flexbox Properties

Here are the key Flexbox properties in React Native:

Property Description
flex Defines how much space an item should take.
flexDirection Sets the direction of children (row or column).
justifyContent Aligns children along the main axis.
alignItems Aligns children along the cross axis.
alignSelf Aligns a single item on the cross axis.
flexWrap Wraps items if they exceed the container size.
gap, rowGap, columnGap Adds spacing between elements (React Native 0.71+).

2. How flex Works

The flex property determines how much space an item takes.

<View style={{ flex: 1, backgroundColor: 'lightblue' }} />
<View style={{ flex: 2, backgroundColor: 'lightgreen' }} />

* The second view takes twice the space of the first.


3. flexDirection: Row vs. Column

By default, React Native uses column, unlike CSS, which defaults to row.

<View style={{ flexDirection: 'row' }}>
  <View style={{ width: 50, height: 50, backgroundColor: 'red' }} />
  <View style={{ width: 50, height: 50, backgroundColor: 'blue' }} />
</View>

* Options:

  • row → Horizontal layout
  • column (default) → Vertical layout
  • row-reverse → Reverse horizontal
  • column-reverse → Reverse vertical

4. justifyContent: Align Items on the Main Axis

Used to position items along the main axis (horizontal for row, vertical for column).

<View style={{ flex: 1, justifyContent: 'center' }}>
  <Text>Centered</Text>
</View>

* Values :

  • flex-start (default) → Items start at the beginning
  • center → Items centered
  • flex-end → Items at the end
  • space-between → Space between items
  • space-around → Space around items
  • space-evenly → Equal spacing

5. alignItems: Align Items on the Cross Axis

Controls alignment along the cross axis (opposite of justifyContent).

<View style={{ flex: 1, alignItems: 'center' }}>
  <Text>Aligned Center</Text>
</View>

* Values :

  • flex-start → Items at start
  • center → Items in the middle
  • flex-end → Items at end
  • stretch (default) → Items stretch to fill the container

6. alignSelf: Align a Single Item

Overrides alignItems for a specific child.

<Text style={{ alignSelf: 'flex-end' }}>I move alone</Text>

7. flexWrap: Wrapping Items

By default, items stay in a single row/column. Use flexWrap to wrap them.

<View style={{ flexDirection: 'row', flexWrap: 'wrap' }}>
  <View style={{ width: 100, height: 50, backgroundColor: 'red' }} />
  <View style={{ width: 100, height: 50, backgroundColor: 'blue' }} />
  <View style={{ width: 100, height: 50, backgroundColor: 'green' }} />
</View>

* Values :

  • nowrap (default) → No wrapping
  • wrap → Items wrap to the next line

8. gap, rowGap, columnGap (React Native 0.71+)

These properties add spacing between items.

<View style={{ flexDirection: 'row', gap: 10 }}>
  <View style={{ width: 50, height: 50, backgroundColor: 'red' }} />
  <View style={{ width: 50, height: 50, backgroundColor: 'blue' }} />
</View>

*  rowGap: Controls gap between rows.
*  columnGap: Controls gap between columns.


9. Example: Responsive Layout Using Flexbox
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';

const App = () => {
  return (
    <View style={styles.container}>
      <View style={styles.box}><Text>1</Text></View>
      <View style={styles.box}><Text>2</Text></View>
      <View style={styles.box}><Text>3</Text></View>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    backgroundColor: '#f0f0f0',
  },
  box: {
    width: 100,
    height: 100,
    backgroundColor: 'skyblue',
    justifyContent: 'center',
    alignItems: 'center',
  },
});

export default App;
* Breakdown :

*  flex: 1 → Takes up the full screen.
*  flexDirection: 'row' → Places items in a row.
*  justifyContent: 'space-between' → Evenly spaces items.
*  alignItems: 'center' → Centers items vertically.


Which Properties Should You Use?
Use Case Recommended Properties
Stack items horizontally flexDirection: 'row'
Center an item inside a container justifyContent: 'center', alignItems: 'center'
Distribute items evenly justifyContent: 'space-between' or space-around
Allow items to wrap flexWrap: 'wrap'
Apply spacing between elements gap, rowGap, columnGap
How to Optimize Performance in React Native

Performance optimization is crucial for a smooth user experience in React Native apps. Here are the best techniques:


1. Use useMemo and useCallback to Prevent Unnecessary Renders

React components re-render when props or state change. Use useMemo and useCallback to optimize performance.

* Solution: Memoization
  • useMemo: Caches expensive calculations.
  • useCallback: Caches function instances.
import React, { useState, useMemo, useCallback } from 'react';
import { View, Text, Button } from 'react-native';

const ExpensiveComponent = ({ count }) => {
  const computedValue = useMemo(() => {
    console.log('Recalculating...');
    return count * 2;
  }, [count]);

  return <Text>Computed Value: {computedValue}</Text>;
};

const App = () => {
  const [count, setCount] = useState(0);

  const increment = useCallback(() => setCount(count + 1), [count]);

  return (
    <View>
      <ExpensiveComponent count={count} />
      <Button title="Increment" onPress={increment} />
    </View>
  );
};

export default App;

* Reduces re-renders, improving performance.


2. Use React.memo to Avoid Re-Rendering Unchanged Components

React re-renders child components even if their props haven’t changed. Use React.memo to prevent this.

import React, { useState } from 'react';
import { View, Text, Button } from 'react-native';

const MemoizedText = React.memo(({ text }) => {
  console.log('Rendering MemoizedText');
  return <Text>{text}</Text>;
});

const App = () => {
  const [count, setCount] = useState(0);

  return (
    <View>
      <MemoizedText text="This component won't re-render" />
      <Text>Count: {count}</Text>
      <Button title="Increment" onPress={() => setCount(count + 1)} />
    </View>
  );
};

export default App;

* Prevents unnecessary re-renders of static components.


3. Optimize FlatList and ScrollView

When dealing with large lists, avoid using ScrollView. Instead, use FlatList, which efficiently renders only the visible items.

* Bad (Performance Issue)
<ScrollView>
  {data.map(item => (
    <Text key={item.id}>{item.name}</Text>
  ))}
</ScrollView>

* ScrollView loads all items into memory at once, causing slowdowns.

* Good (Optimized)
<FlatList
  data={data}
  keyExtractor={item => item.id}
  renderItem={({ item }) => <Text>{item.name}</Text>}
  initialNumToRender={10} // Render only 10 items initially
  maxToRenderPerBatch={10} // Load items in batches
  windowSize={5} // Keep only 5 screens worth of items in memory
/>

* FlatList only renders visible items, improving performance.


4. Optimize Images (Use react-native-fast-image)

Large, unoptimized images can slow down your app.

* Solution: Use react-native-fast-image

Install it:

npm install react-native-fast-image

Usage:

import FastImage from 'react-native-fast-image';

<FastImage
  style={{ width: 200, height: 200 }}
  source={{ uri: 'https://example.com/image.jpg' }}
  resizeMode={FastImage.resizeMode.cover}
/>
* Faster image loading with caching.

5. Use the InteractionManager for Heavy Tasks

To avoid blocking UI updates, defer heavy tasks using InteractionManager.

import { InteractionManager } from 'react-native';

useEffect(() => {
  const task = InteractionManager.runAfterInteractions(() => {
    // Perform expensive operations here
  });

  return () => task.cancel();
}, []);

* Ensures animations and UI remain smooth.


6. Reduce setState Calls

Every time you update state, React re-renders the component. Minimize unnecessary state updates.

* Bad (Inefficient)
const [count, setCount] = useState(0);

const increment = () => {
  setCount(count + 1);
  setCount(count + 2);
};

* Causes multiple re-renders.

* Good (Optimized)
const increment = () => {
  setCount(prev => prev + 2);
};
* Batch updates reduce re-renders.

7. Enable Hermes Engine (For Android Performance)

Hermes is a lightweight JavaScript engine optimized for React Native.

* Enable Hermes

Edit android/app/build.gradle:

enableHermes: true

Then rebuild the project:

cd android && ./gradlew clean
cd .. && npx react-native run-android

* Improves startup time and reduces memory usage.


8. Optimize Animations (Use react-native-reanimated)

Use react-native-reanimated for smoother animations instead of Animated.

* Install react-native-reanimated
npm install react-native-reanimated
* Usage
import Animated, { Easing } from 'react-native-reanimated';

const animation = new Animated.Value(0);

Animated.timing(animation, {
  toValue: 1,
  duration: 500,
  easing: Easing.linear,
}).start();
* More efficient animations with better FPS.

9. Optimize Redux with redux-persist

If you use Redux, persist the state to avoid unnecessary API calls.

* Install redux-persist
npm install redux-persist
* Usage
import { persistStore, persistReducer } from 'redux-persist';
import AsyncStorage from '@react-native-async-storage/async-storage';

const persistConfig = { key: 'root', storage: AsyncStorage };
const persistedReducer = persistReducer(persistConfig, rootReducer);

* Reduces redundant API calls by caching state.


10. Use useNativeDriver for Animations

Animations in React Native should offload work to the GPU.

Animated.timing(animatedValue, {
  toValue: 1,
  duration: 500,
  useNativeDriver: true,
}).start();

* Prevents UI thread lag.


Final Summary
Optimization Tip Benefit
useMemo & useCallback Prevents unnecessary re-renders
React.memo Caches components
Use FlatList instead of ScrollView Improves list performance
Use react-native-fast-image Faster image loading
Use InteractionManager Defer heavy tasks
Minimize setState calls Reduces unnecessary re-renders
Enable Hermes (Android) Speeds up execution
Use react-native-reanimated Improves animation smoothness
Persist Redux state Avoids redundant API calls
Use useNativeDriver Runs animations on the GPU
TextInput is a Core Component that allows the user to enter text. It has an onChangeText prop that takes a function to be called every time the text changes, and an onSubmitEditing prop that takes a function to be called when the text is submitted.
import React, { useState } from 'react';
import { Text, TextInput, View } from 'react-native';

const PizzaTranslator = () => {
 const [text, setText] = useState('');
 return (
   <View style={{padding: 10}}>
     <TextInput
       style={{height: 40}}
       placeholder="Type here to translate!"
       onChangeText={text => setText(text)}
       defaultValue={text}
     />
     <Text style={{padding: 10, fontSize: 42}}>
       {text.split(' ').map((word) => word && '?').join(' ')}
     </Text>
   </View>
 );
}

export default PizzaTranslator;?
Yes, default props available in React Native as they are for React,  If for an instance we do not pass props value, the component will use the default props value.
import React, {Component} from 'react';

import {View, Text} from 'react-native';
class DefaultPropComponent extends Component {
   render() {
       return (
           <View>
             <Text>
              {this.props.name}
            </Text>
          </View>
       )
   }
}
Demo.defaultProps = {
   name: 'BOB'
}

export default DefaultPropComponent;?
Since its launch in 2015, React Native has built a reputation as a reliable and effective JavaScript framework. Some of its key strengths include:

* Cross-platform compatibility : Most of the code is cross-platform, meaning developers only have to create one app rather than two separate apps for both iOS and Android

* Real-time feedback : React Native offers a ‘hot reloading’ feature where developers can immediately view the changes they’ve made in a separate preview window

* Flexible user interface : React Native’s interface is slick and makes it easy for multiple developers to work on a project together

* Third-party plugins : React Native is compatible with many third-party plugins that can be used to support and improve the app development process

* Community : As a popular open-source framework, React Native has a large community of developers that exchange knowledge
Hybrid apps are developed to be used across all platforms, whereas native apps are developed for a particular platform. React Native is used for the development of hybrid apps.

While hybrid apps are faster to develop and typically require less maintenance than native apps, they may perform slightly worse than their native counterparts.
Components are the building blocks of React Native; when combined, they make up the app as a whole. Some of the most common components are :

* View, used to display the entire app layout

* Text, used to display text

* TextInput, used to input text

* ScrollView, used to insert a scrolling container

* StyleSheet, used to insert style objects

* Image, used to render images

* Button, used to insert buttons
Component-driven development (CDD) is a development methodology where the build process is anchored around components rather than objects. Components are loosely coupled and each one serves its own purpose.

When put together, components (buttons, navigation bars, images) form the program as a whole. React Native is a component-driven framework.
Importing Components in React Native

In React Native, you import components using JavaScript's import statement.


1. Importing Built-in Components

React Native provides core components like View, Text, Button, etc.

import { View, Text, Button } from 'react-native';

* Example Usage :

import React from 'react';
import { View, Text, Button } from 'react-native';

const App = () => {
  return (
    <View>
      <Text>Hello, React Native!</Text>
      <Button title="Click Me" onPress={() => alert('Button Pressed!')} />
    </View>
  );
};

export default App;

2. Importing Custom Components

For better code organization, you can create reusable custom components in separate files.

Step 1: Create a Component (MyComponent.js)
import React from 'react';
import { Text } from 'react-native';

const MyComponent = () => {
  return <Text style={{ fontSize: 20 }}>Hello from MyComponent!</Text>;
};

export default MyComponent;
Step 2: Import and Use the Component
import React from 'react';
import { View } from 'react-native';
import MyComponent from './MyComponent'; // Importing the custom component

const App = () => {
  return (
    <View>
      <MyComponent />
    </View>
  );
};

export default App;

3. Importing Components with Aliases (as)

You can rename imported components using as.

import { Text as RNText, View as RNView } from 'react-native';
* Usage :
<RNView>
  <RNText>Hello with alias!</RNText>
</RNView>

4. Importing Default vs Named Exports
* Default Export (Import without {})
  • Used when a module exports a single component.
// MyComponent.js
export default function MyComponent() {
  return <Text>Hello!</Text>;
}

// Import it without curly braces
import MyComponent from './MyComponent';
* Named Export (Import with {})
  • Used when a module exports multiple components.
// MyComponents.js
export const Header = () => <Text>Header</Text>;
export const Footer = () => <Text>Footer</Text>;

// Import specific components
import { Header, Footer } from './MyComponents';

5. Importing Components from Third-Party Libraries

If you install a library like react-native-vector-icons:

npm install react-native-vector-icons

You import components like this:

import Icon from 'react-native-vector-icons/FontAwesome';

<Icon name="home" size={30} color="blue" />

6. Importing Everything (*)

You can import all exports from a module as a single object.

import * as MyComponents from './MyComponents';

<MyComponents.Header />
<MyComponents.Footer />

Summary of Import Methods
Import Type Syntax
Core Component import { View, Text } from 'react-native';
Custom Component import MyComponent from './MyComponent';
Named Exports import { Header, Footer } from './MyComponents';
Import with Alias import { Text as RNText } from 'react-native';
Import All (*) import * as MyComponents from './MyComponents';

In React Native (and React in general), components are the building blocks of your user interface. Historically, there were two main ways to define components: class components and functional components. However, with the introduction of React Hooks, functional components have become the dominant and recommended approach. Here's a breakdown:

Class Components:

  • Definition:
    • Class components are JavaScript classes that extend React.Component.
    • They must have a render() method that returns the JSX (JavaScript XML) that defines the component's UI.
  • Features:
    • They can maintain their own internal state using this.state.
    • They have access to lifecycle methods (e.g., componentDidMount, componentDidUpdate, componentWillUnmount), which allow you to perform actions at specific points in the component's lifecycle.
  • Example :
  • import React, { Component } from 'react';
    import { Text } from 'react-native';
    
    class MyClassComponent extends Component {
      render() {
        return <Text>Hello from a class component!</Text>;
      }
    }
    
    export default MyClassComponent;

     

Functional Components:

  • Definition:
    • Functional components are JavaScript functions that accept props as arguments and return JSX.
    • They are simpler and more concise than class components.
  • Features:
    • With the introduction of React Hooks, functional components can now manage state using useState and perform side effects using useEffect, effectively replicating the capabilities of class components.
    • They tend to be easier to read and test.
  • Example :
  • import React from 'react';
    import { Text } from 'react-native';
    
    const MyFunctionalComponent = () => {
      return <Text>Hello from a functional component!</Text>;
    };
    
    export default MyFunctionalComponent;

Key Differences and the Impact of Hooks :

  • State Management:
    • Before Hooks, only class components could manage state.
    • Now, functional components can manage state using the useState hook.
  • Lifecycle Methods:
    • Class components have lifecycle methods.
    • Functional components can achieve similar effects using the useEffect hook.
  • Simplicity:
    • Functional components are generally simpler and more concise.
  • Hooks:
    • React Hooks have significantly changed how we write React components. They allow functional components to "hook into" React state and lifecycle features. This has made functional components the preferred choice for most React developers.
The core components used in React Native are <View> , <Text> , <Image> , <ScrollView> , <TextInput>

And analogy when compared Web can be explained by below :

REACT NATIVE UI COMPONENT ANDROID VIEW IOS VIEW WEB ANALOG DESCRIPTION
<View> <ViewGroup> <UIView> A non-scrolling <div> A container that supports layout with flexbox style, some touch handling, and accessibility controls.
<Text> <TextView> <UITextView> <p> Displays, styles, and nests strings of text and even handles touch events.
<Image> <ImageView> <UIImageView> <img> Displays different types of images
<ScrollView> <ScrollView> <UIScrollView> <div> A generic scrolling container that can contain multiple components and views.
<TextInput> <EditText> <UITextField> <input type="text"> Allows the user to enter text
Async Storage is the React Native equivalent of Local Storage from the web.

Async Storage is a community-maintained module for React Native that provides an asynchronous, unencrypted, key-value store. Async Storage is not shared between apps: every app has its own sandbox environment and has no access to data from other apps.

DO USE ASYNC STORAGE WHEN.. DON'T USE ASYNC STORAGE FOR..
Persisting non-sensitive data across app runs Token storage
Persisting Redux state Secrets
Persisting GraphQL state  
Storing global app-wide variables  
One of the popular libraries for routing and navigation in a React Native application is React Navigation.

This library helps solve the problem of navigating between multiple screens and sharing data between them.

import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';

const Stack = createStackNavigator();

const MyStack = () => {
 return (
   <NavigationContainer>
     <Stack.Navigator>
       <Stack.Screen
         name="Home"
         component={HomeScreen}
         options={{ title: 'Welcome' }}
       />
       <Stack.Screen name="Profile" component={ProfileScreen} />
     </Stack.Navigator>
   </NavigationContainer>
 );
};?
The FlatList component displays similarly structured data in a scrollable list. It works well for large lists of data where the number of list items might change over time.

Key Feature :

The FlatList shows only those rendered elements which are currently displaying on the screen, not all the elements of the list at once.
import React, { Component } from 'react';  
import { AppRegistry, FlatList,  
   StyleSheet, Text, View,Alert } from 'react-native';  

export default class FlatListBasics extends Component {  
 
   renderSeparator = () => {  
       return (  
           <View  
               style={{  
                   height: 1,  
                   width: "100%",  
                   backgroundColor: "#000",  
               }}  
           />  
       );  
   };  
   //handling onPress action  
   getListViewItem = (item) => {  
       Alert.alert(item.key);  
   }  
 
   render() {  
       return (  
           <View style={styles.container}>  
               <FlatList  
                   data={[  
                       {key: 'Android'},{key: 'iOS'}, {key: 'Java'},{key: 'Swift'},  
                       {key: 'Php'},{key: 'Hadoop'},{key: 'Sap'},  
                   ]}  
                   renderItem={({item}) =>  
                       <Text style={styles.item}  
                             onPress={this.getListViewItem.bind(this, item)}>{item.key}</Text>}  
                   ItemSeparatorComponent={this.renderSeparator}  
               />  
           </View>  
       );  
   }  
}  
AppRegistry.registerComponent('AwesomeProject', () => FlatListBasics);?
Tapping gestures can be captured by Touchable components and can display feedback when a gesture is recognized.

Depending on what kind of feedback you want to provide we choose Touchable Components.

Generally, we use TouchableHighlight anywhere you would use a button or link on the web. The background of the view will be darkened when the user presses down on the button.

We can use TouchableNativeFeedback on Android to display ink surface reaction ripples that respond to the user's touch.

TouchableOpacity can be used to provide feedback by reducing the opacity of the button, allowing the background to be seen through while the user is pressing down.

If we need to handle a tap gesture but you don't want any feedback to be displayed, use TouchableWithoutFeedback.
import React, { Component } from 'react';
import { Platform, StyleSheet, Text, TouchableHighlight, TouchableOpacity, TouchableNativeFeedback, TouchableWithoutFeedback, View } from 'react-native';
export default class Touchables extends Component {
_onPressButton() {
   alert('You tapped the button!')  }
 _onLongPressButton() {
   alert('You long-pressed the button!')
 }
render() {
return (
<View style={styles.container}>
<TouchableHighlight onPress={this._onPressButton} underlayColor="white">
<View style={styles.button}>
<Text style={styles.buttonText}>TouchableHighlight</Text>
</View>
</TouchableHighlight>
);}
}?
The platform module detects the platform in which the app is running.
import { Platform, Stylesheet } from 'react-native';
const styles = Stylesheet.create({
height: Platform.OS === 'IOS' ? 200 : 400
})?

Additionally Platform.select method available that takes an object containing Platform.OS as keys and returns the value for the platform you are currently on.
import { Platform, StyleSheet } from 'react-native';
const styles = StyleSheet.create({
 container: {
flex: 1,
   ...Platform.select({
     ios: {
       backgroundColor: 'red',
     },
     android: {
       backgroundColor: 'green',
     },
     default: {
       // other platforms, web for example
       backgroundColor: 'blue',
     },    }),
},
});?
React Native ListView is a view component that contains the list of items and displays it in a vertically scrollable list.
export default class MyListComponent extends Component {  
constructor() {  
super();  
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});  
this.state = {  
dataSource: ds.cloneWithRows(['Android','iOS', 'Java','Php', 'Hadoop', 'Sap', 'Python','Ajax', 'C++']),
};
}  
render() {  
return (
<ListView
dataSource={this.state.dataSource}  
renderRow={  
(rowData) =>  
<Text style={{fontSize: 30}}>{rowData}</Text>} />  
); }  
}?
* At the first start of the app, the main thread starts execution and starts loading JS bundles.

* When JavaScript code has been loaded successfully, the main thread sends it to another JS thread because when JS does some heavy calculations stuff the thread for a while, the UI thread will not suffer at all times.

* When React starts rendering, Reconciler starts “diffing”, and when it generates a new virtual DOM(layout) it sends changes to another thread(Shadow thread).

* Shadow thread calculates layout and then sends layout parameters/objects to the main(UI) thread. ( Here you may wonder why we call it “shadow”? It’s because it generates shadow nodes )

* Since only the main thread is able to render something on the screen, the shadow thread should send the generated layout to the main thread, and only then UI renders.
The real cause behind React Native performance issues is that each thread (i.e Native and JS thread) is blazingly fast. The performance bottleneck in React Native app occurs when you’re passing the components from one thread to another unnecessarily or more than required. A major thumb rule to avoid any kind of performance-related issue in React Native is to keep the passes over the bridge to a minimum.

* Native thread built for running Java/ Kotlin, Swift/ Objective C.

* Javascript thread is the primary thread that runs everything from javascript-based animations to other UI components.

* The bridge as the name suggests acts as an  intermediate communication point for the native and JS thread.
* Use Proguard to minimize the application size.(It does this by stripping parts of the React Native Java bytecode (and its dependencies) that your app is not using)

* Create reduced-sized APK files for specific CPU architectures. When you do that, your app users will automatically get the relevant APK file for their specific phone’s architecture. This eliminates the need to keep JSCore binaries that support multiple architectures and consequently reduces the app size.

* Compress images and other graphic elements. Another option to reduce image size is using file types like APNG in place of PNG files.

* Don’t store raw JSON data,  eIther we need to Compress it or convert it into static object IDs.

* Optimize native libraries.

* Optimize the number of state operations and remember to use pure and memoized components when needed

* Use Global State wisely for example worst-case scenario is when state change of single control like TextInput or CheckBox propagates render of the whole application. Use libraries like Redux or Overmind.js to handle your state management in a more optimized way.

* Use key attribute on list items, it helps React Native to pick which list to update when rendering a long list of data

* Use VirtualizedList, FlatList, and SectionList for large data sets.

* Clear all the active timers which may lead to heavy memory leakage issues.
Making API Calls in React Native

In React Native, you can make API calls using fetch, Axios, or libraries like React Query for better state management.


1. Using fetch (Built-in JavaScript Method)

The fetch API is the easiest way to make network requests in React Native.

GET Request Example
import React, { useEffect, useState } from 'react';
import { View, Text, ActivityIndicator } from 'react-native';

const App = () => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch('https://jsonplaceholder.typicode.com/posts/1')
      .then(response => response.json())
      .then(json => {
        setData(json);
        setLoading(false);
      })
      .catch(error => console.error('Error fetching data:', error));
  }, []);

  return (
    <View>
      {loading ? (
        <ActivityIndicator size="large" color="blue" />
      ) : (
        <Text>{data?.title}</Text>
      )}
    </View>
  );
};

export default App;

* Handles API calls, updates state, and displays a loading indicator.


2. Using fetch with async/await

For better readability, use async/await.

const fetchData = async () => {
  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
    const json = await response.json();
    setData(json);
  } catch (error) {
    console.error('Error:', error);
  }
};

* Cleaner syntax with error handling.


3. Making a POST Request

Send data to an API using fetch with POST method.

const sendData = async () => {
  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ title: 'React Native', body: 'Hello API!', userId: 1 }),
    });

    const json = await response.json();
    console.log('Response:', json);
  } catch (error) {
    console.error('Error:', error);
  }
};

* Sends JSON data to an API.


4. Using Axios (Alternative to fetch)

Axios is a popular library for API calls because it supports automatic JSON conversion and better error handling.

Install Axios
npm install axios
GET Request with Axios
import axios from 'axios';

const fetchData = async () => {
  try {
    const response = await axios.get('https://jsonplaceholder.typicode.com/posts/1');
    console.log(response.data);
  } catch (error) {
    console.error('Error:', error);
  }
};

* Easier error handling and cleaner syntax.


5. Using React Query for Caching & Background Fetching

React Query helps with caching and automatic refetching.

Install React Query
npm install @tanstack/react-query
Setup React Query
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query';
import { View, Text, ActivityIndicator } from 'react-native';
import axios from 'axios';

const queryClient = new QueryClient();

const fetchPost = async () => {
  const { data } = await axios.get('https://jsonplaceholder.typicode.com/posts/1');
  return data;
};

const Post = () => {
  const { data, isLoading } = useQuery(['post'], fetchPost);

  if (isLoading) return <ActivityIndicator size="large" color="blue" />;
  return <Text>{data.title}</Text>;
};

const App = () => (
  <QueryClientProvider client={queryClient}>
    <Post />
  </QueryClientProvider>
);

export default App;

* Caches responses and automatically refetches data when needed.


6. Handling API Errors Properly

Always include error handling when making API calls.

const fetchData = async () => {
  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
    if (!response.ok) throw new Error('Network response was not ok');
    const json = await response.json();
    setData(json);
  } catch (error) {
    console.error('API Error:', error.message);
  }
};

* Prevents crashes by catching API errors.


7. Optimizing API Calls

* Use caching (e.g., React Query, Redux Toolkit Query).
* Debounce API calls to avoid excessive requests.
* Use background fetch for real-time updates.
* Paginate large datasets (use ?page=1&limit=10).

AsyncStorage vs. SecureStore vs. Redux Persist in React Native

In React Native, AsyncStorage, SecureStore, and Redux Persist are used for storing and persisting data. Each has different use cases and security levels.


1. AsyncStorage (Built-in Storage)

AsyncStorage is a key-value storage system that allows you to store data persistently on the device.

* When to Use :
  • Storing small amounts of non-sensitive data (e.g., user preferences, app settings).
  • Caching API responses for offline access.
* Limitations :
  • Not secure (data is stored as plain text).
  • Slower performance for large datasets.
Installation :
npm install @react-native-async-storage/async-storage
Usage Example :
import AsyncStorage from '@react-native-async-storage/async-storage';

// Save data
const saveData = async () => {
  await AsyncStorage.setItem('username', 'JohnDoe');
};

// Retrieve data
const getData = async () => {
  const value = await AsyncStorage.getItem('username');
  console.log(value);
};

// Remove data
const removeData = async () => {
  await AsyncStorage.removeItem('username');
};

* Easy to use but not recommended for storing sensitive data.


2. SecureStore (Encrypted Storage)

SecureStore (from expo-secure-store) is a secure storage solution that encrypts data, making it ideal for storing sensitive information like tokens, passwords, and user credentials.

* When to Use :
  • Storing sensitive data (e.g., authentication tokens, passwords).
  • When security is a priority.
* Limitations :
  • Works only on physical devices (not on emulators/simulators).
  • Limited storage size (better for small pieces of sensitive data).
Installation (Expo Users Only) :
expo install expo-secure-store
Usage Example :
import * as SecureStore from 'expo-secure-store';

// Save data securely
const saveSecureData = async () => {
  await SecureStore.setItemAsync('token', 'secure-token-123');
};

// Retrieve data
const getSecureData = async () => {
  const value = await SecureStore.getItemAsync('token');
  console.log(value);
};

// Remove data
const removeSecureData = async () => {
  await SecureStore.deleteItemAsync('token');
};

* Best choice for storing sensitive user information.


3. Redux Persist (State Persistence)

Redux Persist is used to persist Redux state across app restarts. It integrates with storage solutions like AsyncStorage.

* When to Use :
  • Persisting global state (e.g., user authentication state, theme settings).
  • Avoiding data loss when the app is restarted.
* Limitations :
  • Can only store Redux state, not general app data.
  • Requires Redux as a dependency.
Installation :
npm install redux-persist @react-native-async-storage/async-storage
Usage Example :
import { persistStore, persistReducer } from 'redux-persist';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { createStore } from 'redux';

// Define a reducer
const reducer = (state = { user: null }, action) => {
  switch (action.type) {
    case 'SET_USER':
      return { ...state, user: action.payload };
    default:
      return state;
  }
};

// Configure Redux Persist
const persistConfig = { key: 'root', storage: AsyncStorage };
const persistedReducer = persistReducer(persistConfig, reducer);
const store = createStore(persistedReducer);
const persistor = persistStore(store);

* Great for persisting Redux state without losing data on app reload.


Comparison Table
Feature AsyncStorage SecureStore Redux Persist
Security Plain text (not secure) Encrypted storage Uses AsyncStorage (not secure)
Use Case Caching, non-sensitive data Sensitive data (passwords, tokens) Persisting Redux state
Works Offline? Yes Yes Yes
Performance Slower for large data Fast for small data Optimized for Redux
Storage Limit Device-dependent Small size Limited by AsyncStorage
Platform Support Android & iOS Android & iOS (Expo) Android & iOS
Generating an APK (Android) & IPA (iOS) in React Native

To distribute your React Native app, you need to generate an APK (Android) or IPA (iOS) file. Below is a step-by-step guide for both platforms.


Generating an APK (Android)

You can generate an APK using either React Native CLI or Expo.


1. Using React Native CLI (Bare Workflow)
Step 1: Prepare Your App for Release
  • Open android/app/build.gradle
  • Change:
    signingConfig signingConfigs.debug
    
    To:
    signingConfig signingConfigs.release
    
Step 2: Generate a Keystore (For Signing)

Run this command in the android folder:

keytool -genkeypair -v -keystore my-release-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000

? This will create a my-release-key.keystore file.

Move it to android/app/.

Step 3: Add Keystore Information

Edit android/gradle.properties and add:

MYAPP_RELEASE_STORE_FILE=my-release-key.keystore
MYAPP_RELEASE_KEY_ALIAS=my-key-alias
MYAPP_RELEASE_STORE_PASSWORD=your-password
MYAPP_RELEASE_KEY_PASSWORD=your-password
Step 4: Build the APK

Run:

cd android
./gradlew assembleRelease

* Your APK is generated at:
android/app/build/outputs/apk/release/app-release.apk


2. Using Expo (Managed Workflow)

Expo makes it easier to generate an APK.

Step 1: Install Expo CLI
npm install -g expo-cli
Step 2: Build the APK
eas build -p android --profile production

* Expo will generate the APK and provide a download link.


Generating an IPA (iOS)

iOS apps require a Mac and an Apple Developer Account.

1. Using React Native CLI
Step 1: Install Xcode & CocoaPods
sudo gem install cocoapods
cd ios
pod install
Step 2: Archive the App in Xcode
  1. Open ios/App.xcworkspace in Xcode.
  2. Select Generic iOS Device or a real iPhone.
  3. Go to Product > Archive.
  4. After archiving, click Distribute App to export the .ipa file.
2. Using Expo
Step 1: Install Expo CLI
npm install -g expo-cli
Step 2: Build the IPA
eas build -p ios --profile production

Expo will generate an .ipa file and provide a link.

Setting Up CI/CD for a React Native Project

Continuous Integration and Continuous Deployment (CI/CD) automate building, testing, and deploying React Native apps. Below is a step-by-step guide to setting up CI/CD using GitHub Actions, Fastlane, and cloud services like EAS, Bitrise, or App Center.


1. Choosing a CI/CD Tool

You can set up CI/CD using:

CI/CD Tool Platform Best For
GitHub Actions Free for GitHub projects Custom pipelines
Bitrise Android & iOS No need for macOS
EAS (Expo) Expo apps Easiest setup
App Center Android & iOS Microsoft ecosystem
Fastlane Android & iOS Advanced automation

2. CI/CD with GitHub Actions

GitHub Actions is a free way to automate builds and tests.

Step 1: Create a .github/workflows/ci.yml File

Inside your React Native project, create the following CI/CD workflow:

name: React Native CI/CD

on: 
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  build-android:
    name: Build Android APK
    runs-on: ubuntu-latest

    steps:
      - name: Checkout Repository
        uses: actions/checkout@v3

      - name: Set up Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 18

      - name: Install Dependencies
        run: npm install

      - name: Build Android APK
        run: cd android && ./gradlew assembleRelease

      - name: Upload APK
        uses: actions/upload-artifact@v3
        with:
          name: app-release.apk
          path: android/app/build/outputs/apk/release/app-release.apk

  build-ios:
    name: Build iOS IPA
    runs-on: macos-latest
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v3

      - name: Install Dependencies
        run: npm install

      - name: Install CocoaPods
        run: cd ios && pod install

      - name: Build iOS App
        run: xcodebuild -workspace ios/App.xcworkspace -scheme App -sdk iphoneos -configuration Release archive -archivePath ios/build/App.xcarchive

      - name: Upload IPA
        uses: actions/upload-artifact@v3
        with:
          name: app-release.ipa
          path: ios/build/App.xcarchive

* This workflow :

  • Runs on every push to main
  • Builds APK and IPA files
  • Uploads them as artifacts for download

3. Automating Deployments with Fastlane

Fastlane helps deploy your app to Google Play Store and App Store.

Step 1: Install Fastlane
npm install -g fastlane
Step 2: Setup for Android
cd android
fastlane init

Then, create android/fastlane/Fastfile with:

platform :android do
  desc "Build and upload APK to Play Store"
  lane :deploy do
    gradle(task: "assembleRelease")
    upload_to_play_store(track: "production")
  end
end

Run:

fastlane android deploy
Step 3: Setup for iOS
cd ios
fastlane init

Modify ios/fastlane/Fastfile:

platform :ios do
  desc "Build and upload IPA to App Store"
  lane :deploy do
    gym(scheme: "App")
    upload_to_app_store
  end
end

Run:

fastlane ios deploy

* Fastlane automates signing, building, and uploading your app!


4. CI/CD with Expo EAS (For Expo Projects)
Step 1: Install EAS CLI
npm install -g eas-cli
eas login
Step 2: Configure EAS
eas build:configure
Step 3: Build & Deploy
eas build -p android
eas submit -p android

* This uploads the app directly to the Play Store/App Store!


5. Using Bitrise (No Mac Needed)

Bitrise is a cloud-based CI/CD tool for React Native.

Steps :
  1. Create a Bitrise accounthttps://www.bitrise.io/
  2. Connect your GitHub repo
  3. Add a new workflow for Android/iOS
  4. Start a build (Bitrise handles everything!)

* Great for teams that don’t have macOS!


Summary of CI/CD Tools
Tool Setup Time Best For
GitHub Actions Medium Free, custom builds
Fastlane Medium Play Store/App Store uploads
EAS (Expo) Easy Expo apps
Bitrise Easy No Mac needed
App Center Medium Microsoft ecosystem
Network Security & SSL Pinning in React Native
1. What is Network Security?

Network security is the practice of protecting data during transmission between a mobile app and a server. It prevents data breaches, hacking, and man-in-the-middle (MITM) attacks.

Key Components of Network Security

* Encryption: Uses protocols like TLS (Transport Layer Security) to encrypt data.
* Authentication: Verifies user identity (e.g., JWT tokens, OAuth).
* SSL/TLS Certificates: Ensure the server is legitimate and secure.
* Firewalls & VPNs: Restrict unauthorized access.


2. What is SSL Pinning?

SSL Pinning is an advanced security technique used to prevent MITM attacks by hardcoding the server’s SSL certificate inside the mobile app.

Why Use SSL Pinning?

* Prevents attackers from intercepting HTTPS traffic
* Stops fake certificates from being accepted
* Ensures communication with the intended server

* Without SSL Pinning:

Hackers can intercept HTTPS traffic, fake a server’s certificate, and steal sensitive data (e.g., API keys, user credentials).

* With SSL Pinning:

Even if an attacker tries to use a fake certificate, the app will reject it!


3. How to Implement SSL Pinning in React Native

To implement SSL Pinning in a React Native app, use the react-native-ssl-pinning library.

* Installation
npm install react-native-ssl-pinning

or

yarn add react-native-ssl-pinning
* Example Usage (API Request with SSL Pinning)
import { fetch } from 'react-native-ssl-pinning';

fetch('https://api.example.com/data', {
  method: 'GET',
  headers: { Accept: 'application/json' },
  sslPinning: {
    certs: ['my-cert'], // Reference to pinned certificate
  },
})
  .then(response => response.json())
  .then(data => console.log('Secure data:', data))
  .catch(error => console.error('SSL Pinning failed:', error));
* Steps to Get SSL Certificate:
  1. Get the certificate using this command:
    openssl s_client -connect api.example.com:443 -showcerts
    
  2. Copy the PEM certificate content and save it as .cer file in your project.
  3. Add the certificate to your app's assets folder.

4. Drawbacks of SSL Pinning

* App Updates Required – If the server updates its SSL certificate, you must release a new app update.
* More Maintenance – Requires managing certificate expiration.
* Device-Specific Bypasses – Advanced attackers can still bypass SSL pinning using rooted devices.


Best Practices for Network Security

* Use TLS 1.2+ for secure data transmission.
* Implement SSL Pinning to prevent MITM attacks.
* Use OAuth, JWT, or API Keys for authentication.
* Never store sensitive data in AsyncStorage (use SecureStore for Expo).
* Use network monitoring tools like Charles Proxy for security testing.


Summary
Feature SSL Pinning General Network Security
Prevents MITM Attacks Yes Limited
Requires Manual Updates Yes No
Works with HTTPS? Yes Yes
Easy to Implement? Medium Yes