Handling User Input and Forms
1. Controlling TextInput
| Property | Description | Values |
|---|---|---|
| value | Sets the text content of the input | string |
| onChangeText | Callback function when text changes | (text: string) => void |
| placeholder | Hint text when input is empty | string |
| multiline | Allows multiple lines in input | true | false |
| editable | Enables or disables editing | true | false |
Example: Basic TextInput Control
<TextInput
value={this.state.text}
onChangeText={(text) => this.setState({ text })}
placeholder="Enter some text"
style={{ height: 40, borderColor: 'gray', borderWidth: 1 }}
/>
Example: Multiline TextInput
<TextInput
multiline={true}
numberOfLines={4}
placeholder="Enter multiple lines"
style={{ height: 100, borderColor: 'gray', borderWidth: 1 }}
/>
2. Handling Keyboard Events
| Event | Description | Properties |
|---|---|---|
| onKeyDown | Triggered when a key is pressed down | key, keyCode, shiftKey |
| onKeyUp | Triggered when a key is released | key, keyCode, shiftKey |
Example: Basic Key Handling
import React, { useState } from 'react';
import { View, Text, TextInput } from 'react-native';
const KeyboardHandler = () => {
const [input, setInput] = useState('');
const handleKeyDown = (e) => {
if (e.nativeEvent.key === 'Enter') {
console.log('Enter pressed:', input);
}
};
return (
<View>
<TextInput
value={input}
onChangeText={setInput}
onKeyDown={handleKeyDown}
placeholder="Press Enter"
/>
</View>
);
};
export default KeyboardHandler;
Note: Keyboard events in React Native are handled through the
onKeyDown and onKeyUp props. These events are not available on all platforms (e.g., iOS may not support onKeyDown in all cases).
3. Managing Keyboard Visibility
Example: Handling Keyboard Events
import React, { useState } from 'react';
import { View, TextInput, Keyboard, StyleSheet } from 'react-native';
const KeyboardExample = () => {
const [text, setText] = useState('');
const [isKeyboardVisible, setIsKeyboardVisible] = useState(false);
React.useEffect(() => {
const keyboardDidShow = Keyboard.addListener('keyboardDidShow', () => {
setIsKeyboardVisible(true);
});
const keyboardDidHide = Keyboard.addListener('keyboardDidHide', () => {
setIsKeyboardVisible(false);
});
return () => {
keyboardDidShow.remove();
keyboardDidHide.remove();
};
}, []);
return (
<View style={[styles.container, isKeyboardVisible ? styles.keyboardVisible : null]}>
<TextInput
style={styles.input}
value={text}
onChangeText={setText}
placeholder="Type something..."
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
keyboardVisible: {
paddingBottom: 200, // Adjust as needed
},
input: {
width: '80%',
padding: 10,
borderWidth: 1,
borderColor: '#ccc',
borderRadius: 5,
},
});
export default KeyboardExample;
| Event | Description | Use Case |
|---|---|---|
| keyboardDidShow | Triggered when the keyboard is about to be shown | Adjust layout or scroll position to keep input in view |
| keyboardDidHide | Triggered when the keyboard is about to be hidden | Reset layout or restore scroll position after input |
| keyboardWillShow | Triggered before the keyboard is shown | Prepare UI adjustments in advance |
| keyboardWillHide | Triggered before the keyboard is hidden | Undo UI adjustments before hiding |
Note: Always remove keyboard event listeners in the cleanup function of useEffect to prevent memory leaks.
Example: Using KeyboardAvoidingView
import React from 'react';
import { View, TextInput, KeyboardAvoidingView, Platform, StyleSheet } from 'react-native';
const KeyboardAvoidingExample = () => {
return (
<KeyboardAvoidingView
style={styles.container}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
>
<View style={styles.inputContainer}>
<TextInput
style={styles.input}
placeholder="Enter text here"
/>
</View>
</KeyboardAvoidingView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
inputContainer: {
padding: 20,
},
input: {
height: 40,
borderColor: 'gray',
borderWidth: 1,
borderRadius: 5,
paddingHorizontal: 10,
},
});
export default KeyboardAvoidingExample;
Keyboard Visibility Management Tips
- Use
KeyboardAPI to listen for keyboard show/hide events - Apply
KeyboardAvoidingViewto automatically adjust layout - Manually adjust view padding or margins when keyboard is visible
- Always clean up event listeners to prevent memory leaks
4. Using KeyboardAvoidingView
KeyboardAvoidingView is a React Native component that automatically adjusts the position of your UI elements to prevent them from being obscured by the keyboard on mobile devices.
| Property | Description | Default Value | Platform Support |
|---|---|---|---|
| behavior | Defines how the view adjusts to the keyboard. Options: height, padding, or position. |
height |
iOS, Android |
| keyboardVerticalOffset | Adjusts the vertical offset of the view to account for status bars or other elements. | 0 |
iOS, Android |
| enabled | Enables or disables the keyboard avoiding behavior. | true |
iOS, Android |
Example: Basic Usage of KeyboardAvoidingView
import React from 'react';
import { KeyboardAvoidingView, TextInput, View, Text, StyleSheet } from 'react-native';
const Example = () => {
return (
<KeyboardAvoidingView style={styles.container} behavior="padding">
<View style={styles.inputContainer}>
<TextInput placeholder="Enter text" style={styles.input} />
<Text>This input will stay visible when keyboard appears</Text>
</View>
</KeyboardAvoidingView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
inputContainer: {
padding: 20,
},
input: {
borderWidth: 1,
borderColor: '#ccc',
padding: 10,
marginBottom: 10,
},
});
export default Example;
Note: On Android,
KeyboardAvoidingView requires the android:windowSoftInputMode attribute to be set in your app's manifest file to function correctly.
Warning: Using
behavior="position" on Android may cause layout issues due to how the keyboard interacts with the view hierarchy.
5. Implementing Form Validation
Example: Basic Form Validation with useState
import React, { useState } from 'react';
const FormValidation = () => {
const [formData, setFormData] = useState({
name: '',
email: ''
});
const [errors, setErrors] = useState({});
const validate = () => {
const newErrors = {};
if (!formData.name.trim()) {
newErrors.name = 'Name is required';
}
if (!formData.email.trim()) {
newErrors.email = 'Email is required';
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
newErrors.email = 'Invalid email format';
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleChange = (e) => {
const { name, value } = e.target;
setFormData({
...formData,
[name]: value
});
};
const handleSubmit = (e) => {
e.preventDefault();
if (validate()) {
console.log('Form submitted', formData);
}
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
name="name"
value={formData.name}
onChange={handleChange}
placeholder="Name"
/>
{errors.name && <span style={{ color: 'red' }}>{errors.name}</span>}
<br />
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
placeholder="Email"
/>
{errors.email && <span style={{ color: 'red' }}>{errors.email}</span>}
<br />
<button type="submit">Submit</button>
</form>
);
};
export default FormValidation;
| Property | Description | Example |
|---|---|---|
| name | Form field name used for validation and submission | name |
| value | Current value of the input field | 'John Doe' |
| onChange | Event handler to update form state | (e) => setFormData({...formData, [e.target.name]: e.target.value}) |
| errors | Object containing validation error messages | { name: 'Name is required' } |
Note: Validation logic should be consistent between client and server-side to ensure data integrity.
Warning: Client-side validation can be bypassed. Always validate on the server.
Form Validation Best Practices
- Validate input on both client and server side
- Provide real-time feedback to users
- Use clear and actionable error messages
- Implement proper state management for form fields
6. Handling Multi-line Input
| Property | Description | Usage |
|---|---|---|
| multiline | Enables multi-line input in TextInput | multiline={true} |
| numberOfLines | Controls the number of visible lines | numberOfLines={3} |
| maxHeight | Sets maximum height for the input area | maxHeight={200} |
Example: Multi-line Text Input
<TextInput
multiline={true}
numberOfLines={4}
maxHeight={200}
placeholder="Enter multi-line text"
style={{
height: 100,
borderColor: "gray",
borderWidth: 1,
padding: 10
}}
/>
Note: Use
numberOfLines to control initial visible lines and maxHeight to prevent overflow.
7. Using Secure Text Entry
| Property | Values | Description |
|---|---|---|
| secureTextEntry | true | false |
When set to true, the input field obscures text (e.g., for passwords). Default is false. |
Note:
secureTextEntry is supported on both iOS and Android, but behavior may vary slightly between platforms.
8. Implementing Auto-focus and Tab Navigation
Example: Auto-focusing a TextInput on Component Mount
<TextInput
ref={inputRef}
placeholder="Enter text"
autoFocus={true}
/>
| Property | Values | Description |
|---|---|---|
| autoFocus | true | false |
Whether the input should receive focus on mount |
| onSubmitEditing | Function | Callback when the user submits the input (e.g., presses Enter) |
Example: Managing Tab Navigation Between Inputs
<View>
<TextInput
ref={firstInputRef}
placeholder="First input"
onSubmitEditing={() => secondInputRef.current?.focus()}
/>
<TextInput
ref={secondInputRef}
placeholder="Second input"
onSubmitEditing={() => thirdInputRef.current?.focus()}
/>
<TextInput
ref={thirdInputRef}
placeholder="Third input"
/>
</View>
Note: Use
ref.current?.focus() to programmatically focus inputs. Ensure refs are properly initialized.
Best Practices for Auto-focus and Tab Navigation
- Use
autoFocussparingly and only when necessary for UX. - Implement
onSubmitEditingto handle Enter key submission. - Use
refto manage focus between inputs in forms.
9. Handling Input Masking
| Property | Description | Usage |
|---|---|---|
| inputMask | Applies a mask to an input field to enforce formatting (e.g., phone number, date) | inputMask="(999) 999-9999" |
| maskType | Defines the type of mask to apply (e.g., phone, date) | maskType="phone" |
Example: Phone Number Input with Mask
import { TextInput } from 'react-native';
<TextInput
placeholder="Enter phone number"
inputMask="(999) 999-9999"
maskType="phone"
keyboardType="phone-pad"
/>
Note: The
inputMask property is not part of the standard React Native API. You may need to use a third-party library like react-native-input-mask.
Comparison: Manual vs Library-Based Masking
- Manual: Requires custom logic to format and validate input
- Library-Based: Provides pre-built mask patterns and validation
10. Managing Form State
| Property | Description | Usage |
|---|---|---|
| useState | React hook for managing local component state | const [value, setValue] = useState(''); |
| useReducer | Alternative to useState for complex state logic | const [state, dispatch] = useReducer(reducer, initialState); |
| useForm | Custom hook for form state management (e.g., from react-hook-form) | const { register, handleSubmit } = useForm(); |
Example: Basic form with useState
import React, { useState } from 'react';\n\nfunction MyForm() {\n const [name, setName] = useState('');\n const [email, setEmail] = useState('');\n\n const handleSubmit = (e) => {\n e.preventDefault();\n console.log({ name, email });\n };\n\n return (\n <form onSubmit={handleSubmit}>\n <input\n type="text"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder="Name"\n />\n <input\n type="email"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n placeholder="Email"\n />\n <button type="submit">Submit</button>\n </form>\n );\n}
Example: Using useReducer for form state
import React, { useReducer } from 'react';\n\nconst formReducer = (state, action) => {\n switch (action.type) {\n case 'SET_FIELD':\n return { ...state, [action.field]: action.value };\n case 'RESET_FORM':\n return action.initialState;\n default:\n return state;\n }\n};\n\nfunction MyForm() {\n const initialState = { name: '', email: '' };\n const [state, dispatch] = useReducer(formReducer, initialState);\n\n const handleChange = (field, value) => {\n dispatch({ type: 'SET_FIELD', field, value });\n };\n\n const handleSubmit = (e) => {\n e.preventDefault();\n console.log(state);\n };\n\n return (\n <form onSubmit={handleSubmit}>\n <input\n type="text"\n value={state.name}\n onChange={(e) => handleChange('name', e.target.value)}\n placeholder="Name"\n />\n <input\n type="email"\n value={state.email}\n onChange={(e) => handleChange('email', e.target.value)}\n placeholder="Email"\n />\n <button type="submit">Submit</button>\n </form>\n );\n}
Note: For complex forms, consider using libraries like
react-hook-form or formik to simplify validation and state management.
11. Dismissing Keyboard
Example: Dismissing Keyboard on Background Tap
import React from 'react';
import { View, TouchableWithoutFeedback, Keyboard } from 'react-native';
const DismissKeyboardView = ({ children }) => (
<TouchableWithoutFeedback onPress={() => Keyboard.dismiss()}>
<View style={{ flex: 1 }}>
{children}
</View>
</TouchableWithoutFeedback>
);
export default DismissKeyboardView;
| Property | Description | Usage |
|---|---|---|
| Keyboard.dismiss() | Method to dismiss the active keyboard | Call when user taps outside input field |
Note: This approach works best when wrapped around the entire form or input container.