Forms in React Native

Formulieren in React Native werken anders dan in web React. Je gebruikt TextInput in plaats van <input>, en voor dropdowns gebruik je de @react-native-picker/picker package.

Belangrijk verschil met web:

  • Geen <form> tag in React Native
  • Gebruik TextInput in plaats van <input>
  • Gebruik onChangeText in plaats van onChange
  • Geen event.preventDefault() nodig

TextInput Basics

De TextInput component is het belangrijkste element voor gebruikersinvoer.

Simpel Voorbeeld

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

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

  return (
    <View style={styles.container}>
      <TextInput
        style={styles.input}
        placeholder="Typ je naam..."
        value={name}
        onChangeText={setName}
      />
      <Text>Hallo {name}!</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    padding: 20,
  },
  input: {
    borderWidth: 1,
    borderColor: '#ddd',
    padding: 10,
    borderRadius: 5,
    fontSize: 16,
  },
});

Belangrijke Props

<TextInput
  placeholder="Email adres"
  value={email}
  onChangeText={setEmail}
  keyboardType="email-address"
  autoCapitalize="none"
  autoCorrect={false}
  secureTextEntry={false}
  maxLength={50}
  multiline={false}
  numberOfLines={1}
/>

Keyboard Types:

  • default - Normaal toetsenbord
  • email-address - Email toetsenbord met @
  • number-pad - Alleen nummers (geen decimalen)
  • phone-pad - Telefoon toetsenbord
  • numeric - Nummers met +/- en decimaal

Meerdere Input Fields

Voor formulieren met meerdere velden gebruik je één useState met een object.

Contact Formulier

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

export default function ContactForm() {
  const [formData, setFormData] = useState({
    name: '',
    email: '',
    message: '',
  });

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

  const handleSubmit = () => {
    console.log('Form data:', formData);
    // Hier kun je data versturen naar een API
  };

  return (
    <View style={styles.container}>
      <TextInput
        style={styles.input}
        placeholder="Naam"
        value={formData.name}
        onChangeText={(text) => handleChange('name', text)}
      />

      <TextInput
        style={styles.input}
        placeholder="Email"
        value={formData.email}
        onChangeText={(text) => handleChange('email', text)}
        keyboardType="email-address"
        autoCapitalize="none"
      />

      <TextInput
        style={[styles.input, styles.textArea]}
        placeholder="Bericht"
        value={formData.message}
        onChangeText={(text) => handleChange('message', text)}
        multiline
        numberOfLines={4}
      />

      <Pressable style={styles.button} onPress={handleSubmit}>
        <Text style={styles.buttonText}>Verstuur</Text>
      </Pressable>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    padding: 20,
  },
  input: {
    borderWidth: 1,
    borderColor: '#ddd',
    padding: 12,
    borderRadius: 8,
    marginBottom: 15,
    fontSize: 16,
  },
  textArea: {
    height: 100,
    textAlignVertical: 'top',
  },
  button: {
    backgroundColor: '#007AFF',
    padding: 15,
    borderRadius: 8,
    alignItems: 'center',
  },
  buttonText: {
    color: 'white',
    fontSize: 16,
    fontWeight: 'bold',
  },
});

Pro tip: Gebruik textAlignVertical: 'top' bij multiline TextInput zodat tekst bovenaan begint!

Picker Component

Voor dropdown menu's gebruik je de @react-native-picker/picker package.

Installatie

npx expo install @react-native-picker/picker

Basis Gebruik

import { useState } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { Picker } from '@react-native-picker/picker';

export default function App() {
  const [selectedValue, setSelectedValue] = useState('');

  return (
    <View style={styles.container}>
      <Text style={styles.label}>Kies een land:</Text>
      
      <Picker
        selectedValue={selectedValue}
        onValueChange={(itemValue) => setSelectedValue(itemValue)}
        style={styles.picker}
      >
        <Picker.Item label="Selecteer een land..." value="" />
        <Picker.Item label="Nederland" value="nl" />
        <Picker.Item label="België" value="be" />
        <Picker.Item label="Duitsland" value="de" />
        <Picker.Item label="Frankrijk" value="fr" />
      </Picker>

      <Text>Geselecteerd: {selectedValue}</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    padding: 20,
  },
  label: {
    fontSize: 16,
    marginBottom: 10,
  },
  picker: {
    borderWidth: 1,
    borderColor: '#ddd',
    borderRadius: 8,
  },
});

