Lesson 71 of 60 – Typing Reusable Components
100%

Typing Reusable Components

Reusable components are an important part of React Native development. Instead of writing the same UI code multiple times, we can create a component once and reuse it in different screens.

TypeScript helps us make reusable components safer by defining the types of their props, functions, children, and data.

Note: A reusable component should have clearly defined props so that TypeScript can detect incorrect values when the component is used.

1. What is a Reusable Component?

A reusable component is a component that can be used in multiple places in an application.

For example, a custom Button component can be used on the Login, Register, Profile, and Settings screens.

function Welcome(): React.JSX.Element {

    return (
        <Text>
            Welcome
        </Text>
    );
}

2. Why Use Reusable Components?

  • Reduce duplicate code.
  • Keep the application organized.
  • Make UI changes easier.
  • Improve consistency.
  • Make components easier to maintain.

3. Component Props

Props allow a parent component to send information to a child component.

interface UserProps {
    name: string;
}

function UserCard(
    { name }: UserProps
): React.JSX.Element {

    return (
        <Text>{name}</Text>
    );
}

4. Typing Component Props

An interface can be used to describe the props of a component.

interface ButtonProps {
    title: string;
    onPress: () => void;
}

Here, title must be a string and onPress must be a function that does not return a value.

5. Creating a Reusable Button

interface ButtonProps {
    title: string;
    onPress: () => void;
}

function CustomButton(
    { title, onPress }: ButtonProps
): React.JSX.Element {

    return (
        <Button
            title={title}
            onPress={onPress}
        />
    );
}

6. Using the Reusable Button

<CustomButton
    title="Login"
    onPress={() =>
        console.log("Login")
    }
/>

The same component can be used with different titles and functions.

7. Multiple Props

interface UserProps {
    name: string;
    age: number;
    course: string;
}

Multiple properties can be defined inside the same interface.

8. Reusable User Card

interface UserProps {
    name: string;
    age: number;
    course: string;
}

function UserCard(
    { name, age, course }: UserProps
): React.JSX.Element {

    return (
        <View>

            <Text>
                {name}
            </Text>

            <Text>
                Age: {age}
            </Text>

            <Text>
                Course: {course}
            </Text>

        </View>
    );
}

9. Passing Data to a Component

<UserCard
    name="Rahul"
    age={20}
    course="React Native"
/>

TypeScript checks that all required props have the correct types.

10. Optional Props

Use ? when a prop is optional.

interface UserProps {
    name: string;
    age?: number;
}

The age property does not have to be provided.

11. Default Values for Optional Props

interface ButtonProps {
    title: string;
    disabled?: boolean;
}

function CustomButton(
    {
        title,
        disabled = false
    }: ButtonProps
): React.JSX.Element {

    return (
        <Button
            title={title}
            disabled={disabled}
            onPress={() => {}}
        />
    );
}

12. Boolean Props

interface ButtonProps {
    title: string;
    disabled: boolean;
}

Boolean props can be used for states such as disabled, loading, selected, or active.

<CustomButton
    title="Save"
    disabled={true}
/>

13. Union Types in Props

Union types allow a prop to accept more than one specific type.

interface BadgeProps {
    status: "active" | "inactive";
}

The status can only be active or inactive.

14. Function Props

A reusable component can receive a function as a prop.

interface CardProps {
    title: string;
    onSelect: () => void;
}

This allows the parent component to decide what should happen when the child component is selected.

15. Function Props with Arguments

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

interface StudentCardProps {
    student: Student;
    onSelect: (student: Student) => void;
}

The function must receive a Student object.

16. Reusable Student Card

interface Student {
    id: number;
    name: string;
    course: string;
}

interface StudentCardProps {
    student: Student;
    onSelect: (student: Student) => void;
}

function StudentCard(
    {
        student,
        onSelect
    }: StudentCardProps
): React.JSX.Element {

    return (
        <Pressable
            onPress={() =>
                onSelect(student)
            }
        >

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

            <Text>
                {student.course}
            </Text>

        </Pressable>
    );
}

17. Reusable Input Component

interface InputProps {
    value: string;
    placeholder: string;
    onChangeText: (text: string) => void;
}

function CustomInput(
    {
        value,
        placeholder,
        onChangeText
    }: InputProps
): React.JSX.Element {

    return (
        <TextInput
            value={value}
            placeholder={placeholder}
            onChangeText={onChangeText}
        />
    );
}

18. Reusable Container Component

A container component can be created to provide common layout or styling.

interface ContainerProps {
    children: React.ReactNode;
}

function Container(
    { children }: ContainerProps
): React.JSX.Element {

    return (
        <View>
            {children}
        </View>
    );
}

19. Typing children

When a component accepts nested content, the children prop can be typed as React.ReactNode.

interface CardProps {
    children: React.ReactNode;
}

This allows the component to receive text, elements, and other React content.

20. Reusable Card Component

