Popular Searches
Popular Course Categories
Popular Courses

Handling Events and User Interactions

Handling Events and User Interactions

React Fundamentals for React Native

Handling Events and User Interactions in React Native

Course: React Native Training

Topic: Handling Events and User Interactions

Course Link: React Native Training Course

Demo Link: Register for a Course Demo

JustAcademy Website: Visit JustAcademy

Official Documentation: React Native Handling Touches


1. Introduction to Events and User Interactions

Handling events and user interactions is an essential part of React Native application development. Mobile applications need to respond to user actions such as tapping buttons, entering text, selecting options, scrolling lists, submitting forms, and pressing controls.

React Native provides components and event props that allow developers to detect these actions and execute JavaScript functions in response. For example, a button can submit a form, a text input can update a search query, and a pressable card can open a product details screen.

React Native event handling usually involves three main elements:

  • Interactive component: The component that receives the interaction, such as Pressable, TextInput, or ScrollView.
  • Event prop: The property that connects the component to an event handler, such as onPress or onChangeText.
  • Event handler: A JavaScript function that runs when the event occurs.

Example

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

export default function App() {
  const handlePress = () => {
    console.log('Button pressed!');
  };

  return (
    <View>
      <Pressable onPress={handlePress}>
        <Text>Click Me</Text>
      </Pressable>
    </View>
  );
}

When the user presses the component, React Native invokes handlePress. This function can perform an action, update state, validate data, or call another function.

2. Learning Objectives

After completing this topic, learners will be able to:

  • Understand event handling in React Native.
  • Identify common mobile interaction events.
  • Use Pressable to create interactive controls.
  • Handle button presses and pass arguments to event handlers.
  • Capture text input using TextInput.
  • Use state to update the interface after user interactions.
  • Handle switches, checkboxes, and selection controls.
  • Understand event objects and touch information.
  • Validate forms and display error messages.
  • Prevent repeated or invalid actions where appropriate.
  • Build interactive components that provide clear user feedback.
  • Apply event-handling best practices in mobile applications.

3. What Is an Event in React Native?

An event is an occurrence that a component or application can respond to. Events may be triggered by a user action or by interactions with the device and its interface.

For example, pressing a button produces a press event, changing the value in a text input triggers a change callback, and scrolling a list can trigger a scroll event.

3.1 Common User Interaction Events

Event Prop Purpose Example Use
onPress Responds to a press interaction. Submit button or navigation control.
onLongPress Responds when the user presses and holds a supported pressable component. Open a contextual menu.
onPressIn Runs when a press interaction begins. Provide immediate pressed feedback.
onPressOut Runs when the press interaction ends. Reset a temporary pressed state.
onChangeText Provides the updated text value. Search field or form input.
onSubmitEditing Runs when the keyboard's submit action is used. Submit a search query.
onFocus Runs when a supported input receives focus. Highlight an active input.
onBlur Runs when an input loses focus. Validate a field after editing.
onScroll Reports scrolling activity. Track a scroll position.
onRefresh Requests a refresh when used with a supported refreshing control, such as RefreshControl. Refresh a list of records.

4. Understanding Event Handlers

An event handler is a function that runs in response to an event. Event handlers can be declared inside or outside a component, depending on their responsibilities.

4.1 Basic Event Handler

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

export default function App() {
  const handleLogin = () => {
    console.log('Login button pressed');
  };

  return (
    <View>
      <Pressable onPress={handleLogin}>
        <Text>Login</Text>
      </Pressable>
    </View>
  );
}

The handleLogin function is passed to onPress. React Native invokes it when the user presses the control.

4.2 Passing a Function Reference

Pass the function reference when you want the framework to call the handler after the event occurs.

<Pressable onPress={handleLogin}>
  <Text>Login</Text>
</Pressable>

A common mistake is calling the function immediately during rendering.

// Incorrect for a normal event handler
<Pressable onPress={handleLogin()}>
  <Text>Login</Text>
</Pressable>

In the incorrect example, handleLogin() is evaluated while React renders the component. This may execute the function before the user presses anything.

