Popular Searches
Popular Course Categories
Popular Courses

Controlled Components and Form State

Controlled Components and Form State

Forms, Validation, and User Input Handling

Controlled Components and Form State in React Native

Course: React Native Training

Topic: Controlled Components and Form State

Course Link: React Native Training at JustAcademy

Demo Registration: Register for a Course Demo

JustAcademy Website: Visit JustAcademy


1. Introduction to Controlled Components

Controlled components are React components whose important input values are managed by React state. In React Native, a common example is a TextInput component whose value prop is connected to a state variable and whose onChangeText callback updates that state.

This approach gives developers control over the data entered by users. The application can read the current value, validate it, display feedback, enable or disable buttons, reset the field, and submit the data to an API.

For example, in a registration form, the user's name can be stored in a state variable called name. Every time the user types, the state is updated. React then renders the current state as the value displayed inside the input.

1.1 What Is a Controlled Component?

A controlled component receives its current value from React state. The user interacts with the input, an event handler receives the change, and the state is updated to reflect the new value.

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

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

  return (
    <View style={{ padding: 20 }}>
      <Text>Enter your name</Text>
      <TextInput
        value={name}
        onChangeText={setName}
        placeholder="Your name"
        style={{
          borderWidth: 1,
          borderColor: '#999',
          padding: 12,
          marginTop: 8,
        }}
      />
      <Text style={{ marginTop: 12 }}>Name: {name}</Text>
    </View>
  );
}

Explanation:

  • useState('') creates the initial state for the name.
  • value={name} makes the input display the value stored in state.
  • onChangeText={setName} updates the state when the text changes.
  • The text below the input displays the current value of name.

React Native's TextInput supports the value and onChangeText properties for this pattern. Reference: Handling Text Input in React Native.

2. Understanding Form State

Form state is the collection of data that represents the current condition of a form. It can include field values, validation errors, whether a field has been touched, whether a request is loading, and whether the form has been submitted successfully.

For example, a login form might track the email address, password, validation messages, and loading status while an authentication request is in progress.

2.1 Common Types of Form State

State Type Purpose Example
Field values Stores user-entered information. email, password
Validation errors Stores messages for invalid fields. errors.email
Touched fields Tracks fields the user has interacted with or left. touched.email
Loading status Indicates that a form request is being processed. isSubmitting
Submission result Stores a status or message after submission. successMessage
Form-level status Stores errors or information that applies to the whole form. formError

2.2 Why Form State Is Important

  • Keeps user input available to the component.
  • Allows immediate or on-submit validation.
  • Displays clear feedback when input is invalid.
  • Supports resetting or pre-filling form values.
  • Prevents accidental duplicate submissions.
  • Makes it easier to manage API requests and submission results.
  • Helps keep the UI synchronized with the application's data.

3. The Relationship Between State and TextInput

A controlled input follows a simple cycle: the component displays the state value, the user edits the text, the change handler receives the new text, and the state is updated. React then renders the latest value.

const [email, setEmail] = useState('');

<TextInput
  value={email}
  onChangeText={(newEmail) => setEmail(newEmail)}
/>

In this example, email represents the current value, and setEmail updates it. The input is controlled because its displayed value comes from React state.

3.1 What Happens When the User Types?

  1. The user types a character into the input.
  2. React Native calls the onChangeText callback with the updated text.
  3. The callback calls setEmail with the new value.
  4. React schedules a state update and renders the component again as needed.
  5. The value prop receives the updated state.

3.2 Important Rule

When using a controlled TextInput, update the state in response to onChangeText. If the value is supplied but the state is never updated, the field may appear unable to accept changes because React continues supplying the same value.

4. Using useState for a Single Form Field

The useState hook is the simplest way to manage form data in a functional component. It returns the current state value and a function that updates it.

4.1 Syntax

const [value, setValue] = useState(initialValue);
  • value is the current state.
  • setValue updates the state.
  • initialValue is the value used when the component initializes.

4.2 Example: Username Input

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