Picker in Formulier

const [formData, setFormData] = useState({
  name: '',
  country: '',
});

<Picker
  selectedValue={formData.country}
  onValueChange={(value) => setFormData({...formData, country: value})}
>
  <Picker.Item label="Selecteer..." value="" />
  <Picker.Item label="Nederland" value="nl" />
  <Picker.Item label="België" value="be" />
</Picker>

ImagePicker - Afbeeldingen Selecteren

Met expo-image-picker kunnen gebruikers foto's selecteren uit hun galerij of met de camera maken.

Installatie

npx expo install expo-image-picker

Basis Gebruik

import { useState } from 'react';
import { View, Image, Pressable, Text, StyleSheet } from 'react-native';
import * as ImagePicker from 'expo-image-picker';

export default function App() {
  const [image, setImage] = useState(null);

  const pickImage = async () => {
    // Vraag permissie
    const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
    
    if (status !== 'granted') {
      alert('Sorry, we hebben permissie nodig!');
      return;
    }

    // Open galerij
    const result = await ImagePicker.launchImageLibraryAsync({
      mediaTypes: ImagePicker.MediaTypeOptions.Images,
      allowsEditing: true,
      aspect: [4, 3],
      quality: 1,
    });

    if (!result.canceled) {
      setImage(result.assets[0].uri);
    }
  };

  return (
    <View style={styles.container}>
      <Pressable style={styles.button} onPress={pickImage}>
        <Text style={styles.buttonText}>Kies foto</Text>
      </Pressable>

      {image && (
        <Image source={{ uri: image }} style={styles.image} />
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    padding: 20,
  },
  button: {
    backgroundColor: '#007AFF',
    padding: 15,
    borderRadius: 8,
  },
  buttonText: {
    color: 'white',
    fontSize: 16,
  },
  image: {
    width: 200,
    height: 200,
    marginTop: 20,
    borderRadius: 8,
  },
});

Camera Gebruiken

const takePhoto = async () => {
  const { status } = await ImagePicker.requestCameraPermissionsAsync();
  
  if (status !== 'granted') {
    alert('Camera permissie nodig!');
    return;
  }

  const result = await ImagePicker.launchCameraAsync({
    allowsEditing: true,
    aspect: [4, 3],
    quality: 1,
  });

  if (!result.canceled) {
    setImage(result.assets[0].uri);
  }
};

Let op: ImagePicker werkt niet in de web browser! Test altijd op je telefoon of emulator.

Form Validatie

Valideer formulierdata voordat je het verstuurt.

Simpele Validatie

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

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

  const validateEmail = (email) => {
    return email.includes('@') && email.includes('.');
  };

  const handleSubmit = () => {
    // Validatie
    if (!email || !password) {
      Alert.alert('Fout', 'Vul alle velden in');
      return;
    }

    if (!validateEmail(email)) {
      Alert.alert('Fout', 'Ongeldig email adres');
      return;
    }

    if (password.length < 6) {
      Alert.alert('Fout', 'Wachtwoord moet minimaal 6 karakters zijn');
      return;
    }

    // Als alles OK is
    console.log('Login met:', email, password);
    Alert.alert('Success', 'Ingelogd!');
  };

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

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

      <Pressable style={styles.button} onPress={handleSubmit}>
        <Text style={styles.buttonText}>Inloggen</Text>
      </Pressable>
    </View>
  );
}

Volledig Voorbeeld - Profiel Formulier

import { useState } from 'react';
import { View, TextInput, Pressable, Text, Image, Alert, StyleSheet } from 'react-native';
import { Picker } from '@react-native-picker/picker';
import * as ImagePicker from 'expo-image-picker';

