Lesson 54 of 60 – Loading and Error Handling in React Native
90%

Loading and Error Handling in React Native

When a React Native application communicates with an API, the request may take some time or may fail. A professional application should handle these situations clearly.

Two important states are loading and error. Loading tells the user that an operation is in progress, while error handling tells the user when an operation could not be completed.

Start
  ↓
Loading
  ↓
API Request
  ↓
Success → Display Data
  ↓
Error   → Display Error Message
Note: Loading and error states are especially important when working with Fetch API, Axios, authentication, forms, and other asynchronous operations.

1. What is Loading State?

A loading state represents an operation that is currently in progress.

For example, while an application is requesting users from an API, the screen can show a loading indicator.

Loading...
Please wait.

This gives the user feedback that the application is working.

2. Why is Loading State Important?

Network requests do not always complete immediately. Without loading feedback, the user may think that the application is not responding.

  • Shows that a request is running
  • Improves user feedback
  • Prevents confusion
  • Makes asynchronous operations easier to understand

3. Creating a Loading State

The useState hook can be used to store loading status.

const [loading, setLoading] =
    useState(false);

When a request starts:

setLoading(true);

When the request finishes:

setLoading(false);

4. Using ActivityIndicator

React Native provides the ActivityIndicator component for showing a loading indicator.

import {
    ActivityIndicator
} from "react-native";

It can be displayed while the application is loading data.

<ActivityIndicator />

5. Conditional Loading UI

The loading state can be used to conditionally display the indicator.

{loading ? (

    <ActivityIndicator />

) : (

    <Text>
        Data Loaded
    </Text>

)}

When loading is true, the indicator is displayed.

6. What is an Error State?

An error state represents a failed operation.

For example, an API request can fail because of:

  • No internet connection
  • Server problems
  • Invalid request
  • Incorrect API URL
  • Authentication failure
  • Unexpected response

7. Creating an Error State

An error message can be stored using useState.

const [error, setError] =
    useState("");

When an error occurs:

setError(
    "Unable to load data"
);

8. Using try...catch

The try...catch statement can handle errors in asynchronous code.

try {

    const response =
        await fetch(url);

} catch (error) {

    console.log(error);

}

If the request throws an error, the catch block runs.

9. Handling Fetch Errors

try {

    const response =
        await fetch(url);

    if (!response.ok) {

        throw new Error(
            "Request failed"
        );

    }

} catch (error) {

    setError(
        "Unable to load data"
    );

}

Checking response.ok allows the application to handle unsuccessful HTTP responses explicitly.

10. Handling Axios Errors

try {

    const response =
        await axios.get(url);

    setData(response.data);

} catch (error) {

    setError(
        "Unable to load data"
    );

}

Axios rejects the Promise for certain request failures, making try...catch useful for error handling.

11. Loading and Error Together

A screen can maintain both loading and error states.

const [loading, setLoading] =
    useState(true);

const [error, setError] =
    useState("");

The application can then display the appropriate UI based on these states.

12. Basic Loading and Error Flow

Start Request
     ↓
setLoading(true)
     ↓
API Request
     ↓
  Success?
   /    \
 Yes     No
 ↓        ↓
Data    Error
 ↓        ↓
Show    Show
Data    Message
     \  /
       ↓
setLoading(false)

13. Using finally

The finally block runs after the try or catch block. It is useful for stopping a loading indicator.

try {

    const response =
        await axios.get(url);

} catch (error) {

    setError(
        "Request failed"
    );

} finally {

    setLoading(false);

}

14. Clearing Previous Errors

When starting a new request, clear an old error message.

setError("");
setLoading(true);

This prevents an old error message from remaining visible during a new request.

15. Displaying an Error Message

{error ? (

    <Text>
        {error}
    </Text>

) : null}

The error message is displayed only when an error value exists.

16. Creating a Retry Button

When a network request fails, the user can be given an option to try again.

<Button
    title="Retry"
    onPress={loadData}
/>

The Retry button can call the same function that loads the data.

17. Loading State with Retry

if (loading) {

    return (
        <ActivityIndicator />
    );

}

if (error) {

    return (

        <View>

            <Text>
                {error}
            </Text>

            <Button
                title="Retry"
                onPress={loadData}
            />

        </View>

    );

}

18. Empty Data State

Loading and error states are not the only states an application should consider. An API can successfully return an empty list.

if (!loading &&
    !error &&
    data.length === 0) {

    return (
        <Text>
            No data found.
        </Text>
    );

}

This provides a useful message when there is no data to display.

19. Success State

After a successful request, the application can display the returned data.

if (!loading &&
    !error) {

    return (
        <Text>
            Data Loaded Successfully
        </Text>
    );

}

A complete UI may therefore have loading, error, empty and success states.

20. Four Common API UI States

API Screen
    |
    ├── Loading
    |
    ├── Error
    |
    ├── Empty
    |
    └── Success
State Example
Loading Loading...
Error Unable to load data
Empty No courses found
Success Course list

21. Loading API Data with useEffect

useEffect(() => {

    loadData();

}, []);