export default function UsernameForm() {
  const [username, setUsername] = useState('');

  return (
    <View style={styles.container}>
      <Text style={styles.label}>Username</Text>
      <TextInput
        style={styles.input}
        value={username}
        onChangeText={setUsername}
        placeholder="Enter username"
        autoCapitalize="none"
        autoCorrect={false}
      />
      <Text>Current username: {username || 'Not entered'}</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    padding: 20,
    gap: 10,
  },
  label: {
    fontSize: 16,
    fontWeight: '600',
  },
  input: {
    borderWidth: 1,
    borderColor: '#B8C0CC',
    borderRadius: 8,
    padding: 12,
    fontSize: 16,
  },
});

Explanation: The state starts as an empty string. As the user types, the input updates the state. The text beneath the input displays the current username or a fallback message.

5. Managing Multiple Form Fields

Most forms contain more than one input. For example, a registration form may contain a full name, email address, phone number, and password. There are two common approaches to storing this data: separate state variables or one object containing all field values.

5.1 Approach One: Separate State Variables

const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [phone, setPhone] = useState('');

Each field has its own state variable and setter function. This approach is easy to understand and can work well for small forms.

5.2 Complete Example with Separate State

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

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

  return (
    <View style={{ padding: 20, gap: 12 }}>
      <Text>Name</Text>
      <TextInput
        value={name}
        onChangeText={setName}
        placeholder="Enter name"
        style={{ borderWidth: 1, padding: 12 }}
      />
      <Text>Email</Text>
      <TextInput
        value={email}
        onChangeText={setEmail}
        placeholder="Enter email"
        keyboardType="email-address"
        autoCapitalize="none"
        style={{ borderWidth: 1, padding: 12 }}
      />
      <Text>Name: {name}</Text>
      <Text>Email: {email}</Text>
    </View>
  );
}

5.3 Approach Two: Store Fields in an Object

When a form has several related fields, storing the values in one object can make the code easier to organize. Each property represents one field.

const [form, setForm] = useState({
  name: '',
  email: '',
  phone: '',
});

To update one property, preserve the other properties using the spread operator and a functional state update.

const updateField = (field, value) => {
  setForm(previousForm => ({
    ...previousForm,
    [field]: value,
  }));
};

Explanation:

  • previousForm represents the previous form object.
  • ... copies the existing properties into a new object.
  • [field]: value updates the property whose name is stored in field.
  • Other fields remain unchanged.

5.4 Complete Example with Object State

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

export default function ProfileForm() {
  const [form, setForm] = useState({
    name: '',
    email: '',
    phone: '',
  });

  const updateField = (field, value) => {
    setForm(previousForm => ({
      ...previousForm,
      [field]: value,
    }));
  };

  return (
    <View style={styles.container}>
      <Text style={styles.label}>Full Name</Text>
      <TextInput
        value={form.name}
        onChangeText={value => updateField('name', value)}
        placeholder="Enter full name"
        style={styles.input}
      />

      <Text style={styles.label}>Email Address</Text>
      <TextInput
        value={form.email}
        onChangeText={value => updateField('email', value)}
        placeholder="Enter email"
        keyboardType="email-address"
        autoCapitalize="none"
        style={styles.input}
      />

      <Text style={styles.label}>Phone Number</Text>
      <TextInput
        value={form.phone}
        onChangeText={value => updateField('phone', value)}
        placeholder="Enter phone number"
        keyboardType="phone-pad"
        style={styles.input}
      />

      <Text>Name: {form.name}</Text>
      <Text>Email: {form.email}</Text>
      <Text>Phone: {form.phone}</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    padding: 20,
    gap: 10,
  },
  label: {
    fontWeight: '600',
  },
  input: {
    borderWidth: 1,
    borderColor: '#B8C0CC',
    borderRadius: 8,
    padding: 12,
  },
});

6. Separate State Variables vs. Object State

