Go2X
Go2X

India's leading training and placement platform offering hands-on learning, powered by 200+ IITian and industry experts, connecting students to 1,000+ hiring and referral partners.

Let's Go2X

Stay updated with Go2X

Get course updates, interview tips, and career insights delivered to your inbox.

Contact Us

Address

1st Floor, Plot No 332, Phase IV, Udyog Vihar,
Sector 19, Gurugram, Haryana 122015

Email

support@go2x.live

Phone

+91 94107 10085

© 2025 Go2X Private Limited. All rights reserved.

Made with 🧡 and a lot of late nights.

About UsPrivacy PolicyTerms of ServiceRefund Policy

On This Page:

Mobile

React Native Interview Questions

Ace your React Native interview with this comprehensive list of 75+ questions covering basics, architecture, Fabric, Bridge, performance, Metro bundler, navigation, state management, security, and advanced topics for all experience levels.

July 08, 2026
45 mins read

I. Beginner Level

1. What is React Native?

React Native is an open-source framework created by Meta (formerly Facebook) that lets you build mobile apps using JavaScript and React. Instead of running in a browser like a web app, React Native compiles your code to actual native components - meaning you get a real iOS or Android app, not a web view in disguise.

The major advantage of React Native is that you write most of your code once and it works on both Android and iOS. It's been used to build major apps like Facebook, Instagram, Skype, and Shopify. In short, if you already know JavaScript and React, React Native is a very natural step into mobile development.

2. How is React Native different from ReactJS?

ReactJS is a library for building web applications that run in the browser. React Native is a framework for building mobile apps for iOS and Android. Both share the same core philosophy - component-based UI with JSX - but they diverge in how they render and what components they use.

  • ReactJS renders HTML elements like <div>, <p>, and <h1> in the browser DOM. React Native renders native mobile components like <View>, <Text>, and <Image>.

  • ReactJS uses CSS for styling. React Native uses a JavaScript-based StyleSheet API that looks like CSS but isn't exactly it.

  • ReactJS uses react-router for navigation. React Native relies on libraries like React Navigation.

  • ReactJS uses the browser's virtual DOM. React Native uses its own bridge (or the newer JSI) to communicate with the native platform.

3. What are the core components in React Native?

Core components are the fundamental building blocks provided by React Native out of the box. They map to actual native UI elements on Android and iOS. Think of them as the equivalent of HTML elements, but for mobile.

  • View: The basic container for layout (like a <div>). Used to wrap and position other components.

  • Text: The only component that can render text. You cannot put raw text outside a <Text> component.

  • Image: Used to display images from local assets or remote URLs.

  • TextInput: The input field for capturing user text. Replaces the HTML <input>.

  • ScrollView: A scrollable container. Renders all child components at once.

  • FlatList: A high-performance lazy-loading list for large datasets.

  • TouchableOpacity: Makes any component tappable with an opacity feedback effect.

  • StyleSheet: Not a visible component, but used to define and optimize styles.

4. What is JSX in React Native?

JSX stands for JavaScript XML. It's a syntax extension that lets you write UI-like code inside JavaScript files. It looks similar to HTML but is actually JavaScript under the hood - Babel transforms it into regular React.createElement() calls before the code runs.

In React Native, JSX doesn't produce HTML - it produces native components. So <View> becomes a UIView on iOS and a ViewGroup on Android.

Example:

jsx
1import { View, Text } from 'react-native';
2
3const Greeting = ({ name }) => (
4  <View>
5    <Text>Hello, {name}!</Text>
6  </View>
7);
8

5. What is state in React Native and how is it used?

State is a component's internal data store - it holds information that can change over time. Whenever state changes, React automatically re-renders the component to reflect the new data. In functional components, you manage state using the useState hook.

Example:

jsx
1import React, { useState } from 'react';
2import { View, Text, Button } from 'react-native';
3
4const Counter = () => {
5  const [count, setCount] = useState(0);
6
7  return (
8    <View>
9      <Text>Count: {count}</Text>
10      <Button title="Increment" onPress={() => setCount(count + 1)} />
11    </View>
12  );
13};
14

6. What is the difference between state and props in React Native?

This is one of those questions that sounds simple but reveals a lot about how well someone understands React's data flow.

  • State is internal and owned by the component itself. It can be changed within the component using setState or the useState hook. It represents dynamic data like a counter value, form input, or loading status.

  • Props are external and passed from a parent component to a child. They are read-only - the child cannot modify them directly. They are used to configure or customize a component, like passing a title or color.

A simple way to remember: props come from outside, state lives inside. If the data needs to change based on user interaction, it's state. If it's just configuration, it's props.

7. What are hooks in React Native?

Hooks are special functions introduced in React 16.8 that let functional components use features previously only available in class components - like state and lifecycle methods. In React Native, hooks work the same way as they do in React.

  • useState: Manages local state inside a functional component.

  • useEffect: Runs side effects like API calls, subscriptions, or timers after render.

  • useRef: Creates a mutable reference that persists across renders without triggering re-renders.

  • useContext: Accesses values from a React Context without prop drilling.

  • useMemo and useCallback: Optimization hooks that memoize values and functions respectively.

8. What does useEffect do in React Native?

useEffect is the hook you reach for when you need to do something after the component renders - like fetching data, setting up subscriptions, or interacting with timers. It takes two arguments: a function to run and a dependency array.

If the dependency array is empty ([]), the effect only runs once after the initial render (similar to componentDidMount). If you list specific values, the effect re-runs whenever those values change. Returning a cleanup function from useEffect handles cleanup on unmount.

Example:

jsx
1import React, { useState, useEffect } from 'react';
2import { View, Text } from 'react-native';
3
4const UserProfile = ({ userId }) => {
5  const [user, setUser] = useState(null);
6
7  useEffect(() => {
8    fetch(`https://api.example.com/users/${userId}`)
9      .then(res => res.json())
10      .then(data => setUser(data));
11
12    return () => {
13      // cleanup on unmount
14    };
15  }, [userId]); // re-runs when userId changes
16
17  return <Text>{user ? user.name : 'Loading...'}</Text>;
18};
19

9. What is Flexbox and how is it used in React Native?

Flexbox is the layout system used in React Native to position and align components. React Native defaults to flexDirection: 'column' (unlike the web which defaults to 'row'), so components stack vertically by default.

The core Flexbox properties in React Native are flexDirection (column or row), justifyContent (alignment along the main axis), alignItems (alignment along the cross axis), and flex (how much space a component takes up relative to siblings).

Example:

jsx
1import { View, Text, StyleSheet } from 'react-native';
2
3const Layout = () => (
4  <View style={styles.container}>
5    <View style={styles.box}><Text>Box 1</Text></View>
6    <View style={styles.box}><Text>Box 2</Text></View>
7  </View>
8);
9
10const styles = StyleSheet.create({
11  container: {
12    flex: 1,
13    flexDirection: 'row',
14    justifyContent: 'space-between',
15    alignItems: 'center',
16    padding: 16,
17  },
18  box: {
19    backgroundColor: '#FF8A3D',
20    padding: 20,
21    borderRadius: 8,
22  },
23});
24

10. What is the purpose of StyleSheet in React Native?

StyleSheet is React Native's equivalent of CSS. You use StyleSheet.create() to define your styles in a structured object. The main reason to use it over plain inline objects is performance - StyleSheet validates your styles and sends them across the bridge only once, rather than re-creating objects on every render.

Example:

jsx
1import { StyleSheet, Text, View } from 'react-native';
2
3const Card = () => (
4  <View style={styles.card}>
5    <Text style={styles.title}>Hello!</Text>
6  </View>
7);
8
9const styles = StyleSheet.create({
10  card: {
11    backgroundColor: '#fff',
12    borderRadius: 12,
13    padding: 16,
14    shadowColor: '#000',
15    shadowOpacity: 0.1,
16    elevation: 4,
17  },
18  title: {
19    fontSize: 18,
20    fontWeight: '600',
21    color: '#333',
22  },
23});
24

11. What is the difference between ScrollView and FlatList?

Both ScrollView and FlatList let users scroll through content, but they work very differently under the hood and are suited for different use cases.

  • ScrollView renders all its children at once when the component mounts. This is fine for small amounts of content (like a form or a few cards) but can cause memory and performance issues for large lists.

  • FlatList renders only the items currently visible on screen and lazily loads more as the user scrolls. This makes it much more efficient for large datasets like social media feeds or product catalogues.

Rule of thumb: use ScrollView for small, fixed content and FlatList for any list of dynamic data that could grow large.

12. What is AsyncStorage in React Native?

AsyncStorage is React Native's version of localStorage from the web. It's a simple, unencrypted, key-value storage system that persists data across app restarts. You use it for things like storing user preferences, authentication tokens, or app settings.

The core built-in module has been deprecated in favour of the community-maintained @react-native-async-storage/async-storage package. All operations are asynchronous, so you'll always use async/await or Promises with it. It's also important to note that it's not suitable for storing sensitive data - use a secure storage library like react-native-keychain for that.

Example:

jsx
1import AsyncStorage from '@react-native-async-storage/async-storage';
2
3// Storing a value
4await AsyncStorage.setItem('userToken', 'abc123');
5
6// Reading a value
7const token = await AsyncStorage.getItem('userToken');
8
9// Removing a value
10await AsyncStorage.removeItem('userToken');
11

13. What is TouchableOpacity used for?

TouchableOpacity is a component that makes any child element respond to touch by reducing its opacity when pressed, giving the user visual feedback. It's commonly used to create custom buttons or make images, text, or cards tappable.

Example:

jsx
1import { TouchableOpacity, Text, StyleSheet } from 'react-native';
2
3const CustomButton = ({ title, onPress }) => (
4  <TouchableOpacity style={styles.button} onPress={onPress} activeOpacity={0.7}>
5    <Text style={styles.text}>{title}</Text>
6  </TouchableOpacity>
7);
8
9const styles = StyleSheet.create({
10  button: { backgroundColor: '#FF8A3D', padding: 14, borderRadius: 8 },
11  text: { color: '#fff', fontWeight: 'bold', textAlign: 'center' },
12});
13

14. What is SafeAreaView in React Native?