5. Using Pressable for User Interactions

Pressable is a flexible React Native component for detecting press interactions. It can be used to build buttons, clickable cards, selectable list items, and custom interactive controls.

5.1 Basic Pressable Example

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

export default function App() {
  const showMessage = () => {
    console.log('Welcome to React Native');
  };

  return (
    <View>
      <Pressable
        onPress={showMessage}
        accessibilityRole="button"
      >
        <Text>Show Message</Text>
      </Pressable>
    </View>
  );
}

5.2 Handling Press State

The style prop of Pressable can receive a function that provides the current pressed state. This allows developers to change a control's appearance while it is being pressed.

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

export default function App() {
  return (
    <View style={{ padding: 20 }}>
      <Pressable
        onPress={() => console.log('Pressed')}
        style={({ pressed }) => ({
          padding: 14,
          backgroundColor: pressed ? '#17408B' : '#2874D0',
          borderRadius: 8,
          opacity: pressed ? 0.8 : 1
        })}
      >
        <Text style={{ color: '#FFFFFF', textAlign: 'center' }}>
          Press Here
        </Text>
      </Pressable>
    </View>
  );
}

This example provides visual feedback while the user presses the control.

6. Handling Button Presses and Function Arguments

Some event handlers need additional information, such as a product ID, selected category, or user ID. You can pass arguments by creating a callback function.

6.1 Passing Arguments to an Event Handler

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

export default function App() {
  const handleSelect = (courseName) => {
    console.log('Selected course:', courseName);
  };

  return (
    <View>
      <Pressable
        onPress={() => handleSelect('React Native')}
      >
        <Text>Select React Native</Text>
      </Pressable>
    </View>
  );
}

The arrow function delays the call until the press event occurs and passes the specified course name to the handler.

6.2 Reusable Button Component

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

function CustomButton({ title, onPress, disabled = false }) {
  return (
    <Pressable
      onPress={onPress}
      disabled={disabled}
      accessibilityRole="button"
      style={{
        padding: 12,
        backgroundColor: disabled ? '#999999' : '#246BCE',
        borderRadius: 8,
        opacity: disabled ? 0.6 : 1
      }}
    >
      <Text style={{ color: '#FFFFFF', textAlign: 'center' }}>
        {title}
      </Text>
    </Pressable>
  );
}

export default CustomButton;

This component receives a title, a callback, and an optional disabled value. It can be reused for different actions by passing different props.

7. Managing Events with React State

Event handlers often need to update the user interface. React's useState hook stores values that change over time. When state is updated through its setter, React can render the component again with the new value.

7.1 Example: Counter Application

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

export default function CounterApp() {
  const [count, setCount] = useState(0);

  const increaseCount = () => {
    setCount(previousCount => previousCount + 1);
  };

  const decreaseCount = () => {
    setCount(previousCount => previousCount - 1);
  };

  const resetCount = () => {
    setCount(0);
  };

  return (
    <View style={{ padding: 20, gap: 12 }}>
      <Text style={{ fontSize: 24 }}>Count: {count}</Text>

      <Pressable onPress={increaseCount}>
        <Text>Increase</Text>
      </Pressable>

      <Pressable onPress={decreaseCount}>
        <Text>Decrease</Text>
      </Pressable>

      <Pressable onPress={resetCount}>
        <Text>Reset</Text>
      </Pressable>
    </View>
  );
}

The counter demonstrates how an event handler can update state and how the updated state appears in the rendered interface. Functional state updates are helpful when the new value depends on the previous value.