Feature Separate State Variables Object State
Structure One state variable per field. Multiple fields stored in one object.
Readability Very clear for a small form. Keeps related fields grouped together.
Updating a field Call the corresponding setter. Copy the old object and update one property.
Adding fields Requires another state declaration. Requires another object property and input.
Potential mistake Many separate variables can become repetitive. Forgetting to preserve previous properties can erase other values.
Best suited for Small forms and independent values. Forms with several related fields.

Neither approach is universally better. Choose the approach that keeps your form clear and easy to maintain.

7. Controlled Components vs. Uncontrolled Inputs

Controlled and uncontrolled inputs differ in how their current values are managed. A controlled input receives its displayed value from React state. An uncontrolled input generally manages its own current text internally, with an initial value supplied through defaultValue when needed.

Controlled Input Uncontrolled Input
Uses a state variable through the value prop. May use defaultValue for its initial value.
Updates state using onChangeText. Does not require state to mirror every text change.
Useful for live validation and conditional UI. Useful when continuous state synchronization is unnecessary.
Easy to reset by updating state. Resetting may require a different approach depending on the component setup.
Provides direct access to current values through state. Current values are not automatically available in a React state variable.

7.1 Controlled Input Example

const [email, setEmail] = useState('');

<TextInput
  value={email}
  onChangeText={setEmail}
/>

7.2 Uncontrolled-Style Input Example

<TextInput
  defaultValue="[email protected]"
  placeholder="Enter email"
/>

Note: defaultValue supplies an initial value rather than continuously synchronizing the displayed text with React state. For forms that require reliable live validation, controlled inputs are often the more straightforward choice. React Native documents both value and defaultValue in its TextInput reference.

8. Handling Input Events

React Native's TextInput supports several events that help developers respond to user interactions. Controlled components commonly use onChangeText, while other events can support focus handling and form submission.

Event or Property Purpose
onChangeText Receives the updated text as a string.
onFocus Runs when the input receives focus.
onBlur Runs when the input loses focus.
onSubmitEditing Runs when the keyboard's submit action is triggered, subject to platform and input behavior.
onEndEditing Runs when editing ends.
editable Controls whether the user can edit the input.

8.1 Example: Updating and Clearing an Error

const [email, setEmail] = useState('');
const [emailError, setEmailError] = useState('');

const handleEmailChange = (value) => {
  setEmail(value);
  setEmailError('');
};

<TextInput
  value={email}
  onChangeText={handleEmailChange}
  onBlur={() => {
    if (!email.trim()) {
      setEmailError('Email is required.');
    }
  }}
/>

This example updates the email state whenever the user types and clears the old error while the user edits the field. The blur handler checks whether the field is empty when the user leaves it.

9. Form Validation with Controlled Components

Controlled components make validation easier because the latest field values are available in state. Validation can run while the user types, when a field loses focus, or when the user submits the form.

9.1 Common Validation Rules

  • Required fields must not be empty.
  • Email addresses should follow the application's accepted format.
  • Passwords must satisfy the required password policy.
  • Phone numbers should be checked against the application's supported formats.
  • Text fields may need minimum or maximum lengths.
  • Numeric values should be checked for valid ranges and expected formats.

9.2 Basic Validation Function

const validateForm = (form) => {
  const errors = {};

  if (!form.name.trim()) {
    errors.name = 'Name is required.';
  }

  if (!form.email.trim()) {
    errors.email = 'Email is required.';
  } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(form.email.trim())) {
    errors.email = 'Enter a valid email address.';
  }

  if (!form.password) {
    errors.password = 'Password is required.';
  } else if (form.password.length < 8) {
    errors.password = 'Password must contain at least 8 characters.';
  }

  return errors;
};

The function returns an object containing error messages. If the object is empty, none of these basic checks failed. The email pattern is a simple format check and does not confirm that the address exists.

9.3 Storing Validation Errors

const [errors, setErrors] = useState({});

const handleSubmit = () => {
  const validationErrors = validateForm(form);
  setErrors(validationErrors);

  if (Object.keys(validationErrors).length === 0) {
    // Continue with submission.
  }
};