SafeAreaView is a component that automatically adds padding to prevent your content from being hidden behind device-specific UI elements like the notch, status bar, or home indicator (especially on iPhones with Face ID). Without it, your content might render underneath the status bar or the notch.

On Android, it's less critical (though still useful), since Android handles these areas differently. The react-native-safe-area-context library is a more powerful community alternative that works well with React Navigation.

Example:

jsx
1import { SafeAreaView, Text } from 'react-native';
2
3const App = () => (
4  <SafeAreaView style={{ flex: 1 }}>
5    <Text>This content is safely within the visible area!</Text>
6  </SafeAreaView>
7);
8

15. What is the difference between functional and class components in React Native?

Both can do the same things, but the React ecosystem has clearly moved towards functional components with hooks. Here's how they differ:

  • Class components use ES6 class syntax, extend React.Component, and manage state with this.state and this.setState. They have lifecycle methods like componentDidMount and componentWillUnmount.

  • Functional components are plain JavaScript functions that accept props and return JSX. With hooks, they can handle state (useState), side effects (useEffect), and everything class components can do - but with far less boilerplate.

In modern React Native development, functional components are the standard. You'll rarely write new class components, but you should understand them since many older codebases still use them.

16. How do you handle forms in React Native?

React Native doesn't have a built-in form element like HTML's <form>. Instead, you build forms using TextInput components and manage their values using useState. For more complex scenarios - like multi-field forms with validation - libraries like Formik (paired with Yup for validation) are the go-to solution.

Example (basic form with useState):

jsx
1import React, { useState } from 'react';
2import { View, TextInput, Button, Text, StyleSheet } from 'react-native';
3
4const LoginForm = () => {
5  const [email, setEmail] = useState('');
6  const [password, setPassword] = useState('');
7
8  const handleSubmit = () => {
9    console.log('Email:', email, 'Password:', password);
10  };
11
12  return (
13    <View style={styles.container}>
14      <TextInput
15        style={styles.input}
16        placeholder="Email"
17        value={email}
18        onChangeText={setEmail}
19        keyboardType="email-address"
20      />
21      <TextInput
22        style={styles.input}
23        placeholder="Password"
24        value={password}
25        onChangeText={setPassword}
26        secureTextEntry
27      />
28      <Button title="Login" onPress={handleSubmit} />
29    </View>
30  );
31};
32
33const styles = StyleSheet.create({
34  container: { padding: 16 },
35  input: { borderWidth: 1, borderColor: '#ccc', borderRadius: 8, padding: 12, marginBottom: 12 },
36});
37

17. What is useRef used for in React Native?

useRef creates a mutable reference object that persists across renders. Unlike useState, changing a ref's value does not cause the component to re-render. In React Native, it has two main use cases.

  • Accessing component instances directly: For example, focusing a TextInput programmatically.

  • Storing mutable values: Like timer IDs or previous state values that you want to track without causing re-renders.

Example:

jsx
1import React, { useRef } from 'react';
2import { TextInput, Button, View } from 'react-native';
3
4const AutoFocusForm = () => {
5  const inputRef = useRef(null);
6
7  const focusInput = () => {
8    inputRef.current.focus();
9  };
10
11  return (
12    <View>
13      <TextInput ref={inputRef} placeholder="Type here" />
14      <Button title="Focus Input" onPress={focusInput} />
15    </View>
16  );
17};
18

18. What is useContext and why do we use it?

useContext is a hook that lets you access data from a React Context anywhere in your component tree without manually passing it as props through every intermediate component. It's the solution to the props drilling problem.

Common use cases include global themes, user authentication state, or language/locale settings that multiple components need access to. You create a context with React.createContext(), provide it at a high level with a Provider, and consume it anywhere below with useContext.

Example:

jsx
1import React, { createContext, useContext, useState } from 'react';
2import { View, Text, Button } from 'react-native';
3
4const ThemeContext = createContext('light');
5
6const ThemedButton = () => {
7  const theme = useContext(ThemeContext);
8  return (
9    <View style={{ backgroundColor: theme === 'dark' ? '#333' : '#fff', padding: 10 }}>
10      <Text style={{ color: theme === 'dark' ? '#fff' : '#000' }}>Current theme: {theme}</Text>
11    </View>
12  );
13};
14
15const App = () => {
16  const [theme, setTheme] = useState('light');
17  return (
18    <ThemeContext.Provider value={theme}>
19      <ThemedButton />
20      <Button title="Toggle Theme" onPress={() => setTheme(t => t === 'light' ? 'dark' : 'light')} />
21    </ThemeContext.Provider>
22  );
23};
24

19. How do you handle conditional rendering in React Native?

Conditional rendering in React Native works the same way as in React - you use plain JavaScript inside JSX to decide what to render. There are a few common patterns.

Example:

jsx
1const MyComponent = ({ isLoggedIn, isLoading, user }) => (
2  <View>
3    {/* Ternary operator */}
4    {isLoggedIn ? <Text>Welcome back, {user.name}!</Text> : <Text>Please log in.</Text>}
5
6    {/* Short-circuit operator - only renders when true */}
7    {isLoading && <Text>Loading...</Text>}
8
9    {/* If/else with a function */}
10    {(() => {
11      if (!user) return <Text>No user data</Text>;
12      if (user.role === 'admin') return <Text>Admin Dashboard</Text>;
13      return <Text>User Dashboard</Text>;
14    })()}
15  </View>
16);
17

20. What are keys in a list and why are they important?

Keys are unique identifiers that React uses to figure out which items in a list have changed, been added, or removed. Without keys, React would re-render the entire list on every update, which is inefficient and can cause bugs with stateful list items.

In FlatList, you provide keys through the keyExtractor prop. Keys should be unique and stable - using an index as a key works in a pinch but can cause bugs if the list order changes. Always prefer using a unique ID from your data.

Example:

jsx
1import { FlatList, Text } from 'react-native';
2
3const users = [{ id: '1', name: 'Alice' }, { id: '2', name: 'Bob' }];
4
5const UserList = () => (
6  <FlatList
7    data={users}
8    keyExtractor={item => item.id}  // unique key
9    renderItem={({ item }) => <Text>{item.name}</Text>}
10  />
11);
12

II. Intermediate Level

1. How do you navigate between screens in React Native?

React Native doesn't have built-in navigation. The most popular solution is the react-navigation library. You install it along with the required dependencies and then wrap your app in a NavigationContainer. Inside, you define navigators (Stack, Tab, Drawer) and screens.

Example (Stack Navigator):

jsx
1import { NavigationContainer } from '@react-navigation/native';
2import { createStackNavigator } from '@react-navigation/stack';
3
4const Stack = createStackNavigator();
5
6const App = () => (
7  <NavigationContainer>
8    <Stack.Navigator>
9      <Stack.Screen name="Home" component={HomeScreen} />
10      <Stack.Screen name="Details" component={DetailsScreen} />
11    </Stack.Navigator>
12  </NavigationContainer>
13);
14
15// Inside a screen component, navigate like this:
16const HomeScreen = ({ navigation }) => (
17  <Button title="Go to Details" onPress={() => navigation.navigate('Details')} />
18);
19

2. How do you pass data between screens in React Native?

With React Navigation, you pass data as the second argument to navigation.navigate() and receive it via the route.params object on the destination screen. This covers parent-to-child screen data passing. For sharing data back from a child screen to a parent, you can use navigation callbacks, context, or a global state manager like Redux or Zustand.

Example:

jsx
1// Passing data
2navigation.navigate('Details', { productId: 42, productName: 'React Native Book' });
3
4// Receiving data
5const DetailsScreen = ({ route }) => {
6  const { productId, productName } = route.params;
7  return <Text>{productName} (ID: {productId})</Text>;
8};
9

3. What are the different types of navigators in React Navigation?

React Navigation provides several navigator types, and most production apps use a combination of them. You can nest navigators inside each other for complex layouts.

  • Stack Navigator: Navigates between screens in a stack (push and pop). The new screen slides in from the right on iOS. Great for sequential flows like login → home → detail.

  • Tab Navigator (Bottom Tabs / Material Top Tabs): Shows tabs at the bottom or top of the screen. Perfect for main app sections like Home, Search, Profile.

  • Drawer Navigator: A side-menu drawer that slides in from the left (or right). Common in admin panels and content-heavy apps.

  • Native Stack Navigator: A newer variant of Stack Navigator that uses native navigation primitives for better performance.

4. How do you handle API calls in React Native?

React Native has the Fetch API built in, so you can make HTTP requests without installing anything. For more features like request interceptors, automatic JSON parsing, and better error handling, Axios is the most popular choice.

Example (with fetch and useEffect):

jsx
1import React, { useState, useEffect } from 'react';
2import { FlatList, Text, ActivityIndicator, View } from 'react-native';
3
4const PostsList = () => {
5  const [posts, setPosts] = useState([]);
6  const [loading, setLoading] = useState(true);
7  const [error, setError] = useState(null);
8
9  useEffect(() => {
10    fetch('https://jsonplaceholder.typicode.com/posts')
11      .then(res => res.json())
12      .then(data => setPosts(data))
13      .catch(err => setError(err.message))
14      .finally(() => setLoading(false));
15  }, []);
16
17  if (loading) return <ActivityIndicator size="large" />;
18  if (error) return <Text>Error: {error}</Text>;
19
20  return (
21    <FlatList
22      data={posts}
23      keyExtractor={item => item.id.toString()}
24      renderItem={({ item }) => <Text>{item.title}</Text>}
25    />
26  );
27};
28

5. What is Redux in React Native and what are its core components?

Redux is a predictable state management library. It puts all your application state into a single store, making it easy to share data across many components and trace state changes over time. It follows a strict unidirectional data flow.

  • Store: The single source of truth. It holds the entire application state as one JavaScript object.

  • Actions: Plain JavaScript objects that describe what happened (e.g., { type: 'INCREMENT_COUNTER' }). They are the only way to trigger state changes.

  • Reducers: Pure functions that take the current state and an action, and return the next state. They never mutate the existing state.

  • Selectors: Functions that extract specific data from the store. Used with hooks like useSelector.

Today, most new projects use Redux Toolkit (RTK) which significantly reduces the boilerplate that vanilla Redux required.