interface CardProps {
    title: string;
    children: React.ReactNode;
}

function CustomCard(
    {
        title,
        children
    }: CardProps
): React.JSX.Element {

    return (
        <View>

            <Text>
                {title}
            </Text>

            {children}

        </View>
    );
}

21. Using the Reusable Card

<CustomCard title="Student">

    <Text>
        Rahul
    </Text>

    <Text>
        React Native
    </Text>

</CustomCard>

The same Card component can be reused with different content.

22. Typing Style Props

A reusable component can also accept a style prop.

import {
    StyleProp,
    ViewStyle
} from "react-native";

interface ContainerProps {
    children: React.ReactNode;
    style?: StyleProp<ViewStyle>;
}

This allows callers to provide additional View styles.

23. Common Reusable Component Mistakes

  • Using any for all props.
  • Making every prop required unnecessarily.
  • Using incorrect function prop types.
  • Forgetting to type children.
  • Using incorrect property names.
  • Putting too much application logic inside simple UI components.
  • Creating components that are too specific to reuse.

24. Best Practices

  • Create a clear interface for component props.
  • Use meaningful prop names.
  • Make optional props optional with ?.
  • Use union types when only specific values are allowed.
  • Type callback functions correctly.
  • Use React.ReactNode for children when appropriate.
  • Keep reusable components focused on a specific responsibility.

25. Reusable Components with State

A reusable component can also maintain its own state when the state belongs to that component.

interface CounterProps {
    initialValue?: number;
}

function Counter(
    { initialValue = 0 }: CounterProps
): React.JSX.Element {

    const [count, setCount] =
        useState<number>(initialValue);

    return (
        <Button
            title={`Count: ${count}`}
            onPress={() =>
                setCount(count + 1)
            }
        />
    );
}

26. Reusable Components with API Data

Reusable components can receive typed API objects as props.

interface User {
    id: number;
    name: string;
}

interface UserCardProps {
    user: User;
}

function UserCard(
    { user }: UserCardProps
): React.JSX.Element {

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

27. Reusable Components with FlatList

Reusable typed components are especially useful with FlatList.

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

The StudentCard receives a correctly typed Student object.

28. Complete Reusable Component Example

import React from "react";

import {
    View,
    Text,
    Pressable,
    StyleSheet
} from "react-native";

interface Student {
    id: number;
    name: string;
    course: string;
}

interface StudentCardProps {
    student: Student;
    onSelect: (student: Student) => void;
}

function StudentCard(
    {
        student,
        onSelect
    }: StudentCardProps
): React.JSX.Element {

    return (
        <Pressable
            style={styles.card}
            onPress={() =>
                onSelect(student)
            }
        >

            <Text style={styles.name}>
                {student.name}
            </Text>

            <Text>
                Course: {student.course}
            </Text>

        </Pressable>
    );
}

export default function App(): React.JSX.Element {

    const students: Student[] = [
        {
            id: 1,
            name: "Rahul",
            course: "React Native"
        },
        {
            id: 2,
            name: "Priya",
            course: "TypeScript"
        }
    ];

    const handleStudent =
        (student: Student): void => {

        console.log(
            "Selected:",
            student.name
        );

    };

    return (
        <View style={styles.container}>

            <Text style={styles.title}>
                Students
            </Text>

            {students.map(
                (student: Student) => (

                <StudentCard
                    key={student.id}
                    student={student}
                    onSelect={handleStudent}
                />

            ))}

        </View>
    );
}

const styles = StyleSheet.create({

    container: {
        flex: 1,
        padding: 20
    },

    title: {
        fontSize: 24,
        fontWeight: "bold",
        marginBottom: 15
    },

    card: {
        padding: 15,
        marginBottom: 10,
        borderWidth: 1,
        borderRadius: 8
    },

    name: {
        fontSize: 18,
        fontWeight: "bold"
    }

});

29. Benefits of Typed Reusable Components

  • Components become easier to reuse.
  • Incorrect props can be detected during development.
  • Callback functions have clear parameter types.
  • Component APIs become easier to understand.
  • Large React Native applications become easier to maintain.
  • Reusable components can be shared across multiple screens.

30. Summary

Reusable components help us avoid duplicate code and create consistent React Native applications. TypeScript makes these components safer by defining their props, callback functions, children, styles, and data.

You should now understand typed component props, optional props, union types, function props, children, style props, reusable inputs, buttons, cards, student components, API data, and FlatList components.

📌 Key Points

  • Use interfaces to define reusable component props.
  • Use optional properties for props that are not always required.
  • Use union types when a prop accepts specific values.
  • Type callback functions with their parameters and return types.
  • Use React.ReactNode for component children when appropriate.
  • Reusable components can receive typed API and FlatList data.
  • Keep reusable components focused and easy to understand.

🧠 Quick Quiz

Question: Which TypeScript type is commonly used for React component children?