The error object can be used to display a message beside each invalid input. Keeping field values and errors in separate state objects helps distinguish user data from validation feedback.

10. Tracking Touched Fields

A touched field is an input that the user has interacted with, often defined as a field that has lost focus at least once. Tracking touched fields helps avoid showing every validation error immediately when a blank form first appears.

10.1 Example of Touched State

const [form, setForm] = useState({
  email: '',
});

const [touched, setTouched] = useState({
  email: false,
});

const [errors, setErrors] = useState({
  email: '',
});

const handleEmailBlur = () => {
  setTouched(previous => ({
    ...previous,
    email: true,
  }));

  if (!form.email.trim()) {
    setErrors(previous => ({
      ...previous,
      email: 'Email is required.',
    }));
  }
};

In the interface, an error can be displayed only when the field has been touched or the user has attempted to submit the form. This makes the form less distracting while preserving helpful validation feedback.

11. Managing Form Submission State

Form state can also track whether a submission is in progress. This is useful when sending data to a backend because network requests may take time or fail.

11.1 Important Submission States

State Purpose
isSubmitting Indicates that a submission request is in progress.
successMessage Displays a successful submission result.
formError Displays a general submission or server error.
errors Stores field-level validation messages.

11.2 Example of a Loading State

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

export default function SubmitExample() {
  const [email, setEmail] = useState('');
  const [isSubmitting, setIsSubmitting] = useState(false);

  const handleSubmit = async () => {
    if (!email.trim()) {
      return;
    }

    setIsSubmitting(true);

    try {
      // Replace this block with your actual API request.
      await new Promise(resolve => setTimeout(resolve, 1000));
    } finally {
      setIsSubmitting(false);
    }
  };

  return (
    <View style={{ padding: 20, gap: 12 }}>
      <Text>Email</Text>
      <TextInput
        value={email}
        onChangeText={setEmail}
        placeholder="Enter email"
        keyboardType="email-address"
        autoCapitalize="none"
        editable={!isSubmitting}
        style={{ borderWidth: 1, padding: 12 }}
      />

      <Pressable
        onPress={handleSubmit}
        disabled={isSubmitting}
        style={{
          padding: 14,
          backgroundColor: isSubmitting ? '#8AA3D6' : '#2457C5',
          alignItems: 'center',
          borderRadius: 8,
        }}
      >
        {isSubmitting ? (
          <ActivityIndicator color="#FFFFFF" />
        ) : (
          <Text style={{ color: '#FFFFFF', fontWeight: '600' }}>
            Submit
          </Text>
        )}
      </Pressable>
    </View>
  );
}

Explanation: The loading state changes when submission starts and ends. The input and button are disabled during the simulated request. In a real application, replace the delay with an API call and handle request failures. A production form should also validate all required fields before submitting.

12. Resetting Controlled Form State

Because controlled input values come from React state, resetting a form can be done by restoring the state to its initial values. Resetting error state and touched state may also be necessary.

12.1 Reset Example

const initialForm = {
  name: '',
  email: '',
  password: '',
};

const [form, setForm] = useState(initialForm);
const [errors, setErrors] = useState({});
const [touched, setTouched] = useState({});

const resetForm = () => {
  setForm(initialForm);
  setErrors({});
  setTouched({});
};

When resetForm runs, the controlled inputs display the initial values again because their value props are connected to the form state.

13. Prefilling a Form with Existing Data

Controlled components are useful when a user edits existing information, such as a profile or saved address. The input values can be initialized with existing data and updated as the user makes changes.

const [form, setForm] = useState({
  name: 'Aarav Sharma',
  email: '[email protected]',
});

const updateField = (field, value) => {
  setForm(previous => ({
    ...previous,
    [field]: value,
  }));
};

For data that arrives asynchronously from an API, update the form state when the data has been loaded and the component is ready to display it. Avoid repeatedly overwriting user edits with old server data during rendering.