6. What is props drilling and how can we avoid it?

Props drilling happens when you pass data from a parent component down through several intermediate components just to get it to a deeply nested child. The intermediate components don't actually use the data - they just pass it along. This makes components harder to maintain and refactor.

There are several ways to avoid it:

  • Context API: Built into React, great for moderate global data like themes or user info.

  • Redux or Zustand: For complex, large-scale application state that many components need.

  • Component Composition: Sometimes restructuring components so they're less deeply nested is the cleanest solution.

7. How do you create animations in React Native?

React Native provides a built-in Animated API for creating animations. You create an Animated.Value, attach it to animatable components (like Animated.View or Animated.Text), and then use methods like Animated.timing(), Animated.spring(), or Animated.sequence() to drive the animation.

For more complex, high-performance animations, React Native Reanimated 2 is the community standard. It runs animations on the UI thread instead of the JS thread, making them buttery smooth even during heavy JS work.

Example (fade-in animation):

jsx
1import React, { useRef, useEffect } from 'react';
2import { Animated, View, Text } from 'react-native';
3
4const FadeInView = ({ children }) => {
5  const opacity = useRef(new Animated.Value(0)).current;
6
7  useEffect(() => {
8    Animated.timing(opacity, {
9      toValue: 1,
10      duration: 800,
11      useNativeDriver: true,  // always enable for performance
12    }).start();
13  }, []);
14
15  return (
16    <Animated.View style={{ opacity }}>
17      {children}
18    </Animated.View>
19  );
20};
21
22const App = () => (
23  <FadeInView>
24    <Text>I fade in smoothly!</Text>
25  </FadeInView>
26);
27

8. How do you debug a React Native application?

Debugging React Native involves a mix of tools depending on what you're investigating. Here are the main approaches:

  • Developer Menu: Shake the device (or press Cmd+D on iOS simulator / Cmd+M on Android emulator) to open it. Gives you options to enable Fast Refresh, open the debugger, and toggle the element inspector.

  • Chrome DevTools: Select 'Debug with Chrome' from the developer menu to use Chrome's console and debugger for JS code.

  • React Native Debugger: A standalone desktop app that combines React DevTools, Redux DevTools, and Chrome DevTools in one. Very popular for Redux-based apps.

  • Flipper: Meta's official debugging platform for React Native. Supports network inspection, layout inspection, logs, and custom plugins.

  • console.log(): Still the most commonly used quick debugging tool. Logs appear in the terminal or Chrome console.

9. What are the different ways to style a React Native application?

There are three main approaches to styling in React Native, each with its own tradeoffs.

  • Inline styles: Pass a style object directly to the component's style prop. Quick for prototyping but can clutter your JSX for complex styles.

  • StyleSheet.create(): The recommended approach. Defines styles outside the render function, improves performance, and enables IDE autocompletion.

  • styled-components: A popular third-party library that lets you write CSS-like syntax directly in JavaScript. Offers great theming support and a familiar syntax for web developers.

Example (styled-components):

jsx
1import styled from 'styled-components/native';
2
3const Container = styled.View`
4  flex: 1;
5  background-color: #0A0A0A;
6  padding: 16px;
7`;
8
9const Title = styled.Text`
10  font-size: 24px;
11  color: #FF8A3D;
12  font-weight: bold;
13`;
14
15const App = () => (
16  <Container>
17    <Title>Hello, styled-components!</Title>
18  </Container>
19);
20

10. How do timers work in React Native?

React Native implements the same browser timer APIs you'd use in web development. The key is to always clean them up inside useEffect's return function to avoid memory leaks.

  • setTimeout / clearTimeout: Runs a function once after a delay.

  • setInterval / clearInterval: Runs a function repeatedly at a fixed interval.

  • requestAnimationFrame: Used for smooth, frame-synchronized animations.

Example (timer with cleanup):

jsx
1import React, { useState, useEffect } from 'react';
2import { Text } from 'react-native';
3
4const Countdown = () => {
5  const [seconds, setSeconds] = useState(10);
6
7  useEffect(() => {
8    const timer = setInterval(() => {
9      setSeconds(prev => {
10        if (prev <= 1) clearInterval(timer);
11        return prev - 1;
12      });
13    }, 1000);
14
15    return () => clearInterval(timer); // cleanup on unmount
16  }, []);
17
18  return <Text>Time left: {seconds}s</Text>;
19};
20

11. What is a Modal in React Native and how do you use it?

A Modal is a component that appears on top of the current screen, blocking interaction with the content behind it. It's used for dialogs, alerts, confirmation prompts, or any overlay content. React Native has a built-in Modal component that takes props like visible, transparent, animationType, and onRequestClose.

Example:

jsx
1import React, { useState } from 'react';
2import { Modal, View, Text, Button, StyleSheet } from 'react-native';
3
4const MyModal = () => {
5  const [visible, setVisible] = useState(false);
6
7  return (
8    <View style={styles.container}>
9      <Button title="Open Modal" onPress={() => setVisible(true)} />
10      <Modal
11        visible={visible}
12        transparent
13        animationType="slide"
14        onRequestClose={() => setVisible(false)}
15      >
16        <View style={styles.overlay}>
17          <View style={styles.dialog}>
18            <Text>Are you sure?</Text>
19            <Button title="Close" onPress={() => setVisible(false)} />
20          </View>
21        </View>
22      </Modal>
23    </View>
24  );
25};
26
27const styles = StyleSheet.create({
28  container: { flex: 1, justifyContent: 'center' },
29  overlay: { flex: 1, backgroundColor: 'rgba(0,0,0,0.5)', justifyContent: 'center', alignItems: 'center' },
30  dialog: { backgroundColor: '#fff', padding: 24, borderRadius: 12, width: '80%' },
31});
32

12. What is the difference between TouchableOpacity and Pressable?

TouchableOpacity has been around since the early days of React Native. It provides a simple press interaction by reducing the wrapped element's opacity on press. It works well for most cases but offers limited customization.

Pressable is a newer, more powerful and flexible replacement. It gives you access to a broader set of events (onPressIn, onPressOut, onLongPress) and lets you apply style changes based on the pressed state directly through a style function. It's also more customizable in terms of the hit area.

Example (Pressable with dynamic style):

jsx
1import { Pressable, Text } from 'react-native';
2
3const PressableButton = ({ title, onPress }) => (
4  <Pressable
5    onPress={onPress}
6    style={({ pressed }) => ([
7      { backgroundColor: pressed ? '#e06a28' : '#FF8A3D' },
8      { padding: 14, borderRadius: 8 }
9    ])}
10  >
11    <Text style={{ color: '#fff', fontWeight: 'bold' }}>{title}</Text>
12  </Pressable>
13);
14

13. Explain FlatList in React Native and its key features.

FlatList is React Native's high-performance solution for rendering scrollable lists of data. Unlike ScrollView, it uses lazy rendering - only rendering items that are currently visible on the screen. As the user scrolls, off-screen items are unmounted and new ones are mounted, keeping memory usage low.

  • data: The array of items to render.

  • renderItem: A function that takes an item and returns the JSX to render for it.

  • keyExtractor: Returns a unique key string for each item.

  • onEndReached: Callback fired when the user scrolls to the end - great for pagination.

  • ListHeaderComponent / ListFooterComponent: Render a header or footer at the top or bottom of the list.

  • refreshing / onRefresh: Built-in pull-to-refresh support.

Example:

jsx
1import { FlatList, Text, View, StyleSheet } from 'react-native';
2
3const data = Array.from({ length: 50 }, (_, i) => ({ id: String(i), title: `Item ${i + 1}` }));
4
5const ItemList = () => (
6  <FlatList
7    data={data}
8    keyExtractor={item => item.id}
9    renderItem={({ item }) => (
10      <View style={styles.item}>
11        <Text>{item.title}</Text>
12      </View>
13    )}
14    ItemSeparatorComponent={() => <View style={styles.separator} />}
15  />
16);
17
18const styles = StyleSheet.create({
19  item: { padding: 16 },
20  separator: { height: 1, backgroundColor: '#eee' },
21});
22

14. How does networking work in React Native?

React Native has the Fetch API and the XMLHttpRequest (XHR) API built in, so you can make HTTP requests without any additional setup. The Fetch API is the modern, cleaner choice. For more advanced use cases, Axios is a popular library built on top of XHR that adds features like interceptors, automatic JSON transformation, and timeout handling.

Example (using Axios with interceptors):

jsx
1import axios from 'axios';
2
3const api = axios.create({
4  baseURL: 'https://api.example.com',
5  timeout: 10000,
6});
7
8// Add auth token to every request
9api.interceptors.request.use(config => {
10  config.headers.Authorization = `Bearer ${getToken()}`;
11  return config;
12});
13
14// Usage
15const fetchUser = async (userId) => {
16  try {
17    const { data } = await api.get(`/users/${userId}`);
18    return data;
19  } catch (error) {
20    console.error('Network error:', error.message);
21    throw error;
22  }
23};
24

15. What are the threads in React Native and what is the role of each?

In the classic React Native architecture, there are three threads running simultaneously, and understanding them is key to diagnosing performance issues.

  • Main/UI Thread: This is the native thread where the actual app UI is rendered. Any stuttering here means dropped frames (janky animations). Heavy work should never happen here.

  • JavaScript Thread: This is where your React and JavaScript code runs. State updates, API calls, and business logic happen here. If this gets blocked, the app feels unresponsive.

  • Shadow Thread: A background thread that computes layout using the Yoga layout engine. It translates Flexbox-based styles into native layout values before they reach the UI thread.

Communication between the JS thread and the main thread used to happen via the Bridge, which serializes data to JSON and passes it asynchronously. The New Architecture replaces this with JSI for direct, synchronous communication.

16. How do native apps differ from hybrid apps?

Native apps are built specifically for one platform (iOS or Android) using platform-specific languages (Swift/Objective-C for iOS, Kotlin/Java for Android). They have direct access to all device features and generally offer the best performance and user experience.

Hybrid apps are built with web technologies (HTML, CSS, JS) and wrapped in a native shell. Apps like Ionic run inside a WebView. React Native is often called a hybrid framework but is more accurately described as a cross-platform native framework - it renders actual native UI components, not web views, which gives it significantly better performance than traditional hybrid apps.