export default function ProfileForm() {
  const [formData, setFormData] = useState({
    name: '',
    email: '',
    country: '',
    bio: '',
  });
  const [profileImage, setProfileImage] = useState(null);

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

  const pickImage = async () => {
    const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
    if (status !== 'granted') return;

    const result = await ImagePicker.launchImageLibraryAsync({
      allowsEditing: true,
      aspect: [1, 1],
      quality: 1,
    });

    if (!result.canceled) {
      setProfileImage(result.assets[0].uri);
    }
  };

  const handleSubmit = () => {
    if (!formData.name || !formData.email || !formData.country) {
      Alert.alert('Fout', 'Vul alle verplichte velden in');
      return;
    }

    console.log('Profile data:', formData);
    console.log('Image:', profileImage);
    Alert.alert('Success', 'Profiel opgeslagen!');
  };

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

      {/* Profile Image */}
      <Pressable onPress={pickImage} style={styles.imageContainer}>
        {profileImage ? (
          <Image source={{ uri: profileImage }} style={styles.image} />
        ) : (
          <View style={styles.imagePlaceholder}>
            <Text>Kies foto</Text>
          </View>
        )}
      </Pressable>

      {/* Name */}
      <TextInput
        style={styles.input}
        placeholder="Naam *"
        value={formData.name}
        onChangeText={(text) => handleChange('name', text)}
      />

      {/* Email */}
      <TextInput
        style={styles.input}
        placeholder="Email *"
        value={formData.email}
        onChangeText={(text) => handleChange('email', text)}
        keyboardType="email-address"
        autoCapitalize="none"
      />

      {/* Country Picker */}
      <View style={styles.pickerContainer}>
        <Picker
          selectedValue={formData.country}
          onValueChange={(value) => handleChange('country', value)}
        >
          <Picker.Item label="Selecteer land *" value="" />
          <Picker.Item label="Nederland" value="nl" />
          <Picker.Item label="België" value="be" />
          <Picker.Item label="Duitsland" value="de" />
        </Picker>
      </View>

      {/* Bio */}
      <TextInput
        style={[styles.input, styles.textArea]}
        placeholder="Bio"
        value={formData.bio}
        onChangeText={(text) => handleChange('bio', text)}
        multiline
        numberOfLines={4}
      />

      {/* Submit Button */}
      <Pressable style={styles.button} onPress={handleSubmit}>
        <Text style={styles.buttonText}>Opslaan</Text>
      </Pressable>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
    backgroundColor: '#f5f5f5',
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 20,
  },
  imageContainer: {
    alignItems: 'center',
    marginBottom: 20,
  },
  image: {
    width: 120,
    height: 120,
    borderRadius: 60,
  },
  imagePlaceholder: {
    width: 120,
    height: 120,
    borderRadius: 60,
    backgroundColor: '#ddd',
    justifyContent: 'center',
    alignItems: 'center',
  },
  input: {
    borderWidth: 1,
    borderColor: '#ddd',
    padding: 12,
    borderRadius: 8,
    marginBottom: 15,
    backgroundColor: 'white',
    fontSize: 16,
  },
  pickerContainer: {
    borderWidth: 1,
    borderColor: '#ddd',
    borderRadius: 8,
    marginBottom: 15,
    backgroundColor: 'white',
  },
  textArea: {
    height: 100,
    textAlignVertical: 'top',
  },
  button: {
    backgroundColor: '#007AFF',
    padding: 15,
    borderRadius: 8,
    alignItems: 'center',
  },
  buttonText: {
    color: 'white',
    fontSize: 16,
    fontWeight: 'bold',
  },
});

Samenvatting

TextInput

Gebruik onChangeText, niet onChange

Picker

Dropdown menu's met @react-native-picker/picker

ImagePicker

Foto's selecteren met expo-image-picker

Validatie

Check data voordat je submit

Je hebt nu geleerd:

  • TextInput gebruiken voor text invoer
  • Meerdere input fields beheren met één state object
  • Picker gebruiken voor dropdown menu's
  • ImagePicker voor foto selectie en camera
  • Form validatie voordat je data verstuurt
  • Complete profiel formulier implementeren