Lesson 120 of 158 – TypeScript with FlatList API Data
76%

TypeScript with FlatList API Data

React Native's FlatList is commonly used to display lists received from REST APIs. When using TypeScript, we can define the API data structure and use that type throughout the FlatList component.

Note: In this lesson, we will connect TypeScript interfaces, Axios, REST API data, React Native state, and FlatList to create a type-safe student list.

1. What is FlatList?

FlatList is a React Native component used to efficiently display a list of data.

<FlatList
    data={students}
    renderItem={({ item }) => (
        <Text>{item.name}</Text>
    )}
/>

It is especially useful for API data containing many records.

2. Why Use TypeScript with FlatList?

  • Defines the structure of each list item.
  • Provides better editor suggestions.
  • Prevents incorrect property access.
  • Improves API data handling.
  • Makes large applications easier to maintain.

3. Creating a Student Interface

interface Student {
    id: number;
    name: string;
    email: string;
    mobile: string;
}

This interface describes one student object.

4. Creating a Student Array

const students: Student[] = [];

Student[] means an array containing Student objects.

const students: Student[] = [
    {
        id: 1,
        name: "Rahul",
        email: "rahul@example.com",
        mobile: "9876543210"
    }
];

5. Typing FlatList State

React Native state can be typed using the Student interface.

const [students, setStudents] =
    useState<Student[]>([]);

TypeScript now knows that students contains an array of Student objects.

6. API Response Interface

interface StudentResponse {
    success: boolean;
    message: string;
    data: Student[];
}

This represents a typical PHP REST API response containing multiple students.

7. Fetching Typed Data with Axios

const response =
    await api.get<StudentResponse>(
        "/students"
    );

setStudents(response.data.data);

Axios knows that the API response follows the StudentResponse interface.

8. Basic Typed FlatList

<FlatList
    data={students}
    renderItem={({ item }) => (
        <Text>{item.name}</Text>
    )}
/>

Because students is typed as Student[], TypeScript understands the type of item.

9. Using keyExtractor

FlatList requires a stable key for each list item.

<FlatList
    data={students}
    keyExtractor={item =>
        item.id.toString()
    }
    renderItem={({ item }) => (
        <Text>{item.name}</Text>
    )}
/>

10. Accessing Typed Properties

renderItem={({ item }) => (
    <View>
        <Text>{item.name}</Text>
        <Text>{item.email}</Text>
        <Text>{item.mobile}</Text>
    </View>
)}

TypeScript provides information about the available Student properties.

11. Creating a Student Card Component

A separate component can be used to display each student.

interface StudentCardProps {
    student: Student;
}

const StudentCard = ({
    student
}: StudentCardProps) => {

    return (
        <View>
            <Text>{student.name}</Text>
            <Text>{student.email}</Text>
        </View>
    );
};

12. Using StudentCard with FlatList

<FlatList
    data={students}
    keyExtractor={item =>
        item.id.toString()
    }
    renderItem={({ item }) => (
        <StudentCard student={item} />
    )}
/>

13. Loading API Data

API data can be loaded using useEffect().

useEffect(() => {

    const loadStudents = async () => {

        const response =
            await api.get<StudentResponse>(
                "/students"
            );

        setStudents(response.data.data);
    };

    loadStudents();

}, []);

14. Adding Loading State

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

The loading state can be used while the API request is running.

setLoading(true);

try {
    // API request
} finally {
    setLoading(false);
}

15. Adding Error State

const [errorMessage, setErrorMessage] =
    useState<string>("");

If the API request fails, a message can be stored in this state.

setErrorMessage(
    "Unable to load students"
);

16. Typed FlatList with Loading and Error

{loading && (
    <Text>Loading...</Text>
)}

{errorMessage !== "" && (
    <Text>{errorMessage}</Text>
)}

<FlatList
    data={students}
    keyExtractor={item =>
        item.id.toString()
    }
    renderItem={({ item }) => (
        <Text>{item.name}</Text>
    )}
/>

17. Handling an Empty List

FlatList provides ListEmptyComponent for empty data.

<FlatList
    data={students}
    keyExtractor={item =>
        item.id.toString()
    }
    renderItem={({ item }) => (
        <Text>{item.name}</Text>
    )}
    ListEmptyComponent={
        <Text>No students found</Text>
    }
/>

18. Pull to Refresh

FlatList can refresh API data using RefreshControl or its refresh properties.

<FlatList
    data={students}
    refreshing={refreshing}
    onRefresh={loadStudents}
    keyExtractor={item =>
        item.id.toString()
    }
    renderItem={({ item }) => (
        <Text>{item.name}</Text>
    )}
/>

19. Typed Search with FlatList

interface StudentSearchParams {
    search: string;
}

const params: StudentSearchParams = {
    search: "Rahul"
};

const response =
    await api.get<StudentResponse>(
        "/students",
        {
            params
        }
    );

setStudents(response.data.data);

20. Typed Pagination Data

interface PaginationMeta {
    page: number;
    limit: number;
    total: number;
    totalPages: number;
}