14. Common Mistakes with Controlled Components

Mistake Why It Causes Problems Correct Approach
Using value without updating state. The input may appear unable to change. Update the state in onChangeText.
Replacing the entire object when updating one field. Other field values can be lost. Preserve previous properties with the spread operator.
Mutating state directly. React may not detect the intended state change. Create a new object or array and update state through its setter.
Using stale values in a state update. Updates may be based on outdated state. Use a functional updater when the next state depends on the previous state.
Mixing field values and error messages in one unclear object. Form logic becomes harder to understand. Keep values, errors, and touched status clearly organized.
Validating only in the interface. Invalid data can still be sent directly to the server. Validate on the client for usability and on the server for security and correctness.
Clearing values before a request succeeds. The user may lose entered data after a failure. Reset the form only after a successful operation or an intentional reset.
Displaying sensitive values in debug output. Passwords or personal information may be exposed. Avoid logging secrets and sensitive form contents.

15. Best Practices for Form State Management

  1. Keep state organized: Use meaningful names for values, errors, touched fields, and submission status.
  2. Use controlled inputs intentionally: Connect value and onChangeText when the application needs to manage the current value.
  3. Preserve object properties: When updating one field in an object, keep the remaining fields intact.
  4. Use functional updates where appropriate: If the next state depends on the previous state, use the setter callback form.
  5. Keep validation understandable: Separate validation rules from the rendering code when the form becomes complex.
  6. Show errors at the right time: Use submission attempts or touched status to avoid overwhelming users.
  7. Prevent duplicate submissions: Track the loading state and disable submission controls while a request is running.
  8. Handle API failures: Keep the form values available when a request fails so the user can retry.
  9. Respect accessibility: Provide visible labels, readable error messages, and suitable touch targets.
  10. Protect sensitive information: Never log passwords or authentication tokens, and use secure communication with the backend.
  11. Choose the simplest suitable approach: Basic forms may only need useState; complex forms may benefit from dedicated libraries.

16. Practical Project: Controlled Registration Form

Project Objective

Create a registration form that manages multiple field values, validates the user's entries, displays errors, tracks submission state, and resets the form after a successful simulated submission.

16.1 Complete Project Code

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

const initialForm = {
  name: '',
  email: '',
  password: '',
};

