Labs ICT
Pro Login

Form Handling

Building and managing forms.

Building Forms

Forms are essential for getting user input — login forms, registration forms, search forms, feedback forms, and more. In React Native, forms are built using TextInput components managed by state. Unlike web forms that use HTML form elements, React Native forms are entirely custom components.

The pattern is straightforward: each input field has a state variable, and when the user types, the state updates. When they submit, you collect all the state values and send them somewhere.

Controlled Form Components

A controlled form is one where React controls the input values through state. Every keystroke updates the state, and the state determines what's displayed.

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

function LoginForm() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const handleSubmit = () => {
    console.log('Email:', email);
    console.log('Password:', password);
    // Send to server here
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Login</Text>

      <TextInput
        style={styles.input}
        placeholder="Email"
        value={email}
        onChangeText={setEmail}
        keyboardType="email-address"
        autoCapitalize="none"
      />

      <TextInput
        style={styles.input}
        placeholder="Password"
        value={password}
        onChangeText={setPassword}
        secureTextEntry
      />

      <Button title="Login" onPress={handleSubmit} />
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: 'center', padding: 20 },
  title: { fontSize: 24, fontWeight: 'bold', marginBottom: 20, textAlign: 'center' },
  input: {
    height: 50,
    borderWidth: 1,
    borderColor: '#ccc',
    borderRadius: 8,
    paddingHorizontal: 15,
    marginBottom: 15,
    fontSize: 16,
  },
});

export default LoginForm;

Each TextInput has a value bound to state and an onChangeText that updates state. The autoCapitalize="none" prevents email fields from auto-capitalizing. The secureTextEntry hides password characters. When the user taps Login, we collect the state values.

Multi-Field Forms

For forms with many fields, you can organize state more efficiently using a single object instead of separate variables.

function RegistrationForm() {
  const [formData, setFormData] = useState({
    firstName: '',
    lastName: '',
    email: '',
    phone: '',
  });

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

  const handleSubmit = () => {
    console.log('Form data:', formData);
  };

  return (
    <View style={{ padding: 20 }}>
      <TextInput
        placeholder="First Name"
        value={formData.firstName}
        onChangeText={(value) => updateField('firstName', value)}
        style={styles.input}
      />
      <TextInput
        placeholder="Last Name"
        value={formData.lastName}
        onChangeText={(value) => updateField('lastName', value)}
        style={styles.input}
      />
      <TextInput
        placeholder="Email"
        value={formData.email}
        onChangeText={(value) => updateField('email', value)}
        keyboardType="email-address"
        style={styles.input}
      />
      <TextInput
        placeholder="Phone"
        value={formData.phone}
        onChangeText={(value) => updateField('phone', value)}
        keyboardType="phone-pad"
        style={styles.input}
      />
      <Button title="Register" onPress={handleSubmit} />
    </View>
  );
}

The updateField function uses the spread operator to copy existing fields and override the one being updated. This pattern scales well — adding a new field only requires adding one state property and one TextInput.

Handling Form Submission

When the user submits the form, you need to validate the data, send it to a server, and handle the response. Always provide feedback during submission.

function ContactForm() {
  const [name, setName] = useState('');
  const [message, setMessage] = useState('');
  const [submitting, setSubmitting] = useState(false);

  const handleSubmit = async () => {
    setSubmitting(true);
    try {
      const response = await fetch('https://api.example.com/contact', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ name, message }),
      });

      if (response.ok) {
        Alert.alert('Success', 'Message sent!');
        setName('');
        setMessage('');
      } else {
        Alert.alert('Error', 'Failed to send message');
      }
    } catch (error) {
      Alert.alert('Error', 'Network error');
    } finally {
      setSubmitting(false);
    }
  };

  return (
    <View style={{ padding: 20 }}>
      <TextInput placeholder="Name" value={name} onChangeText={setName} />
      <TextInput placeholder="Message" value={message} onChangeText={setMessage} multiline />
      <Button
        title={submitting ? 'Sending...' : 'Send Message'}
        onPress={handleSubmit}
        disabled={submitting}
      />
    </View>
  );
}

The submitting state prevents double-submission and shows a loading state. The finally block ensures the submitting state is reset whether the request succeeds or fails. Always clear the form after successful submission and show appropriate messages to the user.