```html

8. Handling Text Input Events

The TextInput component allows users to enter text through the mobile device's keyboard. It supports callbacks that respond to changes in text, focus changes, and keyboard submission.

Common props include:

  • onChangeText: Receives the updated text as a string.
  • onSubmitEditing: Runs when the user submits the input using the keyboard's submit action.
  • onFocus: Runs when the input receives focus.
  • onBlur: Runs when the input loses focus.
  • placeholder: Displays a hint when the input is empty.
  • keyboardType: Configures the keyboard layout for the expected input.

8.1 Basic Text Input Example

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

export default function App() {
  const [name, setName] = useState('');

  return (
    <View style={{ padding: 20 }}>
      <TextInput
        placeholder="Enter your name"
        value={name}
        onChangeText={setName}
        style={{
          borderWidth: 1,
          borderColor: '#999999',
          padding: 12,
          borderRadius: 8
        }}
      />

      <Text>Your name: {name}</Text>
    </View>
  );
}

The input value is stored in state. Every time the user types, onChangeText calls setName, and React updates the displayed name.

8.2 Handling Keyboard Submission

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

export default function SearchScreen() {
  const [query, setQuery] = useState('');
  const [submittedQuery, setSubmittedQuery] = useState('');

  const handleSearch = () => {
    setSubmittedQuery(query.trim());
  };

  return (
    <View style={{ padding: 20 }}>
      <TextInput
        placeholder="Search courses"
        value={query}
        onChangeText={setQuery}
        onSubmitEditing={handleSearch}
        returnKeyType="search"
        style={{ borderWidth: 1, padding: 12 }}
      />

      <Pressable onPress={handleSearch}>
        <Text>Search</Text>
      </Pressable>

      <Text>Search term: {submittedQuery}</Text>
    </View>
  );
}

This example supports submitting the search from either the keyboard or the button. In a real application, the handler can call a search service or filter local data.

Reference: React Native Text Input Documentation.

9. Handling Switch and Toggle Events

The React Native Switch component allows users to enable or disable a setting. Its onValueChange prop receives the new boolean value when the switch changes.

9.1 Switch Example

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

export default function SettingsScreen() {
  const [notificationsEnabled, setNotificationsEnabled] = useState(false);

  return (
    <View style={{ padding: 20 }}>
      <Text>Enable Notifications</Text>

      <Switch
        value={notificationsEnabled}
        onValueChange={setNotificationsEnabled}
      />

      <Text>
        Status: {notificationsEnabled ? 'Enabled' : 'Disabled'}
      </Text>
    </View>
  );
}

The switch is controlled by state. Its value and displayed status update when the user toggles it.

9.2 Common Uses of Switch Events

  • Enabling notifications.
  • Turning dark mode on or off.
  • Enabling location-related features.
  • Changing privacy preferences.
  • Controlling optional application features.

10. Handling Selection Events

Selection events occur when a user chooses an item from a list, category menu, or other selection interface. React Native applications commonly manage selections with state and press handlers.

10.1 Selecting a Category

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

export default function CategorySelector() {
  const [selectedCategory, setSelectedCategory] = useState('All');

  const categories = ['All', 'Mobile', 'Web', 'Database'];

  return (
    <View style={{ padding: 20 }}>
      <Text>Selected: {selectedCategory}</Text>

      {categories.map((category) => (
        <Pressable
          key={category}
          onPress={() => setSelectedCategory(category)}
          style={{
            padding: 12,
            marginTop: 8,
            backgroundColor:
              selectedCategory === category ? '#D5E5FF' : '#EEEEEE'
          }}
        >
          <Text>{category}</Text>
        </Pressable>
      ))}
    </View>
  );
}

This example stores the selected category in state and highlights the active item. The same pattern can be used for filters, tabs, and selectable cards.

11. Understanding Event Objects

Some event callbacks provide an event object containing information about the interaction. For example, press events can provide details about the touch, while text input events can provide information about the native input event.

The event object is useful when an application needs more information than a simple callback value.

11.1 Accessing Press Event Information

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

export default function App() {
  const handlePress = (event) => {
    console.log('Press event:', event.nativeEvent);
  };

  return (
    <View style={{ padding: 20 }}>
      <Pressable onPress={handlePress}>
        <Text>Inspect Press Event</Text>
      </Pressable>
    </View>
  );
}

When the user presses the control, the handler receives a press event. Its nativeEvent can contain touch-related information. Available fields depend on the event type.

11.2 Difference Between onChange and onChangeText

Prop Value Received Typical Use
onChangeText The updated text string. Updating text state directly.
onChange An event object containing native event details. Accessing event metadata when needed.
onSubmitEditing A submission event object. Responding to the keyboard submit action.

For most controlled text inputs, onChangeText is the simpler option because the callback receives the new string directly.

12. Handling Long Press and Touch Interactions

A long press occurs when the user presses and holds a component for a specified period. It can be useful for contextual actions, selection menus, and additional options.

12.1 Long Press Example

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

export default function LongPressExample() {
  const [message, setMessage] = useState('Press and hold the card');

  return (
    <View style={{ padding: 20 }}>
      <Pressable
        onPress={() => setMessage('Card tapped')}
        onLongPress={() => setMessage('Long press detected')}
        delayLongPress={600}
        style={{
          padding: 24,
          backgroundColor: '#E2ECFF',
          borderRadius: 10
        }}
      >
        <Text>Touch This Card</Text>
      </Pressable>

      <Text>{message}</Text>
    </View>
  );
}

The onPress callback responds to a normal press, while onLongPress responds when the user holds the control long enough. The delay can be adjusted with delayLongPress.

12.2 Press Interaction Lifecycle

For a typical successful press, the interaction may follow this sequence:

  1. onPressIn runs when the press becomes active.
  2. onPressOut runs when the press interaction is released or deactivated.
  3. onPress runs after the normal press interaction completes.

For a sufficiently long hold, onLongPress can also run. Exact behavior depends on how the gesture develops, so applications should avoid assuming that every touch produces every callback.

Reference: React Native Pressable Documentation.

13. Form Handling and Validation

Forms collect information such as names, email addresses, passwords, phone numbers, and feedback. Event handling allows applications to capture input, validate the values, and respond to submission.

13.1 Example: Registration Form

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

export default function RegistrationForm() {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [error, setError] = useState('');
  const [success, setSuccess] = useState('');

  const handleSubmit = () => {
    setError('');
    setSuccess('');

    if (!name.trim() || !email.trim()) {
      setError('Please enter your name and email.');
      return;
    }

    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.trim())) {
      setError('Please enter a valid email address.');
      return;
    }

    setSuccess(`Registration details received for ${name.trim()}.`);
  };

  return (
    <View style={{ padding: 20, gap: 12 }}>
      <Text>Registration Form</Text>

      <TextInput
        placeholder="Enter your name"
        value={name}
        onChangeText={setName}
        autoCapitalize="words"
        style={{ borderWidth: 1, padding: 12 }}
      />

      <TextInput
        placeholder="Enter your email"
        value={email}
        onChangeText={setEmail}
        keyboardType="email-address"
        autoCapitalize="none"
        style={{ borderWidth: 1, padding: 12 }}
      />

      <Pressable
        onPress={handleSubmit}
        accessibilityRole="button"
        style={{ padding: 12, backgroundColor: '#2874D0' }}
      >
        <Text style={{ color: '#FFFFFF', textAlign: 'center' }}>
          Register
        </Text>
      </Pressable>

      {!!error && <Text style={{ color: 'red' }}>{error}</Text>}
      {!!success && <Text style={{ color: 'green' }}>{success}</Text>}
    </View>
  );
}

This example validates required fields and checks the basic email format before displaying a success message. It demonstrates client-side validation only; it does not send the registration details to a server or create an account.

13.2 Common Form Events

  • onChangeText captures the user's input.
  • onFocus can highlight the active field.
  • onBlur can trigger validation after the user leaves a field.
  • onSubmitEditing can submit a field using the keyboard.
  • onPress can submit the complete form.

14. Handling Loading States and Repeated Actions

Some interactions start asynchronous operations, such as sending a request to an API. A loading state helps communicate that the application is processing the action and can prevent repeated submissions.

14.1 Example: Async Button

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

export default function SubmitButton() {
  const [loading, setLoading] = useState(false);
  const [message, setMessage] = useState('');

  const handleSubmit = async () => {
    if (loading) return;

    setLoading(true);
    setMessage('');

    try {
      // Replace this with a real API request.
      await Promise.resolve();
      setMessage('Action completed successfully.');
    } catch (error) {
      setMessage('Something went wrong. Please try again.');
    } finally {
      setLoading(false);
    }
  };

  return (
    <View style={{ padding: 20, gap: 12 }}>
      <Pressable
        onPress={handleSubmit}
        disabled={loading}
        accessibilityRole="button"
        accessibilityState={{ disabled: loading, busy: loading }}
        style={{
          padding: 14,
          backgroundColor: loading ? '#888888' : '#2874D0'
        }}
      >
        {loading ? (
          <ActivityIndicator color="#FFFFFF" />
        ) : (
          <Text style={{ color: '#FFFFFF', textAlign: 'center' }}>
            Submit
          </Text>
        )}
      </Pressable>

      <Text>{message}</Text>
    </View>
  );
}

The example demonstrates a loading indicator, disabled button state, success feedback, and error handling. Replace the placeholder operation with a real asynchronous task in a production application.

For sensitive operations or requests where duplicate submissions must be prevented reliably, enforce appropriate safeguards in the data layer or server as well. A UI loading state alone is not a guarantee against duplicate requests.

15. Scroll Events and Pull-to-Refresh

React Native supports interaction events for scrollable content. Scroll events can be used to track position, reveal controls, or implement scroll-based behaviors.

15.1 Handling Scroll Events

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

export default function ScrollExample() {
  const [scrollY, setScrollY] = useState(0);

  return (
    <View style={{ flex: 1, padding: 16 }}>
      <Text>Scroll Position: {Math.round(scrollY)}</Text>

      <ScrollView
        onScroll={(event) => {
          setScrollY(event.nativeEvent.contentOffset.y);
        }}
        scrollEventThrottle={16}
      >
        {Array.from({ length: 25 }, (_, index) => (
          <Text
            key={index}
            style={{ padding: 18, marginVertical: 4, backgroundColor: '#EEEEEE' }}
          >
            List Item {index + 1}
          </Text>
        ))}
      </ScrollView>
    </View>
  );
}

The vertical scroll position is available through event.nativeEvent.contentOffset.y. Updating React state on every scroll event can cause frequent re-renders, so use this approach thoughtfully for more demanding interfaces.

15.2 Pull-to-Refresh Concept

A pull-to-refresh interaction lets users request updated data by pulling a list downward. React Native supports this through RefreshControl, which is commonly used with ScrollView and FlatList.

import React, { useState } from 'react';
import { ScrollView, RefreshControl, Text } from 'react-native';

export default function RefreshExample() {
  const [refreshing, setRefreshing] = useState(false);
  const [message, setMessage] = useState('Data is up to date');

  const onRefresh = async () => {
    setRefreshing(true);

    try {
      // Replace with an actual data refresh operation.
      await Promise.resolve();
      setMessage('Data refreshed successfully');
    } finally {
      setRefreshing(false);
    }
  };

  return (
    <ScrollView
      contentContainerStyle={{ padding: 24 }}
      refreshControl={
        <RefreshControl
          refreshing={refreshing}
          onRefresh={onRefresh}
        />
      }
    >
      <Text>{message}</Text>
    </ScrollView>
  );
}

This example demonstrates the refresh interaction. In production, the refresh function should fetch or reload the actual data and handle any errors appropriately.

16. Keyboard Handling and Focus Events

Mobile applications frequently use the keyboard to collect text. Focus events can help identify the active input, while keyboard configuration can make data entry easier.

16.1 Handling Focus and Blur

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

export default function FocusExample() {
  const [focused, setFocused] = useState(false);
  const [email, setEmail] = useState('');

  return (
    <View style={{ padding: 20 }}>
      <TextInput
        value={email}
        onChangeText={setEmail}
        onFocus={() => setFocused(true)}
        onBlur={() => setFocused(false)}
        placeholder="Enter email"
        keyboardType="email-address"
        autoCapitalize="none"
        style={{
          padding: 12,
          borderWidth: 1,
          borderColor: focused ? '#2874D0' : '#999999',
          borderRadius: 8
        }}
      />

      <Text>{focused ? 'Input is active' : 'Input is inactive'}</Text>
    </View>
  );
}

Focus events are useful for styling active inputs and performing field-level validation. Avoid relying exclusively on focus or blur for critical validation, because users can submit forms through different interaction paths.

17. Accessibility in Interactive Components

Accessible interaction design helps people use applications with different abilities and assistive technologies. Interactive elements should communicate their role, label, and state clearly.

17.1 Accessibility Best Practices

  • Use accessibilityRole="button" for custom button-like controls.
  • Provide meaningful labels for icons and controls.
  • Use accessibilityState to describe states such as disabled, selected, or busy where appropriate.
  • Make interactive targets large enough to tap comfortably.
  • Do not rely only on color to communicate errors or selection.
  • Provide text feedback when actions succeed or fail.
  • Ensure disabled controls cannot trigger unintended actions.
  • Test with screen readers and the relevant Android and iOS accessibility features.

17.2 Accessible Pressable Example

<Pressable
  onPress={handleSave}
  disabled={saving}
  accessibilityRole="button"
  accessibilityLabel="Save profile"
  accessibilityHint="Saves your profile changes"
  accessibilityState={{
    disabled: saving,
    busy: saving
  }}
>
  <Text>{saving ? 'Saving...' : 'Save Profile'}</Text>
</Pressable>

Accessibility props should describe the actual behavior and state of the component. If a control is not busy, for example, its busy state should not be reported as true.

18. Common Event-Handling Mistakes

Mistake Problem Solution
Calling the handler during rendering The action runs before the user interacts. Pass a function reference or callback.
Updating a normal variable The interface may not re-render. Use React state when a value affects the UI.
Ignoring input validation Invalid or incomplete values may be accepted. Validate input before processing it.
Not showing feedback Users may not know whether the action worked. Display loading, success, or error states.
Allowing repeated submissions Duplicate operations may occur. Disable controls while processing and apply appropriate data-layer safeguards.
Mutating state directly React may not detect the intended change. Use state setters and immutable updates.
Using overly complex inline callbacks Rendering logic becomes harder to read. Extract named handler functions when appropriate.
Ignoring accessibility Controls may be difficult to understand or operate. Provide roles, labels, states, and suitable touch targets.
Failing to handle asynchronous errors The application may leave users without feedback. Use appropriate error handling and restore loading state.

19. Event Handling Best Practices

  • Use the correct event prop for each interaction.
  • Keep event handlers small and focused.
  • Use descriptive handler names such as handleSubmit and handleSearch.
  • Use state setters to update values that affect the UI.
  • Use functional state updates when the next value depends on the previous value.
  • Validate user input before processing or sending it.
  • Show users clear confirmation, loading, and error feedback.
  • Prevent inappropriate actions while a request is processing.
  • Use accessibility labels, roles, and state information.
  • Test normal interactions, rapid repeated taps, invalid input, and failure scenarios.
  • Avoid expensive work inside frequently triggered event handlers.
  • Use appropriate list components and avoid unnecessary rendering for large data sets.
  • Keep server-side validation and authorization in place for security-sensitive operations.

20. Practical Project: Interactive To-Do List

Objective: Create an interactive React Native to-do list that demonstrates text input, press events, state updates, conditional rendering, and list item interactions.

Project Requirements

  • Allow users to enter a task.
  • Add a task when the user presses the Add button.
  • Display all tasks in a list.
  • Allow users to mark tasks as completed.
  • Allow users to remove tasks.
  • Display a helpful message when the list is empty.
  • Prevent blank tasks from being added.

Complete Code

import React, { useState } from 'react';
import {
  View,
  Text,
  TextInput,
  Pressable,
  ScrollView,
  StyleSheet
} from 'react-native';

export default function TodoApp() {
  const [taskText, setTaskText] = useState('');
  const [tasks, setTasks] = useState([]);
  const [error, setError] = useState('');

  const addTask = () => {
    const title = taskText.trim();

    if (!title) {
      setError('Please enter a task.');
      return;
    }

    const newTask = {
      id: `${Date.now()}-${Math.random()}`,
      title,
      completed: false
    };

    setTasks(previousTasks => [...previousTasks, newTask]);
    setTaskText('');
    setError('');
  };

  const toggleTask = (taskId) => {
    setTasks(previousTasks =>
      previousTasks.map(task =>
        task.id === taskId
          ? { ...task, completed: !task.completed }
          : task
      )
    );
  };

  const removeTask = (taskId) => {
    setTasks(previousTasks =>
      previousTasks.filter(task => task.id !== taskId)
    );
  };

  return (
    <View style={styles.container}>
      <Text style={styles.heading}>My To-Do List</Text>

      <View style={styles.inputRow}>
        <TextInput
          style={styles.input}
          placeholder="Enter a task"
          value={taskText}
          onChangeText={setTaskText}
          onSubmitEditing={addTask}
          returnKeyType="done"
        />

        <Pressable
          onPress={addTask}
          accessibilityRole="button"
          style={styles.addButton}
        >
          <Text style={styles.buttonText}>Add</Text>
        </Pressable>
      </View>

      {!!error && (
        <Text style={styles.error}>{error}</Text>
      )}

      {tasks.length === 0 ? (
        <Text style={styles.empty}>No tasks yet. Add your first task.</Text>
      ) : (
        <ScrollView>
          {tasks.map(task => (
            <View key={task.id} style={styles.taskRow}>
              <Pressable
                onPress={() => toggleTask(task.id)}
                accessibilityRole="checkbox"
                accessibilityState={{ checked: task.completed }}
                style={styles.taskTextContainer}
              >
                <Text
                  style={{
                    textDecorationLine: task.completed
                      ? 'line-through'
                      : 'none'
                  }}
                >
                  {task.title}
                </Text>
              </Pressable>

              <Pressable
                onPress={() => removeTask(task.id)}
                accessibilityRole="button"
                accessibilityLabel={`Delete ${task.title}`}
              >
                <Text style={styles.deleteText}>Delete</Text>
              </Pressable>
            </View>
          ))}
        </ScrollView>
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
    paddingTop: 40,
    backgroundColor: '#FFFFFF'
  },
  heading: {
    fontSize: 26,
    fontWeight: 'bold',
    marginBottom: 18
  },
  inputRow: {
    flexDirection: 'row',
    gap: 8,
    marginBottom: 10
  },
  input: {
    flex: 1,
    borderWidth: 1,
    borderColor: '#BBBBBB',
    borderRadius: 8,
    padding: 12
  },
  addButton: {
    justifyContent: 'center',
    paddingHorizontal: 18,
    borderRadius: 8,
    backgroundColor: '#246BCE'
  },
  buttonText: {
    color: '#FFFFFF',
    fontWeight: 'bold'
  },
  error: {
    color: '#C62828',
    marginBottom: 10
  },
  empty: {
    color: '#666666',
    marginTop: 20
  },
  taskRow: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
    padding: 14,
    marginBottom: 8,
    borderWidth: 1,
    borderColor: '#DDDDDD',
    borderRadius: 8
  },
  taskTextContainer: {
    flex: 1,
    paddingRight: 12
  },
  deleteText: {
    color: '#C62828',
    fontWeight: 'bold'
  }
});

Project Explanation

  1. Text input: The TextInput captures the task description.
  2. Add event: The Add button and keyboard submission invoke addTask.
  3. Validation: Empty or whitespace-only task names are rejected.
  4. State management: The tasks array stores task information.
  5. List rendering: The map() method displays each task with a stable key.
  6. Completion event: Pressing a task toggles its completed status.
  7. Delete event: The Delete button removes the selected task.
  8. Conditional rendering: The application displays an empty-state message when no tasks exist.
  9. Immutable updates: State arrays and task objects are updated without modifying the existing values directly.

21. Practice Exercises

Exercise 1: Click Counter

Create a counter with Increase, Decrease, and Reset buttons. Display the updated value after every interaction.

Exercise 2: Live Name Preview

Create a text input that displays a personalized greeting while the user types their name.

Exercise 3: Password Visibility

Build a password field with a control that switches between hidden and visible text using a boolean state variable.

Exercise 4: Theme Switch

Create a switch that changes the background and text colors between light and dark themes.

Exercise 5: Form Validation

Create a registration form with name, email, and password fields. Display validation messages and a success message after valid submission.

Exercise 6: Long-Press Card

Create a card that displays one message on a normal press and a different message on a long press.

Exercise 7: Searchable List

Create a search input that filters a list of courses based on the text entered by the user.

Exercise 8: Async Submission

Create a submit button that displays a loading indicator, prevents repeated presses while processing, and displays success or error feedback.

22. Interview Questions and Answers

Q1. What is event handling in React Native?

Answer: Event handling is the process of responding to interactions such as presses, text changes, selections, and keyboard submissions by executing JavaScript functions.

Q2. What is an event handler?

Answer: An event handler is a function that runs in response to a particular event.

Q3. What is the purpose of Pressable?

Answer: Pressable detects press interactions and supports callbacks such as onPress, onLongPress, onPressIn, and onPressOut.

Q4. What is the difference between onPress and onLongPress?

Answer: onPress responds to a completed normal press, while onLongPress responds when the user holds the press long enough.

Q5. How do you pass arguments to an event handler?

Answer: Use an arrow function, such as onPress={() => handleSelect('React Native')}.

Q6. Why is useState useful for event handling?

Answer: It stores changing values and allows React to update the interface when the state setter is called.

Q7. What is the purpose of onChangeText?

Answer: It receives the new text string whenever a TextInput value changes.

Q8. What is the difference between onChangeText and onSubmitEditing?

Answer: onChangeText runs as the text changes, while onSubmitEditing responds when the keyboard's submit action is used.

Q9. How do you prevent a button from being pressed during loading?

Answer: Use state to track the loading status and pass that value to the button's disabled prop. For critical operations, also protect against duplicates in the relevant data or server layer.

Q10. How do you validate form inputs?

Answer: Read input values from state, check required fields and expected formats, then display validation messages before processing the form.

Q11. What are focus and blur events?

Answer: onFocus runs when an input receives focus, while onBlur runs when it loses focus.

Q12. How do you handle asynchronous events?

Answer: Use asynchronous functions with async and await, manage loading state, handle errors, and restore the appropriate UI state after completion.

Q13. Why is accessibility important for interactive controls?

Answer: Accessibility helps people using assistive technologies understand and operate controls. Roles, labels, state descriptions, and suitable touch targets improve usability.

Q14. What is a controlled TextInput?

Answer: A controlled input receives its current value from React state through the value prop and updates that state through an event callback such as onChangeText.

Q15. How can you avoid unnecessary rendering during scroll events?

Answer: Avoid updating React state for every scroll event unless necessary, optimize expensive calculations, and consider animation or performance tools designed for high-frequency interactions.

23. Quick Revision Checklist

  • Events let applications respond to user interactions.
  • Use Pressable for flexible touch interactions.
  • Use onPress for normal press actions.
  • Use onLongPress for press-and-hold interactions.
  • Use TextInput and onChangeText to capture text.
  • Use Switch and onValueChange for toggle settings.
  • Use useState when interactions need to update the interface.
  • Use callbacks to pass arguments to handlers.
  • Use event objects when native event information is required.
  • Validate user input before processing it.
  • Display loading, success, and error states.
  • Use disabled states and appropriate safeguards to manage repeated actions.
  • Handle asynchronous errors and restore loading state.
  • Follow accessibility practices for all interactive controls.
  • Test interactions on the relevant Android and iOS devices.

24. Conclusion

Handling events and user interactions is a fundamental skill for developing responsive React Native applications. Event props connect interactive components to JavaScript functions, while state management allows the interface to respond to user actions.

By practising button presses, text input, switches, selections, touch events, form validation, scroll interactions, and asynchronous operations, developers can create mobile interfaces that are interactive, accessible, reliable, and easier to maintain.

For further learning, explore the React Native Training Course, visit the JustAcademy website, or register for a course demo.

Additional learning resources: Handling Touches, Handling Text Input, and Pressable API.

whatsapp