17. How do you create reusable components in React Native?

Reusable components are at the heart of React Native's component model. You create them as standalone files that accept props to customize their appearance and behavior. A good reusable component is self-contained, well-typed (with TypeScript), and doesn't carry logic that's too specific to one screen.

Example (reusable Button component):

jsx
1// components/AppButton.tsx
2import React from 'react';
3import { TouchableOpacity, Text, StyleSheet, ViewStyle } from 'react-native';
4
5interface AppButtonProps {
6  title: string;
7  onPress: () => void;
8  variant?: 'primary' | 'secondary';
9  style?: ViewStyle;
10}
11
12const AppButton: React.FC<AppButtonProps> = ({ title, onPress, variant = 'primary', style }) => (
13  <TouchableOpacity
14    style={[styles.button, variant === 'secondary' && styles.secondary, style]}
15    onPress={onPress}
16  >
17    <Text style={[styles.text, variant === 'secondary' && styles.secondaryText]}>{title}</Text>
18  </TouchableOpacity>
19);
20
21const styles = StyleSheet.create({
22  button: { backgroundColor: '#FF8A3D', padding: 14, borderRadius: 8, alignItems: 'center' },
23  secondary: { backgroundColor: 'transparent', borderWidth: 1, borderColor: '#FF8A3D' },
24  text: { color: '#fff', fontWeight: '600', fontSize: 16 },
25  secondaryText: { color: '#FF8A3D' },
26});
27
28export default AppButton;
29

18. What is a controlled component in React Native?

A controlled component is a form input whose value is driven by React state rather than by the DOM (or native input) itself. In React Native, a TextInput is controlled when you set its value prop to a state variable and update that state in onChangeText. This gives React complete control over the input's value, making it easier to validate, transform, or react to changes.

Example:

jsx
1import React, { useState } from 'react';
2import { TextInput, Text, View } from 'react-native';
3
4const ControlledInput = () => {
5  const [text, setText] = useState('');
6
7  return (
8    <View>
9      {/* Controlled: value is tied to state */}
10      <TextInput
11        value={text}
12        onChangeText={newText => setText(newText.toUpperCase())} // transforms input on the fly
13        placeholder="Type here..."
14      />
15      <Text>You typed: {text}</Text>
16    </View>
17  );
18};
19

19. What is a splash screen and how do you implement it?

A splash screen is the first screen shown when the app launches, typically displaying your app's logo or brand. It gives the app time to load initial data (auth state, cached data, etc.) before presenting the main UI. A poor launch experience can hurt first impressions significantly.

The most straightforward way is using the react-native-splash-screen library. You configure a native splash screen that shows immediately on launch (before JS loads), and then hide it from React Native once your app is ready. Expo provides its own expo-splash-screen module that handles this automatically.

Example (with expo-splash-screen):

jsx
1import * as SplashScreen from 'expo-splash-screen';
2import { useEffect, useState } from 'react';
3
4SplashScreen.preventAutoHideAsync(); // Keep splash visible
5
6const App = () => {
7  const [appReady, setAppReady] = useState(false);
8
9  useEffect(() => {
10    const prepare = async () => {
11      await loadFonts();
12      await checkAuthStatus();
13      setAppReady(true);
14      await SplashScreen.hideAsync(); // Hide once ready
15    };
16    prepare();
17  }, []);
18
19  if (!appReady) return null;
20  return <MainApp />;
21};
22

20. How do you handle screen orientation changes in React Native?

You can detect and respond to screen orientation changes using the Dimensions API from React Native, which provides width and height of the screen. By listening to the 'change' event, you can update your UI when the user rotates the device.

For locking orientation or getting more detailed control, the react-native-orientation-locker library is the standard choice. It lets you lock the app to portrait, landscape, or allow all orientations based on context.

Example (using Dimensions):

jsx
1import React, { useState, useEffect } from 'react';
2import { Dimensions, View, Text } from 'react-native';
3
4const useOrientation = () => {
5  const [orientation, setOrientation] = useState(
6    Dimensions.get('window').width > Dimensions.get('window').height ? 'LANDSCAPE' : 'PORTRAIT'
7  );
8
9  useEffect(() => {
10    const subscription = Dimensions.addEventListener('change', ({ window }) => {
11      setOrientation(window.width > window.height ? 'LANDSCAPE' : 'PORTRAIT');
12    });
13    return () => subscription?.remove();
14  }, []);
15
16  return orientation;
17};
18
19const Screen = () => {
20  const orientation = useOrientation();
21  return <Text>Current orientation: {orientation}</Text>;
22};
23

III. Advanced Level

1. What are some common performance optimization techniques in React Native?

Performance is a top concern in React Native since your JS code runs on a separate thread from the UI. Here are the most impactful techniques:

  • Use useNativeDriver: true for Animated API animations. This moves animation computations to the native thread, avoiding JS thread bottlenecks.

  • Memoize components with React.memo to prevent unnecessary re-renders when props haven't changed.

  • Use useMemo and useCallback to memoize expensive calculations and stable function references.

  • Use FlatList instead of ScrollView for large lists and implement getItemLayout for fixed-height items to avoid measurement overhead.

  • Avoid anonymous functions and object literals in JSX props - they create new references on every render and defeat memoization.

  • Enable Hermes engine - it improves startup time and reduces memory usage significantly.

  • Use lazy image loading libraries like react-native-fast-image for better image caching and performance.

2. What is the Hermes JavaScript engine in React Native?

Hermes is a JavaScript engine developed by Meta specifically optimized for React Native on mobile devices. Unlike V8 (used in Chrome) or JavaScriptCore (used in Safari and default iOS), Hermes is designed to improve startup time, reduce memory usage, and lower the app's binary size.

The key trick Hermes uses is ahead-of-time compilation - it compiles JavaScript to bytecode at build time rather than at runtime. This means the app doesn't have to parse and compile JS on every launch, which is a significant win for startup performance on low-end Android devices.

As of React Native 0.70+, Hermes is enabled by default on both Android and iOS. You can verify if your app is running on Hermes by checking HermesInternal in the JS environment.

3. What is the New Architecture in React Native (JSI, Fabric, TurboModules)?

The New Architecture is a major overhaul of React Native's internals that became stable in React Native 0.74. It addresses the fundamental limitations of the old Bridge-based communication model.

  • JSI (JavaScript Interface): Replaces the old Bridge. It allows JS code to hold direct references to C++ host objects, enabling synchronous, bidirectional communication between JS and native code without JSON serialization overhead.

  • Fabric: The new rendering system. It moves the rendering pipeline to C++ and enables synchronous layout and rendering, concurrent features, and better interaction with React 18's concurrent mode.

  • TurboModules: A replacement for the old Native Modules system. Modules are loaded lazily (only when needed) and use JSI for faster, type-safe communication. This reduces startup time significantly.

4. What is the React Native Bridge and why is it important?

The Bridge is the mechanism in the old React Native architecture that allows the JavaScript thread and the native thread to communicate. Since they run in separate threads and in different runtimes, they can't directly call each other - data has to be serialized to JSON, passed asynchronously over the bridge, and deserialized on the other side.