export default function ControlledRegistrationForm() {
  const [form, setForm] = useState(initialForm);
  const [errors, setErrors] = useState({});
  const [touched, setTouched] = useState({});
  const [isSubmitting, setIsSubmitting] = useState(false);

  const updateField = (field, value) => {
    setForm(previous => ({
      ...previous,
      [field]: value,
    }));

    setErrors(previous => ({
      ...previous,
      [field]: '',
    }));
  };

  const validateForm = () => {
    const nextErrors = {};

    if (!form.name.trim()) {
      nextErrors.name = 'Name is required.';
    }

    const email = form.email.trim();

    if (!email) {
      nextErrors.email = 'Email is required.';
    } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
      nextErrors.email = 'Enter a valid email address.';
    }

    if (!form.password) {
      nextErrors.password = 'Password is required.';
    } else if (form.password.length < 8) {
      nextErrors.password = 'Use at least 8 characters.';
    }

    setErrors(nextErrors);
    setTouched({
      name: true,
      email: true,
      password: true,
    });

    return Object.keys(nextErrors).length === 0;
  };

  const handleSubmit = async () => {
    if (isSubmitting || !validateForm()) {
      return;
    }

    setIsSubmitting(true);

    try {
      // Replace this simulated operation with a secure API request.
      await new Promise(resolve => setTimeout(resolve, 800));

      Alert.alert('Success', 'Basic form validation passed.');
      setForm(initialForm);
      setErrors({});
      setTouched({});
    } catch (error) {
      Alert.alert('Error', 'Unable to submit the form. Please try again.');
    } finally {
      setIsSubmitting(false);
    }
  };

  const showError = field => touched[field] && errors[field];

  return (
    <ScrollView
      contentContainerStyle={styles.container}
      keyboardShouldPersistTaps="handled"
    >
      <Text style={styles.heading}>Create Account</Text>
      <Text style={styles.description}>
        Enter your details below.
      </Text>

      <Text style={styles.label}>Full Name</Text>
      <TextInput
        value={form.name}
        onChangeText={value => updateField('name', value)}
        onBlur={() => setTouched(previous => ({
          ...previous,
          name: true,
        }))}
        placeholder="Enter your full name"
        autoCapitalize="words"
        editable={!isSubmitting}
        style={[styles.input, showError('name') && styles.inputError]}
      />
      {showError('name') ? (
        <Text style={styles.error}>{errors.name}</Text>
      ) : null}

      <Text style={styles.label}>Email Address</Text>
      <TextInput
        value={form.email}
        onChangeText={value => updateField('email', value)}
        onBlur={() => setTouched(previous => ({
          ...previous,
          email: true,
        }))}
        placeholder="[email protected]"
        keyboardType="email-address"
        autoCapitalize="none"
        autoCorrect={false}
        editable={!isSubmitting}
        style={[styles.input, showError('email') && styles.inputError]}
      />
      {showError('email') ? (
        <Text style={styles.error}>{errors.email}</Text>
      ) : null}

      <Text style={styles.label}>Password</Text>
      <TextInput
        value={form.password}
        onChangeText={value => updateField('password', value)}
        onBlur={() => setTouched(previous => ({
          ...previous,
          password: true,
        }))}
        placeholder="At least 8 characters"
        secureTextEntry={true}
        autoCapitalize="none"
        autoCorrect={false}
        editable={!isSubmitting}
        style={[styles.input, showError('password') && styles.inputError]}
      />
      {showError('password') ? (
        <Text style={styles.error}>{errors.password}</Text>
      ) : null}

      <Pressable
        onPress={handleSubmit}
        disabled={isSubmitting}
        accessibilityRole="button"
        style={[styles.button, isSubmitting && styles.buttonDisabled]}
      >
        {isSubmitting ? (
          <ActivityIndicator color="#FFFFFF" />
        ) : (
          <Text style={styles.buttonText}>Register</Text>
        )}
      </Pressable>
    </ScrollView>
  );
}

const styles = StyleSheet.create({
  container: {
    flexGrow: 1,
    padding: 20,
    justifyContent: 'center',
    backgroundColor: '#F6F8FB',
  },
  heading: {
    fontSize: 26,
    fontWeight: '700',
    color: '#17243A',
    marginBottom: 8,
  },
  description: {
    fontSize: 15,
    lineHeight: 22,
    color: '#596579',
    marginBottom: 20,
  },
  label: {
    fontSize: 15,
    fontWeight: '600',
    marginTop: 12,
    marginBottom: 6,
  },
  input: {
    minHeight: 48,
    borderWidth: 1,
    borderColor: '#B8C0CC',
    borderRadius: 8,
    paddingHorizontal: 12,
    paddingVertical: 10,
    backgroundColor: '#FFFFFF',
    fontSize: 16,
  },
  inputError: {
    borderColor: '#C62828',
  },
  error: {
    color: '#C62828',
    fontSize: 13,
    marginTop: 5,
  },
  button: {
    minHeight: 48,
    marginTop: 24,
    padding: 12,
    borderRadius: 8,
    backgroundColor: '#2457C5',
    alignItems: 'center',
    justifyContent: 'center',
  },
  buttonDisabled: {
    opacity: 0.65,
  },
  buttonText: {
    color: '#FFFFFF',
    fontSize: 16,
    fontWeight: '700',
  },
});

16.2 How the Project Works

  • Initial values: The initialForm object defines the default values for each field.
  • Controlled inputs: Each TextInput reads its value from the form object.
  • State updates: The updateField function changes one property without removing other values.
  • Validation: The validateForm function checks the fields and stores error messages.
  • Touched status: The touched object records interaction with each field.
  • Submission status: The isSubmitting variable disables inputs and the button while the simulated operation runs.
  • Reset: After the simulated success, the form values and validation state are restored to their initial state.