interface StudentResponse {
    success: boolean;
    message: string;
    data: Student[];
    meta: PaginationMeta;
}

The FlatList can use this metadata to implement pagination.

21. Loading More FlatList Data

The onEndReached property can load another API page when the user reaches the end of the list.

<FlatList
    data={students}
    onEndReached={loadMore}
    onEndReachedThreshold={0.5}
    keyExtractor={item =>
        item.id.toString()
    }
    renderItem={({ item }) => (
        <Text>{item.name}</Text>
    )}
/>

22. Typed Pagination State

const [page, setPage] =
    useState<number>(1);

const [hasMore, setHasMore] =
    useState<boolean>(true);

const [loadingMore, setLoadingMore] =
    useState<boolean>(false);

TypeScript keeps the pagination state consistent.

23. Combining API Data with Existing Data

When another page is loaded, the new students can be appended.

setStudents(previous => [
    ...previous,
    ...response.data.data
]);

Both arrays are typed as Student arrays.

24. Reusable Typed List Item

interface StudentItemProps {
    item: Student;
}

const StudentItem = ({
    item
}: StudentItemProps) => {

    return (
        <View>
            <Text>{item.name}</Text>
            <Text>{item.email}</Text>
            <Text>{item.mobile}</Text>
        </View>
    );
};

25. Using a Typed List Item

<FlatList
    data={students}
    keyExtractor={item =>
        item.id.toString()
    }
    renderItem={({ item }) => (
        <StudentItem item={item} />
    )}
/>

The StudentItem component receives only a Student object.

26. Common TypeScript FlatList Mistakes

  • Using any[] instead of a specific interface.
  • Using the wrong property names.
  • Forgetting to convert numeric IDs to strings in keyExtractor.
  • Assuming API data always exists.
  • Not handling an empty list.
  • Using an incorrect API response interface.
  • Appending data without checking pagination.

27. Complete Student API Interfaces

interface Student {
    id: number;
    name: string;
    email: string;
    mobile: string;
}

interface PaginationMeta {
    page: number;
    limit: number;
    total: number;
    totalPages: number;
}

interface StudentResponse {
    success: boolean;
    message: string;
    data: Student[];
    meta: PaginationMeta;
}

28. Complete API Loading Function

const loadStudents = async () => {

    try {

        setLoading(true);
        setErrorMessage("");

        const response =
            await api.get<StudentResponse>(
                "/students?page=1&limit=10"
            );

        setStudents(response.data.data);

    } catch (error) {

        setErrorMessage(
            "Unable to load students"
        );

    } finally {

        setLoading(false);

    }
};

29. Complete Typed FlatList Example

import React, { useEffect, useState } from "react";
import {
    View,
    Text,
    FlatList
} from "react-native";
import api from "./api";

interface Student {
    id: number;
    name: string;
    email: string;
    mobile: string;
}

interface StudentResponse {
    success: boolean;
    message: string;
    data: Student[];
}

const StudentList = () => {

    const [students, setStudents] =
        useState<Student[]>([]);

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

    const [errorMessage, setErrorMessage] =
        useState<string>("");

    const loadStudents = async () => {

        try {

            setLoading(true);
            setErrorMessage("");

            const response =
                await api.get<StudentResponse>(
                    "/students"
                );

            setStudents(response.data.data);

        } catch (error) {

            setErrorMessage(
                "Unable to load students"
            );

        } finally {

            setLoading(false);

        }
    };

    useEffect(() => {
        loadStudents();
    }, []);

    return (
        <View>

            {loading && (
                <Text>Loading...</Text>
            )}

            {errorMessage !== "" && (
                <Text>{errorMessage}</Text>
            )}

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

                    <View>
                        <Text>{item.name}</Text>
                        <Text>{item.email}</Text>
                        <Text>{item.mobile}</Text>
                    </View>

                )}
                ListEmptyComponent={
                    <Text>No students found</Text>
                }
            />

        </View>
    );
};

export default StudentList;

30. Complete TypeScript + API + FlatList Flow

PHP + MySQL
      ↓
REST API
      ↓
JSON Response
      ↓
Axios
      ↓
StudentResponse Interface
      ↓
React Native State
      ↓
Student[]
      ↓
FlatList
      ↓
Typed Student Item
      ↓
Mobile UI

The PHP REST API returns student data as JSON. Axios receives the response, the TypeScript interface describes its structure, and the Student array is stored in React Native state.

Finally, FlatList displays each Student object. This approach gives us a clean and type-safe connection between the REST API and the React Native user interface.

📌 Key Points

  • FlatList is useful for displaying API data in React Native.
  • TypeScript interfaces define the structure of each list item.
  • Use Student[] for an array of student objects.
  • Axios can return typed API responses.
  • FlatList's renderItem receives a typed item.
  • Use keyExtractor with a unique ID.
  • Loading, error, and empty states should be handled.
  • Typed pagination metadata can support infinite scrolling.
  • Reusable typed list-item components improve code organization.
  • TypeScript makes API-driven FlatList applications safer and easier to maintain.

🧠 Quick Quiz

Question: Which property of FlatList contains the array of records that should be displayed?