The Bridge works fine for most scenarios, but it has known limitations: the JSON serialization adds overhead, communication is always asynchronous (even when you don't want it to be), and large data payloads across the bridge can cause jank. These are the exact problems that the New Architecture's JSI was designed to solve.

5. What are Native Modules in React Native and when would you use them?

Native Modules let you write platform-specific code (in Swift/Objective-C for iOS or Kotlin/Java for Android) and expose it to JavaScript. You'd reach for them when you need to access a native platform API that React Native doesn't expose out of the box - things like Bluetooth, NFC, biometric authentication, background tasks, or proprietary SDKs.

With the New Architecture, TurboModules are the modern, more efficient way to write native modules. They're loaded lazily and communicate via JSI instead of the old asynchronous bridge.

Example (calling a Native Module from JS):

jsx
1import { NativeModules } from 'react-native';
2
3const { BiometricModule } = NativeModules;
4
5const authenticateUser = async () => {
6  try {
7    const result = await BiometricModule.authenticate('Verify your identity');
8    console.log('Auth result:', result);
9  } catch (e) {
10    console.error('Biometric auth failed:', e);
11  }
12};
13

6. How does React.memo, useMemo, and useCallback help in React Native?

These are React's built-in memoization tools that help you prevent unnecessary re-renders and expensive recalculations.

  • React.memo: A higher-order component that wraps a functional component. It prevents the component from re-rendering if its props haven't changed by doing a shallow comparison.

  • useMemo: Memoizes the result of an expensive calculation. The calculation only re-runs when its dependencies change.

  • useCallback: Returns a memoized version of a callback function. Useful when passing callbacks as props to memoized child components - without it, a new function reference is created on every render, defeating React.memo.

Example:

jsx
1import React, { useState, useMemo, useCallback } from 'react';
2import { View, Text, Button } from 'react-native';
3
4const ExpensiveList = React.memo(({ items, onPress }) => {
5  console.log('ExpensiveList rendered'); // will only log when items or onPress changes
6  return items.map(item => (
7    <Text key={item.id} onPress={() => onPress(item.id)}>{item.name}</Text>
8  ));
9});
10
11const ParentComponent = () => {
12  const [count, setCount] = useState(0);
13  const [items] = useState([{ id: 1, name: 'Apple' }, { id: 2, name: 'Banana' }]);
14
15  // Without useCallback, a new fn reference is created on every render
16  const handlePress = useCallback((id) => {
17    console.log('Pressed item:', id);
18  }, []); // stable reference - no deps
19
20  // Without useMemo, this runs on every render
21  const totalItems = useMemo(() => items.length, [items]);
22
23  return (
24    <View>
25      <Text>Count: {count} | Total items: {totalItems}</Text>
26      <Button title="Increment" onPress={() => setCount(c => c + 1)} />
27      <ExpensiveList items={items} onPress={handlePress} />
28    </View>
29  );
30};
31

7. How do you test React Native applications?

Testing in React Native covers multiple levels, and a good test strategy uses a mix of them.

  • Unit tests with Jest: Jest comes pre-configured with React Native. It's great for testing individual functions, hooks, and utility logic in isolation.

  • Component tests with React Native Testing Library (RNTL): The recommended way to test components. RNTL encourages testing from a user's perspective by querying elements by accessible role or text, not internal implementation.

  • End-to-end tests with Detox: Detox is the most popular E2E testing framework for React Native. It runs tests on a real device or emulator, simulating actual user interactions like taps, swipes, and text input.

Example (component test with RNTL):

jsx
1import React from 'react';
2import { render, fireEvent } from '@testing-library/react-native';
3import Counter from './Counter';
4
5test('increments counter on button press', () => {
6  const { getByText } = render(<Counter />);
7
8  // Initial state
9  expect(getByText('Count: 0')).toBeTruthy();
10
11  // Simulate press
12  fireEvent.press(getByText('Increment'));
13
14  // Assert new state
15  expect(getByText('Count: 1')).toBeTruthy();
16});
17

8. What is CodePush and how is it used in React Native?

CodePush (now part of App Center by Microsoft) is a cloud service that lets you push JavaScript and asset updates to your React Native app directly - bypassing the App Store and Play Store review process. This is possible because React Native's JS bundle is loaded dynamically, so you can swap it out without rebuilding the native binary.

It's incredibly useful for shipping hotfixes and minor feature updates quickly. The update can be applied silently in the background or you can prompt the user to restart. One important caveat: you can only push JS changes - any native code changes still require a full app store release.

9. What is deep linking in React Native and how do you implement it?

Deep linking allows external sources (like a browser, email, or another app) to open a specific screen in your app directly, rather than just launching the app's home screen. For example, clicking a product link in an email might open your app directly on that product's detail screen.

There are two types: URL scheme deep links (myapp://product/42) which work when the app is already installed, and Universal Links (iOS) / App Links (Android) which use real HTTPS URLs and work even if the app isn't installed (they fall back to the website). React Navigation has built-in deep linking support through its linking configuration.

Example (React Navigation linking config):

jsx
1const linking = {
2  prefixes: ['myapp://', 'https://myapp.com'],
3  config: {
4    screens: {
5      Home: 'home',
6      ProductDetail: 'product/:productId',
7      Profile: {
8        path: 'user/:userId',
9        screens: {
10          Posts: 'posts',
11        },
12      },
13    },
14  },
15};
16
17const App = () => (
18  <NavigationContainer linking={linking}>
19    {/* ... */}
20  </NavigationContainer>
21);
22

10. How do you implement push notifications in React Native?

Push notifications in React Native require setup on three levels: the device (registering for push), the server (sending the notification), and the app (receiving and handling it). The two most popular solutions are Firebase Cloud Messaging (FCM) via @react-native-firebase/messaging and Expo Notifications for Expo-managed projects.

The general flow is: the app requests permission from the user, gets a device token, sends that token to your backend server, and the server uses it to send notifications via FCM or APNs. Your app then handles the notification via foreground/background listeners.

Example (with @react-native-firebase/messaging):

jsx
1import messaging from '@react-native-firebase/messaging';
2import { useEffect } from 'react';
3
4const usePushNotifications = () => {
5  useEffect(() => {
6    // Request permission
7    messaging().requestPermission();
8
9    // Get device token
10    messaging().getToken().then(token => {
11      sendTokenToServer(token);
12    });
13
14    // Listen for foreground messages
15    const unsubscribe = messaging().onMessage(async remoteMessage => {
16      console.log('Foreground notification:', remoteMessage);
17    });
18
19    return unsubscribe;
20  }, []);
21};
22

11. When should you use Context API vs Redux in React Native?

This is a common decision point, and the answer depends on the scale and complexity of your app's state.

  • Use Context API when: You have relatively simple global state (like theme, language, or user authentication), you don't need complex state logic or middleware, and the data updates infrequently. Context is built-in, requires no library, and works great for these cases.

  • Use Redux (or Redux Toolkit) when: Your app has complex, deeply nested state, many components need to read and write the same data, you need powerful debugging tools (Redux DevTools), or you need middleware for async operations (Redux Thunk / Redux Saga).

  • Consider Zustand: A lightweight, modern alternative to Redux with a much simpler API that handles most use cases without the boilerplate. It's increasingly popular in newer React Native projects.

12. What is React Native Reanimated and how is it different from the Animated API?

The built-in Animated API runs animations on the JavaScript thread. Even with useNativeDriver: true, complex gesture-driven animations can still stutter because the JS thread must calculate values and communicate them to the native thread frame by frame.

React Native Reanimated 2 solves this by running all animation worklets directly on the UI thread using a concept called Worklets - small JavaScript functions that are executed natively. Combined with react-native-gesture-handler for gesture handling, it enables extremely smooth 60fps animations even under heavy load. It's the gold standard for complex animations in React Native today.

Example (Reanimated 2 shared value):

jsx
1import Animated, { useSharedValue, useAnimatedStyle, withSpring } from 'react-native-reanimated';
2import { Button, View } from 'react-native';
3
4const BouncingBox = () => {
5  const offset = useSharedValue(0);
6
7  const animatedStyle = useAnimatedStyle(() => ({
8    transform: [{ translateY: offset.value }],
9  }));
10
11  return (
12    <View>
13      <Animated.View style={[{ width: 100, height: 100, backgroundColor: '#FF8A3D' }, animatedStyle]} />
14      <Button title="Bounce" onPress={() => {
15        offset.value = withSpring(-100, {}, () => {
16          offset.value = withSpring(0);
17        });
18      }} />
19    </View>
20  );
21};
22

13. What are Error Boundaries and how do you use them in React Native?

Error Boundaries are class components (a rare place where you still need class components) that catch JavaScript errors anywhere in their child component tree, log them, and render a fallback UI instead of crashing the entire app. They prevent a bug in one part of the UI from taking down the whole app.

Note that Error Boundaries only catch errors during rendering, lifecycle methods, and constructors. They don't catch errors in event handlers or async code (those need try/catch). For production apps, react-native-error-boundary is a popular library that adds functional component support and crash reporting hooks.

Example:

jsx
1import React from 'react';
2import { View, Text } from 'react-native';
3
4class ErrorBoundary extends React.Component {
5  state = { hasError: false, error: null };
6
7  static getDerivedStateFromError(error) {
8    return { hasError: true, error };
9  }
10
11  componentDidCatch(error, info) {
12    // Log to crash reporting service (e.g., Sentry)
13    console.error('Caught error:', error, info);
14  }
15
16  render() {
17    if (this.state.hasError) {
18      return (
19        <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
20          <Text>Something went wrong. Please restart the app.</Text>
21        </View>
22      );
23    }
24    return this.props.children;
25  }
26}
27
28// Usage
29const App = () => (
30  <ErrorBoundary>
31    <MainApp />
32  </ErrorBoundary>
33);
34

14. How do you implement offline support in a React Native app?

Offline support means your app remains usable (at least partially) when there's no network connection. The approach depends on how data-heavy your app is.

  • Detect connectivity: Use @react-native-community/netinfo to detect when the device goes online or offline and adjust your UI accordingly.

  • Cache data locally: Store API responses in AsyncStorage or SQLite (via react-native-sqlite-storage) so the app can display cached data when offline.

  • Queue offline actions: Use a queue (stored in AsyncStorage) to collect user actions made while offline, and sync them to the server once connectivity is restored.

  • Use React Query or SWR: These data-fetching libraries have built-in caching and background refetching that work great for stale-while-revalidate patterns.

  • WatermelonDB or Realm: For complex offline-first apps, these embedded databases handle synchronization with remote servers automatically.

15. How do you set up CI/CD for a React Native application?

CI/CD for React Native automates your build, test, and deployment pipeline. The most popular tools in the React Native ecosystem are Fastlane, Bitrise, GitHub Actions, and Expo EAS Build.

  • Fastlane: A Ruby-based tool that automates common tasks like building, testing, signing, and uploading to the App Store or Play Store. Works for both iOS and Android.

  • GitHub Actions: You can set up workflows that run on every PR or push - running Jest tests, building the app, and distributing test builds via Firebase App Distribution or TestFlight.

  • EAS Build (Expo): For Expo projects, EAS Build is by far the easiest way to build, sign, and submit apps to the stores. It handles certificates and provisioning profiles automatically.

16. What are best practices for securing sensitive data in React Native?

Security in mobile apps often gets overlooked, but it's critical. Here are the most important practices:

  • Never store sensitive data in AsyncStorage: It's unencrypted. Use react-native-keychain or expo-secure-store instead, which use the device's native secure storage (Keychain on iOS, Keystore on Android).

  • Don't hardcode secrets: API keys, secrets, and tokens should never be in your JS bundle. Use environment variables and keep them server-side where possible.

  • Use HTTPS everywhere: Always use HTTPS for API calls and consider certificate pinning with libraries like react-native-ssl-pinning to prevent man-in-the-middle attacks.

  • Obfuscate your code: Use ProGuard on Android to minify and obfuscate your bytecode, making reverse engineering harder.

  • Implement biometric authentication: For sensitive screens (payments, settings), use biometric auth via react-native-biometrics.

17. What is the difference between Expo and React Native CLI?

This is a practical question that comes up in almost every React Native project decision.

  • Expo is a managed framework built on top of React Native. It provides a curated set of native APIs, removes the need to touch native code for most use cases, and offers tools like Expo Go (test on device without building), EAS Build (cloud building), and over-the-air updates. It's the fastest way to get started and perfect for many apps.

  • React Native CLI gives you full access to the native iOS and Android projects. You have complete control over native code, can use any native library, and aren't limited to Expo's SDK. But it requires Xcode and Android Studio setup and you're responsible for managing native dependencies.

The lines have blurred significantly - Expo now supports custom native modules via EAS Build and you can eject to bare workflow. For most new projects, starting with Expo and ejecting only if needed is a solid approach.

18. What is SectionList and how is it different from FlatList?

SectionList is similar to FlatList but designed for data that is divided into sections with headers - like a contacts list grouped alphabetically, or a settings screen grouped by category.

While FlatList takes a flat array of data, SectionList takes an array of section objects, each with a title and a data array. It has a renderSectionHeader prop in addition to renderItem.

Example:

jsx
1import { SectionList, Text, View, StyleSheet } from 'react-native';
2
3const sections = [
4  { title: 'Fruits', data: ['Apple', 'Banana', 'Mango'] },
5  { title: 'Vegetables', data: ['Carrot', 'Potato', 'Onion'] },
6];
7
8const GroupedList = () => (
9  <SectionList
10    sections={sections}
11    keyExtractor={(item, index) => item + index}
12    renderItem={({ item }) => <Text style={styles.item}>{item}</Text>}
13    renderSectionHeader={({ section }) => (
14      <Text style={styles.header}>{section.title}</Text>
15    )}
16  />
17);
18
19const styles = StyleSheet.create({
20  header: { fontSize: 18, fontWeight: 'bold', backgroundColor: '#f4f4f4', padding: 8 },
21  item: { padding: 12 },
22});
23

19. How do you handle accessibility in React Native apps?

Accessibility (a11y) ensures your app can be used by people with disabilities. React Native has built-in support for screen readers (VoiceOver on iOS, TalkBack on Android) through accessibility props.

  • accessible: Set to true to mark a component as an accessibility element.

  • accessibilityLabel: A string read by the screen reader to describe the element.

  • accessibilityRole: Tells the screen reader what type of element it is (button, image, header, etc.).

  • accessibilityHint: Provides additional context about what happens when the user interacts with the element.

  • accessibilityState: Describes the state of the element (disabled, selected, checked, etc.).

Example:

jsx
1<TouchableOpacity
2  accessible
3  accessibilityRole="button"
4  accessibilityLabel="Submit form"
5  accessibilityHint="Double tap to submit your registration form"
6  accessibilityState={{ disabled: isLoading }}
7  onPress={handleSubmit}
8  disabled={isLoading}
9>
10  <Text>Submit</Text>
11</TouchableOpacity>
12

20. What are Zustand and MobX and how do they compare to Redux for state management?

There are several excellent state management options beyond Redux in the React Native ecosystem today.

  • Redux (with RTK): The most established option. Excellent DevTools, middleware support (Thunk, Saga), and a large community. Can still feel verbose even with Redux Toolkit.

  • Zustand: A minimal, hook-based state management library. Has almost no boilerplate - you define a store as a simple object with state and actions. Great for small to medium apps and increasingly popular in 2025/26.

  • MobX: Uses observable state and reactive computed values. State changes automatically propagate to any component observing them. It's very intuitive coming from an OOP background, but the reactive model can be harder to debug than Redux's explicit action model.

Example (Zustand store):

jsx
1import { create } from 'zustand';
2
3const useCartStore = create((set) => ({
4  items: [],
5  addItem: (item) => set((state) => ({ items: [...state.items, item] })),
6  removeItem: (id) => set((state) => ({ items: state.items.filter(i => i.id !== id) })),
7  clearCart: () => set({ items: [] }),
8}));
9
10// Usage in a component
11const Cart = () => {
12  const { items, addItem, clearCart } = useCartStore();
13  return <Text>Cart has {items.length} items</Text>;
14};
15

21. How do you implement lazy loading and code splitting in React Native?

Code splitting in React Native works differently from the web since there's no webpack-style chunk splitting for mobile bundles. However, you can achieve lazy loading of components using React.lazy() combined with Suspense, which allows components to be loaded on demand rather than bundled upfront.

For navigation, React Navigation supports lazy loading of screens by default in Tab navigators (screens are only rendered when first visited). For images, use libraries like react-native-fast-image which cache and load images progressively. For large data, FlatList with pagination (onEndReached) is your primary lazy loading tool.

Example (React.lazy with Suspense):

jsx
1import React, { Suspense, lazy } from 'react';
2import { ActivityIndicator } from 'react-native';
3
4// Lazily load a heavy component
5const HeavyChart = lazy(() => import('./HeavyChart'));
6
7const Dashboard = () => (
8  <Suspense fallback={<ActivityIndicator size="large" />}>
9    <HeavyChart />
10  </Suspense>
11);
12

22. What causes memory leaks in React Native and how do you prevent them?

Memory leaks in React Native happen when resources (event listeners, subscriptions, timers, or async operations) are not properly cleaned up when a component unmounts. Over time, these accumulate and can cause the app to slow down or crash.

  • Timers not cleared: Always clearTimeout / clearInterval in useEffect's cleanup function.

  • Event listeners not removed: Remove all event listeners (like Dimensions.addEventListener, AppState.addEventListener) on cleanup.

  • Setting state on unmounted components: An async operation (like a fetch) completing after a component unmounts will try to set state on a dead component. Use an isMounted flag or AbortController to cancel pending operations.

  • Unsubscribed observables / subscriptions: Always unsubscribe from Redux store subscriptions, context listeners, or third-party observables.

Example (preventing stale setState with AbortController):

jsx
1import { useState, useEffect } from 'react';
2
3const useUserData = (userId) => {
4  const [user, setUser] = useState(null);
5
6  useEffect(() => {
7    const controller = new AbortController();
8
9    fetch(`/api/users/${userId}`, { signal: controller.signal })
10      .then(res => res.json())
11      .then(data => setUser(data))
12      .catch(err => {
13        if (err.name !== 'AbortError') console.error(err);
14      });
15
16    return () => controller.abort(); // cancel fetch on unmount
17  }, [userId]);
18
19  return user;
20};
21

23. What are the advantages of using React Native?

React Native has become one of the most popular choices for cross-platform mobile development, and for good reason. It solves many pain points that teams face when building for both iOS and Android simultaneously.

  • Cross-platform code sharing: A single JavaScript codebase runs on both iOS and Android. Depending on the app, 80–95% of code can be shared between platforms, dramatically reducing development time and cost compared to maintaining two separate native apps.

  • True native UI: Unlike hybrid frameworks like Ionic that render inside a WebView, React Native maps its components to actual native UI elements. This means users get the look, feel, and performance of a native app, not a web app wrapped in a shell.

  • Fast Refresh (formerly Hot Reloading): Changes to your code appear instantly in the running app without losing the current application state. This makes the development feedback loop extremely tight.

  • Leverage existing web skills: If your team already knows JavaScript and React, picking up React Native is relatively smooth. You don't need separate iOS and Android teams - one team can own both platforms.

  • Large ecosystem and community: React Native is backed by Meta and has an enormous open-source ecosystem. Libraries, tools, and community support are widely available. Popular apps like Facebook, Instagram, Shopify, and Discord have used it in production.

  • Over-the-air (OTA) updates: Since the JavaScript bundle is loaded at runtime, you can push JS-only updates via services like CodePush without going through the App Store or Play Store review process. Great for fast hotfixes.

  • Access to native APIs: When you need something React Native doesn't provide out of the box, you can write Native Modules in Swift/Kotlin and expose them to JavaScript. You're never truly blocked.

24. What are the main disadvantages of using React Native?

No framework is perfect, and React Native comes with its own set of genuine trade-offs that you should understand before committing to it for a project.

  • Performance ceiling: React Native is fast for most apps but it will never quite match a fully native app for extremely compute-intensive tasks like complex 3D graphics, heavy real-time processing, or computationally expensive algorithms. The JS thread adds overhead that pure native code doesn't have.

  • The Bridge bottleneck (old architecture): In the classic architecture, all communication between JavaScript and native happens asynchronously over a serialized bridge. Passing large objects or high-frequency data across it (like gesture coordinates 60 times per second) can cause jank.

  • Platform-specific quirks: Even with shared code, you often end up writing platform-specific adjustments. UI components render slightly differently on iOS vs Android, and some APIs behave differently across platforms. "Write once, run everywhere" is more like "write once, debug everywhere."

  • Third-party library inconsistency: The quality and maintenance level of community libraries varies widely. Some popular libraries have poor iOS or Android support, slow updates, or are abandoned altogether. You often need to evaluate and sometimes fork libraries.

  • Native code requirement for complex features: Anything involving Bluetooth, NFC, advanced camera controls, background audio, or deep OS integrations still requires writing native code in Swift/Kotlin. You can't escape native code entirely for production-grade apps.

  • Larger app binary size: React Native apps include the JS engine and the framework itself in the binary, making the initial download size larger than a comparable native app.

  • Breaking changes in major upgrades: React Native has historically had painful upgrade paths between major versions. The ecosystem moves fast, and keeping dependencies in sync during upgrades can take significant engineering time.

25. What are the main performance issues in React Native and what causes them?

Performance problems in React Native almost always fall into one of a few categories. Understanding the root cause is the first step to fixing them.

  • JS thread blocking: The JavaScript thread handles all your application logic - state updates, business rules, API calls, and React's reconciliation. If you run a heavy computation synchronously on the JS thread (like parsing large JSON or running a complex algorithm), it blocks everything and makes the app feel frozen. The fix is to offload heavy work to background threads using InteractionManager, web workers (via react-native-threads), or native modules.

  • Bridge overhead (old architecture): Every interaction between JS and native involves serializing data to JSON and deserializing it on the other side. For high-frequency operations like scroll events or gesture tracking, this back-and-forth can cause dropped frames. This is precisely why animations should always use useNativeDriver: true - it moves the animation math to the native side, completely bypassing the bridge.

  • Unnecessary re-renders: React re-renders a component any time its state or props change. If parent components re-render frequently and pass unstable references (new object literals or anonymous functions) as props to children, the entire subtree re-renders even when nothing meaningful changed. This kills list performance especially.

  • Rendering too many items at once (ScrollView misuse): Using ScrollView for long lists renders everything upfront, consuming memory and making the initial render slow. FlatList with windowing is the solution for long datasets.

  • Image performance: Loading large, unoptimized images is one of the most common causes of both slow screens and excessive memory usage. Using the right image size, caching (react-native-fast-image), and progressive loading significantly helps.

  • Memory leaks: Timers, event listeners, or async operations that aren't cleaned up on component unmount keep consuming memory over time. In a long-running app session, this can eventually cause crashes.

  • Startup time: A large JS bundle takes longer to parse and execute on app launch. Using Hermes (which pre-compiles JS to bytecode), lazy loading screens, and splitting your bundle helps reduce time-to-interactive.

26. What is the role of the Bridge in React Native?

The Bridge is the original communication layer in React Native that allows the JavaScript world and the native world to talk to each other. Since these two environments run in completely separate threads with different runtimes, they cannot call each other directly - the Bridge acts as a translator and messenger between them.

Here is how it works step by step:

  • When JavaScript code wants to call a native API (like showing a camera or vibrating the phone), it sends a message to the Bridge.

  • The message is serialized into a JSON string (because you can't pass JavaScript objects directly to native code).

  • The Bridge passes this JSON payload asynchronously to the native side.

  • The native side deserializes the JSON, executes the native API call, serializes the result back to JSON, and sends it back across the Bridge to JavaScript.

The critical limitation of the Bridge is that it is always asynchronous and involves JSON serialization overhead on every crossing. For most operations this is invisible, but for high-frequency interactions (like tracking a finger position 60 times per second for a gesture), the serialization cost adds up and causes dropped frames. This limitation was the primary motivation for the New Architecture, which replaces the Bridge with JSI - a C++ layer that allows JavaScript to hold direct references to native objects and call them synchronously without any serialization.

27. How is React Native code processed to display the final output on the screen?

Understanding the full pipeline from your JSX file to what appears on the user's screen is important for diagnosing both build-time and runtime issues. Here is the complete journey:

  • Step 1 - Metro bundler: When you run your app, Metro (React Native's JavaScript bundler) reads your entry file (index.js), resolves all imports, transforms JSX and modern JavaScript using Babel, and packages everything into a single JavaScript bundle file.

  • Step 2 - JS Engine execution: The native app launches and the JavaScript engine (Hermes or JavaScriptCore) loads and executes the bundle. Your React components start running and produce a virtual DOM - a lightweight JavaScript representation of the UI tree.

  • Step 3 - React reconciliation: React's reconciliation algorithm (the Fiber engine) compares the new virtual DOM with the previous one and calculates the minimal set of changes needed (this is called diffing).

  • Step 4 - Shadow tree and layout (Yoga): The changes are passed to the Shadow Thread, which uses the Yoga layout engine (a cross-platform implementation of Flexbox written in C++) to calculate the exact pixel positions and sizes of every element.

  • Step 5 - Bridge/JSI communication: The calculated layout instructions are passed to the Main/UI thread. In the old architecture this crosses the Bridge as JSON. In the New Architecture (Fabric), this happens synchronously in C++ without serialization.

  • Step 6 - Native rendering: The Main thread receives the layout instructions and creates or updates actual native views - UIView on iOS, View on Android - using the platform's native rendering APIs. What the user finally sees is a real native UI, not a web view.

Simplified pipeline diagram:

text
1Your JSX Code
2      │
3      ▼
4  Metro Bundler  ──→  JS Bundle (.jsbundle)
5      │
6      ▼
7  JS Engine (Hermes/JSC)
8  - Runs your React code
9  - Builds Virtual DOM
10  - React Fiber reconciliation
11      │
12      ▼
13  Shadow Thread (Yoga)
14  - Calculates Flexbox layout
15  - Computes exact px positions
16      │
17   Bridge / JSI
18      │
19      ▼
20  Main/UI Thread
21  - Creates native views
22  - UIView (iOS) / View (Android)
23      │
24      ▼
25  📱 Rendered on Screen
26

28. What is the role of Fabric in React Native?

Fabric is the new rendering system introduced as part of React Native's New Architecture. It is a complete rewrite of the old UIManager-based renderer and addresses the fundamental limitations of how React Native previously managed the UI.

What was wrong with the old renderer? In the old architecture, every UI update had to:

  • Be serialized to JSON on the JS thread.

  • Cross the Bridge asynchronously to the native side.

  • Be deserialized and applied to the native view tree.

How Fabric fixes this:

  • C++ rendering core: Fabric moves the core rendering logic into C++, which runs on multiple threads and is accessible from both JavaScript (via JSI) and native code without serialization.

  • Synchronous layout reads: With Fabric, JavaScript can synchronously read layout measurements (like the size and position of a view) without waiting for an async round trip. This was previously impossible and forced developers into awkward callback-based patterns.

  • Concurrent rendering support: Fabric is built to work with React 18's concurrent features like Suspense and transitions, allowing React to interrupt and resume rendering work - something the old architecture could not support.

  • Improved thread safety: The new renderer is designed to be called from multiple threads safely, reducing entire classes of race condition bugs that could occur in the old architecture.

  • Better host component interop: Native components can be accessed and manipulated more efficiently, enabling smoother gesture-driven UI and faster animations.

In short, Fabric is to the UI layer what JSI is to the communication layer - both work together in the New Architecture to bring React Native's rendering much closer to the speed and flexibility of fully native apps.

29. What steps would you take in React Native if you have an app that crashes continually?

Continuous crashes need to be investigated systematically. Randomly changing code rarely fixes the underlying issue - you need to identify whether it is a JS crash, a native crash, or a specific environment/device issue.

  • Step 1 - Read the error: Open the Metro terminal and the device logs (via adb logcat on Android or Xcode console on iOS). The crash message, stack trace, and file names will point you to the source. Never skip reading the full error output.

  • Step 2 - Determine if it is a JS or native crash: JS crashes usually show a red screen with a JavaScript stack trace. Native crashes (null pointer, memory violation) will show up in platform-specific crash logs. On Android, use adb logcat -s ReactNativeJS to filter JS-related logs.

  • Step 3 - Reproduce consistently: Identify exactly which steps cause the crash. Check if it is device-specific (only on Android, only on older devices), OS-version-specific, or environment-specific (only in production build).

  • Step 4 - Check recent changes: If the crash started after a specific commit or library upgrade, use git bisect or simply check your git log to narrow down what changed. Library version upgrades are a very common source of sudden crashes.

  • Step 5 - Add Error Boundaries: Wrap your component tree in an Error Boundary to catch JS render errors gracefully, show a fallback UI, and log the error details without crashing the whole app.

  • Step 6 - Use a crash reporting tool: Integrate Sentry, Firebase Crashlytics, or Bugsnag. These tools give you symbolicated stack traces, device info, OS version, and reproduction steps for every crash that happens in production. This is non-negotiable for production apps.

  • Step 7 - Clean build: Sometimes crashes are caused by stale build caches. Run npx react-native start --reset-cache, clean the Gradle cache (./gradlew clean on Android), and clean the Xcode build folder (Product → Clean Build Folder on iOS).

  • Step 8 - Check native dependencies: If a native module was recently added or updated, verify its installation steps. Missing pod installs (cd ios && pod install) or incorrect gradle configurations are very common sources of native crashes.

Example (Sentry crash reporting setup):

jsx
1import * as Sentry from '@sentry/react-native';
2
3// Initialize once in your app entry point
4Sentry.init({
5  dsn: 'https://your-dsn@sentry.io/your-project',
6  environment: __DEV__ ? 'development' : 'production',
7  tracesSampleRate: 1.0,
8});
9
10// Capture a manual error
11try {
12  riskyOperation();
13} catch (error) {
14  Sentry.captureException(error);
15}
16
17// Add user context for better debugging
18Sentry.setUser({ id: user.id, email: user.email });
19

30. How do you handle large lists and pagination in production React Native apps?

Large lists are one of the most common performance bottlenecks in production React Native apps. Getting them right requires the right component choice, proper configuration, and a solid pagination strategy.

Always use FlatList (or SectionList for grouped data) - never ScrollView for dynamic lists. FlatList uses a windowing technique: it only renders the items currently visible on screen plus a small buffer (controlled by windowSize), unmounting items that scroll far off screen.

Key FlatList optimization props for large lists:

  • getItemLayout: If your list items have a fixed height, providing this function lets FlatList skip the measurement step entirely, drastically improving scroll performance for very long lists.

  • keyExtractor: Always provide a stable, unique key. This allows React to efficiently reuse existing component instances rather than recreating them.

  • removeClippedSubviews: Setting this to true unmounts views that are off screen from the native view hierarchy, reducing memory usage - especially helpful on Android.

  • maxToRenderPerBatch and updateCellsBatchingPeriod: These control how many items are rendered per batch and how often. Tuning them helps balance initial render speed vs scroll performance.

  • Memoize renderItem: Wrap your item component in React.memo to prevent re-rendering items whose data hasn't changed when the parent FlatList re-renders.

For pagination, the onEndReached callback fires when the user scrolls near the end of the list. Use it to fetch the next page and append items to your data array. Track a loading state to prevent duplicate requests.

Example (FlatList with pagination):

jsx
1import React, { useState, useCallback } from 'react';
2import { FlatList, View, Text, ActivityIndicator, StyleSheet } from 'react-native';
3
4const ITEM_HEIGHT = 72;
5
6// Memoized item component - avoids re-render if item data is unchanged
7const ProductItem = React.memo(({ item }) => (
8  <View style={styles.item}>
9    <Text style={styles.title}>{item.title}</Text>
10    <Text style={styles.price}>₹{item.price}</Text>
11  </View>
12));
13
14const ProductList = () => {
15  const [products, setProducts] = useState([]);
16  const [page, setPage] = useState(1);
17  const [loading, setLoading] = useState(false);
18  const [hasMore, setHasMore] = useState(true);
19
20  const fetchProducts = useCallback(async () => {
21    if (loading || !hasMore) return;
22    setLoading(true);
23    try {
24      const res = await fetch(`https://api.example.com/products?page=${page}&limit=20`);
25      const data = await res.json();
26      if (data.items.length === 0) {
27        setHasMore(false);
28      } else {
29        setProducts(prev => [...prev, ...data.items]);
30        setPage(prev => prev + 1);
31      }
32    } catch (err) {
33      console.error('Fetch error:', err);
34    } finally {
35      setLoading(false);
36    }
37  }, [loading, hasMore, page]);
38
39  const getItemLayout = useCallback((_, index) => ({
40    length: ITEM_HEIGHT,
41    offset: ITEM_HEIGHT * index,
42    index,
43  }), []);
44
45  const renderFooter = () => loading
46    ? <ActivityIndicator size="small" style={{ margin: 16 }} />
47    : null;
48
49  return (
50    <FlatList
51      data={products}
52      keyExtractor={item => item.id.toString()}
53      renderItem={({ item }) => <ProductItem item={item} />}
54      onEndReached={fetchProducts}
55      onEndReachedThreshold={0.5}  // trigger when 50% from the end
56      ListFooterComponent={renderFooter}
57      getItemLayout={getItemLayout}
58      removeClippedSubviews
59      maxToRenderPerBatch={10}
60      windowSize={10}
61    />
62  );
63};
64
65const styles = StyleSheet.create({
66  item: { height: ITEM_HEIGHT, padding: 16, borderBottomWidth: 1, borderColor: '#eee' },
67  title: { fontSize: 16, fontWeight: '500' },
68  price: { fontSize: 14, color: '#666', marginTop: 4 },
69});
70

31. How does the Metro bundler work in React Native?

Metro is the JavaScript bundler built specifically for React Native. It is the equivalent of webpack for web apps, but optimized for the mobile development workflow. Understanding how Metro works helps you debug build errors, optimize bundle size, and configure the build pipeline.

What Metro does during a build:

  • Resolution: Starting from your entry file (index.js), Metro follows every import statement to build a complete dependency graph of your entire application. It resolves module aliases, platform-specific files (e.g., Component.ios.js vs Component.android.js), and node_modules.

  • Transformation: Each file in the dependency graph is transformed. Metro runs your code through Babel to convert JSX to React.createElement() calls, downcompile modern JavaScript syntax to versions the JS engine can handle, and apply any custom transforms you've configured.

  • Serialization: Metro combines all the transformed modules into a single JavaScript bundle (.jsbundle). Each module is wrapped in a define() call that registers it by module ID, and a require() polyfill handles module resolution at runtime.

Metro in development mode:

  • Metro runs as a local HTTP server (default port 8081). The native app fetches the JS bundle from this server over your local network.

  • Metro caches the transformation results for every file. When you change a file, only that file and its dependents are re-transformed, not the whole bundle. This makes Fast Refresh nearly instant.

  • Symbolication: When a JavaScript error occurs, Metro translates the compiled bundle line numbers back into source code line numbers using source maps so stack traces point to your original JSX files.

Example (Customizing metro.config.js for SVG support):

javascript
1const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
2
3const defaultConfig = getDefaultConfig(__dirname);
4const { assetExts, sourceExts } = defaultConfig.resolver;
5
6const config = {
7  transformer: {
8    babelTransformerPath: require.resolve('react-native-svg-transformer'),
9  },
10  resolver: {
11    assetExts: assetExts.filter(ext => ext !== 'svg'),
12    sourceExts: [...sourceExts, 'svg'],
13  },
14};
15
16module.exports = mergeConfig(defaultConfig, config);
17

32. What are the common causes of unnecessary re-renders in React Native?

Unnecessary re-renders are a primary source of sluggish animations, slow list scrolling, and high CPU usage in React Native. Because native UI updates cross thread boundaries, minimizing wasted renders is critical.

  • Inline object literals and style arrays: Passing inline objects like style={{ padding: 10 }} or inline arrays creates a new reference on every single render, causing child components to re-render even if styles did not change. Always use StyleSheet.create or memoize objects.

  • Inline callback functions: Defining event handlers inline like onPress={() => doSomething(id)} creates a new function reference every render. Wrap callbacks passed to child components in useCallback.

  • Unmemoized Context providers: Providing an unmemoized object value={{ user, theme }} to a Context Provider causes every consumer across the entire subtree to re-render whenever the parent renders.

  • Over-subscribing to global state: Selecting an entire store state slice when a component only needs one property triggers re-renders on unrelated store updates. Always select granular primitive properties.

Example (Optimizing child component rendering with React.memo & useCallback):

jsx
1import React, { useState, useCallback } from 'react';
2import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
3
4// Child won't re-render unless title or onPress reference changes
5const ActionButton = React.memo(({ title, onPress }) => (
6  <TouchableOpacity style={styles.btn} onPress={onPress}>
7    <Text style={styles.btnText}>{title}</Text>
8  </TouchableOpacity>
9));
10
11const ParentScreen = () => {
12  const [count, setCount] = useState(0);
13
14  // Stable callback reference across re-renders
15  const handleAction = useCallback(() => {
16    console.log('Action triggered');
17  }, []);
18
19  return (
20    <View style={styles.container}>
21      <Text>Count: {count}</Text>
22      <ActionButton title="Save" onPress={handleAction} />
23    </View>
24  );
25};
26
27const styles = StyleSheet.create({
28  container: { padding: 20 },
29  btn: { backgroundColor: '#007AFF', padding: 12, borderRadius: 8, marginTop: 10 },
30  btnText: { color: '#fff', textAlign: 'center', fontWeight: '600' },
31});
32

33. Describe networking in React Native and how to make AJAX network calls.

React Native includes standard web networking APIs out of the box, including fetch and XMLHttpRequest (XHR), backed by native network stacks (NSURLSession on iOS and OkHttp on Android).

  • Standard fetch API: You can use standard async/await fetch calls just like in browser JavaScript without needing extra polyfills.

  • Axios & Interceptors: In production apps, Axios is commonly used for request/response interceptors (adding auth headers, refreshing expired JWTs, handling global error alerts).

  • TanStack Query (React Query): For server state management, caching, background refetching, and automatic offline retry behavior.

  • Network monitoring: Use @react-native-community/netinfo to detect online/offline transitions and pause network requests when disconnected.

Example (Robust Axios API client with auth interceptor):

javascript
1import axios from 'axios';
2import * as Keychain from 'react-native-keychain';
3
4const apiClient = axios.create({
5  baseURL: 'https://api.example.com/v1',
6  timeout: 10000,
7});
8
9// Automatically attach secure access token to every request
10apiClient.interceptors.request.use(async (config) => {
11  const credentials = await Keychain.getGenericPassword();
12  if (credentials) {
13    config.headers.Authorization = `Bearer ${credentials.password}`;
14  }
15  return config;
16}, (error) => Promise.reject(error));
17
18export default apiClient;
19

34. How can you write different code for iOS and Android in the same codebase?

React Native provides three built-in mechanisms for writing platform-specific logic and UI styles cleanly within a single codebase.

  • Platform.OS comparison: Simple conditional checks like Platform.OS === 'ios' for inline branching.

  • Platform.select(): Selects platform-specific values, components, or style objects cleanly from a configuration object.

  • Platform-specific file extensions: For completely different component architectures, create Button.ios.js and Button.android.js. Metro automatically bundles the appropriate file based on the target platform.

Example (Using Platform.select for styling and elevation):

jsx
1import { Platform, StyleSheet } from 'react-native';
2
3const styles = StyleSheet.create({
4  card: {
5    backgroundColor: '#fff',
6    borderRadius: 12,
7    padding: 16,
8    ...Platform.select({
9      ios: {
10        shadowColor: '#000',
11        shadowOffset: { width: 0, height: 2 },
12        shadowOpacity: 0.1,
13        shadowRadius: 6,
14      },
15      android: {
16        elevation: 4,
17      },
18    }),
19  },
20});
21

35. What is Network Security and SSL Pinning in React Native?

SSL Pinning (Certificate Pinning) secures API communication against Man-in-the-Middle (MitM) attacks. Standard HTTPS trusts any certificate signed by a system-trusted Certificate Authority (CA). SSL Pinning enforces that your mobile app only trusts a specific server certificate or public key hash.

  • Why it matters: If an attacker intercepts network traffic or installs a fraudulent CA certificate on a user's device, SSL Pinning prevents them from decrypting sensitive HTTPS payloads.

  • Android implementation: Configure a native Network Security Config file (res/xml/network_security_config.xml) with your domain's SHA-256 public key pin.

  • iOS implementation: Configure Info.plist under NSAppTransportSecurity using NSPinnedDomains with the SPKI SHA-256 digest.

  • Backup pins: Always pin at least one backup certificate key so app updates are not broken when your SSL certificate is renewed.

Example (Android res/xml/network_security_config.xml):

xml
1<?xml version="1.0" encoding="utf-8"?>
2<network-security-config>
3    <domain-config>
4        <domain includeSubdomains="true">api.example.com</domain>
5        <pin-set expiration="2028-01-01">
6            <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
7            <pin digest="SHA-256">backupPinHashHereSHA256Base64=</pin>
8        </pin-set>
9    </domain-config>
10</network-security-config>
11

36. How do you store sensitive data securely in React Native?

Never store sensitive data (JWT tokens, OAuth secrets, PII, passwords, or encryption keys) in AsyncStorage. AsyncStorage stores plain-text files on disk and can be read on rooted/jailbroken devices or extracted from unencrypted backups.

  • Hardware-backed secure storage: Use iOS Keychain Services and Android Keystore / EncryptedSharedPreferences (backed by hardware Trusted Execution Environments).

  • react-native-keychain: The industry-standard library for securely storing credentials and authentication tokens with biometric prompt options.

  • Encrypted MMKV: If you need high-speed synchronous storage (e.g., react-native-mmkv), generate an encryption key once, store the key in Keychain/Keystore, and instantiate MMKV with that key.

Example (Storing and retrieving auth tokens with react-native-keychain):

javascript
1import * as Keychain from 'react-native-keychain';
2
3// Save secure token
4export const saveAuthToken = async (token) => {
5  await Keychain.setGenericPassword('auth_token', token, {
6    accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
7  });
8};
9
10// Retrieve secure token
11export const getAuthToken = async () => {
12  const credentials = await Keychain.getGenericPassword();
13  return credentials ? credentials.password : null;
14};
15
16// Clear secure token on logout
17export const clearAuthToken = async () => {
18  await Keychain.resetGenericPassword();
19};
20

Found this helpful?

Share it with your network

Related Articles

Frontend

JavaScript Interview Questions

Prepare for your next tech interview with the most asked JavaScript interview questions and answers. It includes basic to advanced concepts, coding problems, and real-world scenarios for freshers and experienced developers.

Full Stack

Next Js Interview Questions

Explore the most important Next.js interview questions including SSR, SSG, ISR, routing, performance optimization, and real-world implementation examples.