The data loading function can be called when the component is mounted.

The function should update loading, data and error states appropriately.

22. Handling Network Errors

A network request may fail because the device cannot communicate with the server.

try {

    const response =
        await fetch(url);

} catch (error) {

    setError(
        "Please check your internet connection."
    );

}

The message should be understandable to the user.

23. Handling Server Errors

A server may return an unsuccessful HTTP status.

const response =
    await fetch(url);

if (!response.ok) {

    throw new Error(
        "Server request failed"
    );

}

The application can then display an appropriate error state.

24. Handling Login Errors

Authentication screens should also provide useful error feedback.

try {

    await axios.post(
        "/api/login",
        loginData
    );

} catch (error) {

    setError(
        "Invalid login details"
    );

}

The exact message should reflect what the backend reports and what is safe to reveal to the user.

25. Avoiding Multiple Requests

A loading state can also help prevent repeated actions while a request is in progress.

<Button
    title={
        loading
        ? "Loading..."
        : "Load Data"
    }
    disabled={loading}
    onPress={loadData}
/>

Disabling an action during a request can help avoid accidental duplicate requests.

26. Common Loading and Error Mistakes

  • Not showing any loading feedback.
  • Never resetting the loading state.
  • Ignoring network errors.
  • Ignoring unsuccessful HTTP responses.
  • Displaying technical error details directly to users.
  • Not providing a retry option when appropriate.
  • Leaving old error messages visible during new requests.
  • Ignoring empty API responses.
  • Allowing repeated requests while one is already running.

27. Complete Loading and Error Example

import React, {
    useEffect,
    useState
} from "react";

import {
    View,
    Text,
    Button,
    ActivityIndicator,
    FlatList
} from "react-native";

import axios from "axios";

export default function CoursesScreen() {

    const [courses, setCourses] =
        useState([]);

    const [loading, setLoading] =
        useState(true);

    const [error, setError] =
        useState("");

    async function loadCourses() {

        try {

            setLoading(true);
            setError("");

            const response =
                await axios.get(
                    "https://example.com/api/courses"
                );

            setCourses(
                response.data
            );

        } catch (error) {

            setError(
                "Unable to load courses."
            );

        } finally {

            setLoading(false);

        }

    }

    useEffect(() => {

        loadCourses();

    }, []);

    if (loading) {

        return (

            <View>

                <ActivityIndicator />

                <Text>
                    Loading courses...
                </Text>

            </View>

        );

    }

    if (error) {

        return (

            <View>

                <Text>
                    {error}
                </Text>

                <Button
                    title="Retry"
                    onPress={loadCourses}
                />

            </View>

        );

    }

    if (courses.length === 0) {

        return (

            <Text>
                No courses found.
            </Text>

        );

    }

    return (

        <FlatList
            data={courses}
            keyExtractor={
                item =>
                item.id.toString()
            }
            renderItem={({ item }) => (

                <View>

                    <Text>
                        {item.name}
                    </Text>

                </View>

            )}
        />

    );

}

This example handles loading, error, empty and success states in one screen.

28. Loading and Error Best Practices

  • Always provide feedback during longer asynchronous operations.
  • Keep loading state changes consistent.
  • Handle both network and server failures.
  • Show user-friendly error messages.
  • Provide retry functionality where useful.
  • Handle empty data separately from errors.
  • Disable repeated actions while appropriate requests are running.
  • Keep detailed technical information in logs rather than exposing it unnecessarily.
  • Test success, loading, empty and error scenarios.

29. Important Loading and Error Concepts

Concept Purpose
loading Indicates that an operation is in progress
error Stores or represents a failed operation
ActivityIndicator Displays a visual loading indicator
try...catch Handles errors in asynchronous operations
finally Runs after the try or catch block
Retry Allows the user to attempt a failed request again
Empty State Handles successful requests with no data
Success State Displays successfully loaded data

30. Complete Loading and Error Workflow

Open Screen
     ↓
Start API Request
     ↓
setLoading(true)
     ↓
Show Loading UI
     ↓
Send Request
     ↓
     Result
    /      \
Success    Failure
  ↓          ↓
Data       Error
  ↓          ↓
Check      Set Error
Empty?       ↓
  ↓       Error UI
Show Data    ↓
  ↓        Retry
setLoading(false)
     ↓
Final UI State

Loading and error handling are essential when building React Native applications that communicate with APIs. A good screen should clearly handle loading, success, empty and error states so that users always understand what is happening.

📌 Key Points

  • Use a loading state while an asynchronous operation is running.
  • ActivityIndicator can display loading feedback.
  • Use an error state to represent failed operations.
  • Use try...catch for asynchronous error handling.
  • Check HTTP responses when using Fetch.
  • Use finally to reset loading state after a request.
  • Provide retry functionality when appropriate.
  • Handle empty data separately from errors.
  • Disable repeated actions when necessary.
  • Test loading, success, empty and error states.
  • Use clear and user-friendly error messages.
  • Good state handling makes API-driven screens easier to use.

🧠 Quick Quiz

Question: Which React Native component is commonly used to display a loading indicator?