This project demonstrates client-side form management. The simulated delay is not a real registration request. In a production app, replace it with a real API call, handle server validation and network errors, and use a secure authentication flow. Never send a password to an untrusted endpoint or log it for debugging.

17. Exercises and Assignments

  1. Create a controlled input for a username using useState.
  2. Build a form with name, email, and phone number using separate state variables.
  3. Refactor the same form to use a single object for its values.
  4. Create an updateField function that preserves other object properties.
  5. Build a login form with controlled email and password inputs.
  6. Add validation errors and display them beneath the corresponding fields.
  7. Track whether each field has been touched.
  8. Add a reset button that clears the values and errors.
  9. Disable the submit button while a simulated request is in progress.
  10. Prefill a profile form with existing user details and allow the user to edit them.
  11. Test the form with empty values, invalid email formats, and long text.
  12. Explain when separate state variables are preferable to object state.

18. Interview Questions and Answers

Q1. What is a controlled component in React Native?

Answer: A controlled component is an input whose current value is managed by React state and supplied through a property such as value.

Q2. What is form state?

Answer: Form state is the data that describes a form's current values and status. It can include input values, validation errors, touched fields, loading status, and submission results.

Q3. How do you control a TextInput?

Answer: Connect its value property to a state variable and use onChangeText to update that state.

Q4. What is the role of useState?

Answer: The useState hook lets a functional component store and update changing data such as form values and errors.

Q5. What is the difference between value and onChangeText?

Answer: The value property supplies the displayed text, while onChangeText receives updated text when the user types.

Q6. Why use object state for forms?

Answer: Object state groups related field values together and can simplify management of forms with several fields.

Q7. Why should the spread operator be used when updating object state?

Answer: It copies existing properties into a new object so that updating one field does not accidentally remove the other field values.

Q8. What is the difference between controlled and uncontrolled inputs?

Answer: A controlled input gets its current value from React state. An uncontrolled-style input manages its current text internally, with an optional initial value supplied through defaultValue.

Q9. What is the purpose of touched state?

Answer: Touched state tracks whether a user has interacted with or left a field. It can help decide when validation errors should be displayed.

Q10. How should form validation be handled?

Answer: Validate the current state using the application's rules, store any errors, and display understandable messages. Validate again on the server before accepting submitted data.

Q11. How can you reset a controlled form?

Answer: Restore the form state to its initial values and clear related errors and touched status as appropriate.

Q12. How can you prevent duplicate submissions?

Answer: Track submission status, disable the submit button while a request is in progress, and ensure the handler does not start another request until the current one is finished.

Q13. When should a form-management library be considered?

Answer: Consider a library when a form has many fields, complex validation, dynamic sections, or repetitive state-management logic. Small forms can often be handled with React state alone.

19. Quick Revision Checklist

  • Understand controlled components and form state.
  • Connect TextInput to state using value.
  • Update input values using onChangeText.
  • Use useState to manage form data.
  • Choose separate state variables or object state according to the form's complexity.
  • Preserve previous object properties when updating a field.
  • Understand controlled inputs versus uncontrolled-style inputs.
  • Track validation errors and touched fields where useful.
  • Manage loading, success, and error states for submission.
  • Reset controlled values through state updates.
  • Avoid mutating state directly.
  • Validate user input on both client and server.
  • Protect passwords and other sensitive information.

20. Conclusion

Controlled components and form state are fundamental concepts in React Native development. They allow developers to keep input values synchronized with React, validate user information, display useful feedback, reset fields, and manage asynchronous form submissions.

For simple forms, useState and controlled TextInput components are often sufficient. As forms grow, organizing values, errors, touched fields, and submission status separately can make the implementation easier to maintain. Always combine a clear user experience with server-side validation and appropriate security practices.

21. Additional Learning Resources

whatsapp