Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Handling User Input with TextInput

User Input with TextInput

~9 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026

Every app eventually needs text input – a search box, a login form, an address. In React Native that's handled by the TextInput component.

Basics: controlled input fields

Unlike an HTML <input>, whose value simply lives in the DOM, a TextInput in React Native (and React in general) is usually a "controlled" field: its value comes from state, and every keystroke updates that state.

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

function SearchField() {
  const [searchText, setSearchText] = useState('');

  return (
    <View style={styles.container}>
      <TextInput
        style={styles.input}
        placeholder="Search products..."
        value={searchText}
        onChangeText={setSearchText}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: { padding: 16 },
  input: {
    borderWidth: 1,
    borderColor: '#d1d5db',
    borderRadius: 8,
    padding: 10,
    fontSize: 16,
  },
});
HTML/WebReact Native
<input type="text" placeholder="..."><TextInput placeholder="..." />
input.addEventListener('input', e => ...)onChangeText={{(text) => ...}} – receives the new text value directly, no event object needed
input.value = searchText (set imperatively)value={{searchText}} (declarative, from state)
<input type="password"><TextInput secureTextEntry />
<input type="email" inputmode="email"><TextInput keyboardType="email-address" /> – shows the matching on-screen keyboard
CSS :focus stylingonFocus / onBlur callback props + your own state

Now let's build this directly into our real screens/ProductListScreen.js from chapter 10. Replace the entire content of the file with this extended version:

screens/ProductListScreen.js
import { useState, useEffect } from 'react';
import {
  View,
  Text,
  FlatList,
  TextInput,
  StyleSheet,
  ActivityIndicator,
} from 'react-native';
import { fetchProducts } from '../api/magentoApi';
import ProductCard from '../components/ProductCard';

function ProductListScreen({ navigation }) {
  const [products, setProducts] = useState([]);
  const [loading, setLoading] = useState(true);
  const [searchText, setSearchText] = useState('');

  useEffect(() => {
    async function loadProducts() {
      try {
        const items = await fetchProducts();
        setProducts(items);
      } catch (error) {
        console.error(error);
      } finally {
        setLoading(false);
      }
    }
    loadProducts();
  }, []);

  if (loading) {
    return <ActivityIndicator size="large" style={styles.loader} />;
  }

  const filteredProducts = products.filter((product) =>
    product.name.toLowerCase().includes(searchText.toLowerCase())
  );

  return (
    <View style={styles.container}>
      <TextInput
        style={styles.searchInput}
        placeholder="Search products..."
        value={searchText}
        onChangeText={setSearchText}
      />
      <FlatList
        data={filteredProducts}
        keyExtractor={(item) => item.sku}
        renderItem={({ item }) => (
          <ProductCard
            name={item.name}
            price={item.price}
            imageUrl={item.imageUrl}
            onPress={() =>
              navigation.navigate('ProductDetail', { productSku: item.sku })
            }
          />
        )}
        ListEmptyComponent={<Text style={styles.emptyText}>No products found.</Text>}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, paddingTop: 16, paddingHorizontal: 16 },
  loader: { flex: 1, justifyContent: 'center' },
  searchInput: {
    borderWidth: 1,
    borderColor: '#d1d5db',
    borderRadius: 8,
    padding: 10,
    fontSize: 16,
    marginBottom: 12,
  },
  emptyText: { textAlign: 'center', marginTop: 24, color: '#6b7280' },
});

export default ProductListScreen;

What's new is the searchText state, the TextInput field above the list, and the line const filteredProducts = products.filter(...). On every keystroke, searchText changes via setSearchText, the component re-renders, filteredProducts gets recalculated, and the FlatList automatically shows only matching results – no manual DOM filtering needed, unlike setting element.style.display = 'none' per row on the web. Note that the FlatList now receives filteredProducts instead of products as its data.

Achtung: For large product lists (hundreds/thousands of entries), filtering should ideally happen server-side through the search API (with Magento, e.g. the /rest/V1/search endpoint or Elasticsearch), rather than iterating the full array client-side – you'll learn this in an advanced tutorial section once this series is extended with a debounce technique.