22. Eventos
Curso: Eventos en React Native - Manejo de Interacción del Usuario
📚 Módulo 1: Fundamentos de Eventos en React Native
🎯 Objetivo del Curso
Aprender a manejar eventos en React Native para crear aplicaciones interactivas que respondan a las acciones del usuario.
📋 ¿Qué son los Eventos?
Los eventos son acciones que ocurren en respuesta a la interacción del usuario con la aplicación:
Toques/Clics: onPress, onLongPress
Texto: onChangeText, onSubmitEditing
Scroll: onScroll, onMomentumScrollEnd
Gestos: onSwipe, onPinch
Teclado: onKeyboardShow, onKeyboardHide
🛠️ Módulo 2: Evento Básico - onPress
2.1 Componentes Táctiles en React Native
React Native proporciona varios componentes para manejar toques:
2.2 Implementando onPress en Functional Components
tsx
// src/components/BotonConEvento/BotonConEvento.tsx
import React from 'react';
import {
TouchableOpacity,
Text,
StyleSheet,
ViewStyle,
ColorValue,
} from 'react-native';
// Definir interfaz para props incluyendo el evento
interface BotonConEventoProps {
titulo: string;
colorFondo?: ColorValue;
colorTexto?: ColorValue;
estiloPersonalizado?: ViewStyle;
onPress: () => void; // ✅ Propiedad de evento requerida
}
function BotonConEvento({
titulo,
colorFondo = '#007AFF',
colorTexto = 'white',
estiloPersonalizado,
onPress, // ✅ Recibir el manejador de eventos
}: BotonConEventoProps): JSX.Element {
return (
<TouchableOpacity
style={[
styles.boton,
{backgroundColor: colorFondo},
estiloPersonalizado,
]}
onPress={onPress} // ✅ Asignar el manejador de eventos
activeOpacity={0.7} // Opacidad al presionar (0 a 1)
>
<Text style={[styles.texto, {color: colorTexto}]}>
{titulo}
</Text>
</TouchableOpacity>
);
}
const styles = StyleSheet.create({
boton: {
paddingVertical: 12,
paddingHorizontal: 24,
borderRadius: 8,
alignItems: 'center',
justifyContent: 'center',
margin: 10,
},
texto: {
fontSize: 16,
fontWeight: '600',
},
});
export default BotonConEvento;
2.3 Uso del Componente con Evento
tsx
// App.tsx
import React from 'react';
import {SafeAreaView, Text, View} from 'react-native';
import BotonConEvento from './src/components/BotonConEvento/BotonConEvento';
function App(): JSX.Element {
// Manejador de evento como función inline
const manejarPresionSimple = () => {
console.log('¡Botón presionado!');
alert('Evento onPress ejecutado');
};
// Manejador de evento con parámetros
const manejarPresionConParametro = (mensaje: string) => {
console.log(`Mensaje: ${mensaje}`);
};
// Manejador de evento complejo
const manejarPresionComplejo = () => {
console.log('1. Validando datos...');
console.log('2. Enviando a servidor...');
console.log('3. Actualizando estado...');
alert('Proceso completado');
};
return (
<SafeAreaView style={{flex: 1, padding: 20}}>
<Text style={{fontSize: 24, marginBottom: 20}}>
Ejemplos de Eventos onPress
</Text>
{/* Evento simple */}
<BotonConEvento
titulo="Presióname"
onPress={manejarPresionSimple}
/>
{/* Evento con parámetro usando arrow function */}
<BotonConEvento
titulo="Saludar"
onPress={() => manejarPresionConParametro('¡Hola Mundo!')}
colorFondo="#34C759"
/>
{/* Evento complejo */}
<BotonConEvento
titulo="Proceso Completo"
onPress={manejarPresionComplejo}
colorFondo="#FF9500"
/>
{/* Evento inline sin función separada */}
<BotonConEvento
titulo="Evento Inline"
onPress={() => {
console.log('Evento ejecutado directamente');
console.log('Hora:', new Date().toLocaleTimeString());
}}
colorFondo="#5856D6"
/>
</SafeAreaView>
);
}
export default App;
🏗️ Módulo 3: Eventos en Class Components
3.1 Implementando Eventos en Class Components
tsx
// src/components/BotonClase/BotonClase.tsx
import React from 'react';
import {
TouchableOpacity,
Text,
StyleSheet,
ViewStyle,
ColorValue,
} from 'react-native';
interface BotonClaseProps {
titulo: string;
colorFondo?: ColorValue;
onPress: () => void;
}
interface BotonClaseState {
contadorPresiones: number;
estaPresionado: boolean;
}
class BotonClase extends React.Component<BotonClaseProps, BotonClaseState> {
constructor(props: BotonClaseProps) {
super(props);
this.state = {
contadorPresiones: 0,
estaPresionado: false,
};
}
// Manejador de evento como método de clase
manejarPresion = () => {
const {onPress} = this.props;
// Actualizar estado
this.setState(prevState => ({
contadorPresiones: prevState.contadorPresiones + 1,
estaPresionado: true,
}));
// Ejecutar callback del padre
onPress();
// Resetear estado después de 200ms
setTimeout(() => {
this.setState({estaPresionado: false});
}, 200);
};
render() {
const {titulo, colorFondo = '#FF3B30'} = this.props;
const {contadorPresiones, estaPresionado} = this.state;
return (
<TouchableOpacity
style={[
styles.boton,
{backgroundColor: colorFondo},
estaPresionado && styles.botonPresionado,
]}
onPress={this.manejarPresion} // ✅ Usar método de clase
activeOpacity={0.7}
>
<Text style={styles.texto}>
{titulo} ({contadorPresiones})
</Text>
</TouchableOpacity>
);
}
}
const styles = StyleSheet.create({
boton: {
paddingVertical: 12,
paddingHorizontal: 24,
borderRadius: 8,
alignItems: 'center',
justifyContent: 'center',
margin: 10,
},
botonPresionado: {
transform: [{scale: 0.95}],
},
texto: {
fontSize: 16,
fontWeight: '600',
color: 'white',
},
});
export default BotonClase;
3.2 Usando Ambos Tipos de Componentes
tsx
// App.tsx - Ejemplo completo
import React, {useState} from 'react';
import {SafeAreaView, Text, View, ScrollView} from 'react-native';
import BotonConEvento from './src/components/BotonConEvento/BotonConEvento';
import BotonClase from './src/components/BotonClase/BotonClase';
function App(): JSX.Element {
const [contadorFuncional, setContadorFuncional] = useState(0);
const [contadorClase, setContadorClase] = useState(0);
const [mensaje, setMensaje] = useState('Presiona un botón');
const manejarEventoFuncional = () => {
const nuevoContador = contadorFuncional + 1;
setContadorFuncional(nuevoContador);
setMensaje(`Functional Component: ${nuevoContador} clics`);
console.log('Evento desde Functional Component');
};
const manejarEventoClase = () => {
const nuevoContador = contadorClase + 1;
setContadorClase(nuevoContador);
setMensaje(`Class Component: ${nuevoContador} clics`);
console.log('Evento desde Class Component');
};
return (
<SafeAreaView style={{flex: 1}}>
<ScrollView contentContainerStyle={{padding: 20}}>
<Text style={styles.titulo}>Manejo de Eventos en React Native</Text>
<View style={styles.seccion}>
<Text style={styles.subtitulo}>Functional Component</Text>
<BotonConEvento
titulo="Presióname (Functional)"
onPress={manejarEventoFuncional}
colorFondo="#007AFF"
/>
<Text style={styles.contador}>
Clics: {contadorFuncional}
</Text>
</View>
<View style={styles.seccion}>
<Text style={styles.subtitulo}>Class Component</Text>
<BotonClase
titulo="Presióname (Class)"
onPress={manejarEventoClase}
colorFondo="#FF9500"
/>
<Text style={styles.contador}>
Clics: {contadorClase}
</Text>
</View>
<View style={styles.mensajeContainer}>
<Text style={styles.mensaje}>{mensaje}</Text>
</View>
{/* Ejemplos adicionales */}
<View style={styles.seccion}>
<Text style={styles.subtitulo}>Más Ejemplos</Text>
<BotonConEvento
titulo="Alerta Simple"
onPress={() => alert('¡Hola desde React Native!')}
colorFondo="#34C759"
/>
<BotonConEvento
titulo="Cambiar Tema"
onPress={() => {
console.log('Cambiando tema...');
// Lógica para cambiar tema
}}
colorFondo="#5856D6"
/>
<BotonConEvento
titulo="Navegar"
onPress={() => {
console.log('Navegando a otra pantalla...');
// Lógica de navegación
}}
colorFondo="#AF52DE"
/>
</View>
</ScrollView>
</SafeAreaView>
);
}
const styles = {
titulo: {
fontSize: 28,
fontWeight: 'bold',
textAlign: 'center',
marginBottom: 30,
color: '#1D1D1F',
},
subtitulo: {
fontSize: 18,
fontWeight: '600',
marginBottom: 10,
color: '#424245',
},
seccion: {
marginBottom: 30,
padding: 20,
backgroundColor: '#F5F5F7',
borderRadius: 12,
},
contador: {
fontSize: 16,
textAlign: 'center',
marginTop: 10,
color: '#6E6E73',
},
mensajeContainer: {
padding: 20,
backgroundColor: '#E8F4FF',
borderRadius: 12,
marginBottom: 30,
},
mensaje: {
fontSize: 16,
textAlign: 'center',
color: '#007AFF',
},
};
export default App;
🎨 Módulo 4: Tipos de Eventos y Componentes
4.1 Eventos Disponibles en Diferentes Componentes
tsx
// src/components/ComponenteConMultiplesEventos/ComponenteConMultiplesEventos.tsx
import React, {useState} from 'react';
import {
View,
Text,
TextInput,
TouchableOpacity,
ScrollView,
Switch,
StyleSheet,
} from 'react-native';
function ComponenteConMultiplesEventos(): JSX.Element {
const [texto, setTexto] = useState('');
const [esActivo, setEsActivo] = useState(false);
const [posicionScroll, setPosicionScroll] = useState(0);
return (
<View style={styles.container}>
{/* 1. Evento onPress (TouchableOpacity) */}
<TouchableOpacity
style={styles.boton}
onPress={() => console.log('onPress ejecutado')}
onLongPress={() => console.log('onLongPress ejecutado (presión larga)')}
onPressIn={() => console.log('onPressIn: touch comenzó')}
onPressOut={() => console.log('onPressOut: touch terminó')}
>
<Text style={styles.textoBoton}>Presióname</Text>
</TouchableOpacity>
{/* 2. Eventos de TextInput */}
<TextInput
style={styles.input}
placeholder="Escribe algo..."
value={texto}
onChangeText={nuevoTexto => {
console.log(`Texto cambiado: ${nuevoTexto}`);
setTexto(nuevoTexto);
}}
onSubmitEditing={() => console.log('onSubmitEditing: Enter presionado')}
onFocus={() => console.log('onFocus: input enfocado')}
onBlur={() => console.log('onBlur: input perdió foco')}
onKeyPress={event => {
console.log(`Tecla presionada: ${event.nativeEvent.key}`);
}}
/>
{/* 3. Eventos de ScrollView */}
<ScrollView
style={styles.scrollView}
onScroll={event => {
const y = event.nativeEvent.contentOffset.y;
setPosicionScroll(y);
console.log(`Scroll position: ${y}`);
}}
onMomentumScrollBegin={() => console.log('Scroll comenzó con momentum')}
onMomentumScrollEnd={() => console.log('Scroll terminó con momentum')}
onScrollBeginDrag={() => console.log('Usuario comenzó a arrastrar')}
onScrollEndDrag={() => console.log('Usuario terminó de arrastrar')}
>
<View style={styles.scrollContent}>
<Text>Desliza hacia abajo</Text>
<Text>Posición: {posicionScroll.toFixed(0)}</Text>
{/* Contenido largo para hacer scroll */}
{Array.from({length: 20}).map((_, i) => (
<Text key={i}>Elemento {i + 1}</Text>
))}
</View>
</ScrollView>
{/* 4. Eventos de Switch */}
<Switch
value={esActivo}
onValueChange={nuevoValor => {
console.log(`Switch cambiado a: ${nuevoValor}`);
setEsActivo(nuevoValor);
}}
trackColor={{false: '#767577', true: '#34C759'}}
/>
{/* 5. Eventos personalizados con Pressable (Moderno) */}
<Pressable
style={({pressed}) => [
styles.pressable,
pressed && styles.pressablePresionado,
]}
onPress={() => console.log('Pressable: onPress')}
onPressIn={() => console.log('Pressable: onPressIn')}
onPressOut={() => console.log('Pressable: onPressOut')}
onLongPress={() => console.log('Pressable: onLongPress')}
>
{({pressed}) => (
<Text style={styles.textoPressable}>
{pressed ? '¡Presionado!' : 'Presionable'}
</Text>
)}
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
container: {
padding: 20,
},
boton: {
backgroundColor: '#007AFF',
padding: 15,
borderRadius: 8,
alignItems: 'center',
marginBottom: 20,
},
textoBoton: {
color: 'white',
fontSize: 16,
fontWeight: '600',
},
input: {
borderWidth: 1,
borderColor: '#C7C7CC',
borderRadius: 8,
padding: 12,
fontSize: 16,
marginBottom: 20,
},
scrollView: {
height: 200,
borderWidth: 1,
borderColor: '#C7C7CC',
borderRadius: 8,
marginBottom: 20,
},
scrollContent: {
padding: 20,
},
pressable: {
backgroundColor: '#5856D6',
padding: 15,
borderRadius: 8,
alignItems: 'center',
},
pressablePresionado: {
backgroundColor: '#4A49B5',
transform: [{scale: 0.95}],
},
textoPressable: {
color: 'white',
fontSize: 16,
fontWeight: '600',
},
});
export default ComponenteConMultiplesEventos;
🔧 Módulo 5: Patrones Avanzados de Manejo de Eventos
5.1 Pasar Parámetros a Event Handlers
tsx
// src/components/ListaInteractiva/ListaInteractiva.tsx
import React from 'react';
import {
View,
Text,
TouchableOpacity,
FlatList,
StyleSheet,
} from 'react-native';
interface Item {
id: string;
nombre: string;
descripcion: string;
}
function ListaInteractiva(): JSX.Element {
const datos: Item[] = [
{id: '1', nombre: 'Manzana', descripcion: 'Fruta roja'},
{id: '2', nombre: 'Banana', descripcion: 'Fruta amarilla'},
{id: '3', nombre: 'Naranja', descripcion: 'Fruta cítrica'},
{id: '4', nombre: 'Uva', descripcion: 'Fruta en racimos'},
];
// Manejador que recibe parámetros
const manejarSeleccionItem = (item: Item, index: number) => {
console.log(`Item seleccionado:`, {
id: item.id,
nombre: item.nombre,
indice: index,
timestamp: new Date().toISOString(),
});
alert(`Seleccionaste: ${item.nombre}\n${item.descripcion}`);
};
// Renderizar cada item
const renderItem = ({item, index}: {item: Item; index: number}) => (
<TouchableOpacity
style={styles.item}
onPress={() => manejarSeleccionItem(item, index)} // ✅ Pasar parámetros
onLongPress={() => {
console.log(`Long press en: ${item.nombre}`);
alert(`Mantenido presionado: ${item.nombre}`);
}}
>
<Text style={styles.nombre}>{item.nombre}</Text>
<Text style={styles.descripcion}>{item.descripcion}</Text>
</TouchableOpacity>
);
return (
<View style={styles.container}>
<Text style={styles.titulo}>Lista Interactiva</Text>
<FlatList
data={datos}
renderItem={renderItem}
keyExtractor={item => item.id}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
},
titulo: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 20,
},
item: {
backgroundColor: 'white',
padding: 20,
marginBottom: 10,
borderRadius: 10,
shadowColor: '#000',
shadowOffset: {width: 0, height: 2},
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
nombre: {
fontSize: 18,
fontWeight: '600',
marginBottom: 5,
},
descripcion: {
fontSize: 14,
color: '#666',
},
});
export default ListaInteractiva;
5.2 Eventos con Callbacks Anidados
tsx
// src/components/FormularioInteractivo/FormularioInteractivo.tsx
import React, {useState} from 'react';
import {
View,
Text,
TextInput,
TouchableOpacity,
Alert,
StyleSheet,
} from 'react-native';
interface FormularioInteractivoProps {
onSubmit: (datos: {
nombre: string;
email: string;
mensaje: string;
}) => void;
onCancel: () => void;
}
function FormularioInteractivo({
onSubmit,
onCancel,
}: FormularioInteractivoProps): JSX.Element {
const [nombre, setNombre] = useState('');
const [email, setEmail] = useState('');
const [mensaje, setMensaje] = useState('');
const manejarEnvio = () => {
// Validaciones
if (!nombre.trim()) {
Alert.alert('Error', 'Por favor ingresa tu nombre');
return;
}
if (!email.trim() || !email.includes('@')) {
Alert.alert('Error', 'Por favor ingresa un email válido');
return;
}
// Preparar datos
const datosFormulario = {
nombre: nombre.trim(),
email: email.trim(),
mensaje: mensaje.trim(),
};
// Ejecutar callback del padre
onSubmit(datosFormulario);
// Limpiar formulario
setNombre('');
setEmail('');
setMensaje('');
console.log('Formulario enviado:', datosFormulario);
};
const manejarCancelar = () => {
Alert.alert(
'Confirmar',
'¿Estás seguro de que quieres cancelar?',
[
{text: 'No', style: 'cancel'},
{
text: 'Sí',
onPress: () => {
console.log('Formulario cancelado');
onCancel(); // Ejecutar callback del padre
},
},
],
);
};
return (
<View style={styles.container}>
<Text style={styles.titulo}>Formulario de Contacto</Text>
<TextInput
style={styles.input}
placeholder="Nombre completo"
value={nombre}
onChangeText={setNombre}
onFocus={() => console.log('Campo nombre enfocado')}
/>
<TextInput
style={styles.input}
placeholder="Email"
value={email}
onChangeText={setEmail}
keyboardType="email-address"
autoCapitalize="none"
onFocus={() => console.log('Campo email enfocado')}
/>
<TextInput
style={[styles.input, styles.textArea]}
placeholder="Mensaje"
value={mensaje}
onChangeText={setMensaje}
multiline
numberOfLines={4}
onFocus={() => console.log('Campo mensaje enfocado')}
/>
<View style={styles.botonesContainer}>
<TouchableOpacity
style={[styles.boton, styles.botonCancelar]}
onPress={manejarCancelar}
onLongPress={() =>
console.log('Botón cancelar mantenido presionado')
}
>
<Text style={styles.textoBoton}>Cancelar</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.boton, styles.botonEnviar]}
onPress={manejarEnvio}
onPressIn={() => console.log('Botón enviar: press in')}
onPressOut={() => console.log('Botón enviar: press out')}
>
<Text style={styles.textoBoton}>Enviar</Text>
</TouchableOpacity>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
padding: 20,
backgroundColor: 'white',
borderRadius: 12,
shadowColor: '#000',
shadowOffset: {width: 0, height: 2},
shadowOpacity: 0.1,
shadowRadius: 8,
elevation: 5,
},
titulo: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 20,
textAlign: 'center',
},
input: {
borderWidth: 1,
borderColor: '#E5E5EA',
borderRadius: 8,
padding: 12,
fontSize: 16,
marginBottom: 15,
},
textArea: {
height: 100,
textAlignVertical: 'top',
},
botonesContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
marginTop: 20,
},
boton: {
flex: 1,
paddingVertical: 15,
borderRadius: 8,
alignItems: 'center',
marginHorizontal: 5,
},
botonCancelar: {
backgroundColor: '#FF3B30',
},
botonEnviar: {
backgroundColor: '#34C759',
},
textoBoton: {
color: 'white',
fontSize: 16,
fontWeight: '600',
},
});
export default FormularioInteractivo;
5.3 Uso del Formulario en App.tsx
tsx
// App.tsx - Integración del formulario
import React, {useState} from 'react';
import {SafeAreaView, ScrollView, Text, View} from 'react-native';
import FormularioInteractivo from './src/components/FormularioInteractivo/FormularioInteractivo';
function App(): JSX.Element {
const [historial, setHistorial] = useState<Array<{
nombre: string;
email: string;
mensaje: string;
fecha: string;
}>>([]);
const manejarEnvioFormulario = (datos: {
nombre: string;
email: string;
mensaje: string;
}) => {
const nuevoRegistro = {
...datos,
fecha: new Date().toLocaleString(),
};
setHistorial([nuevoRegistro, ...historial]);
Alert.alert(
'¡Éxito!',
`Formulario enviado por ${datos.nombre}`,
[{text: 'OK'}],
);
};
const manejarCancelarFormulario = () => {
console.log('Formulario cancelado desde App');
// Puedes agregar más lógica aquí
};
return (
<SafeAreaView style={{flex: 1}}>
<ScrollView contentContainerStyle={{padding: 20}}>
<Text style={styles.titulo}>Formulario Interactivo</Text>
<FormularioInteractivo
onSubmit={manejarEnvioFormulario}
onCancel={manejarCancelarFormulario}
/>
{historial.length > 0 && (
<View style={styles.historial}>
<Text style={styles.subtitulo}>Historial de Envíos</Text>
{historial.map((item, index) => (
<View key={index} style={styles.registro}>
<Text style={styles.registroNombre}>{item.nombre}</Text>
<Text style={styles.registroEmail}>{item.email}</Text>
<Text style={styles.registroFecha}>{item.fecha}</Text>
</View>
))}
</View>
)}
</ScrollView>
</SafeAreaView>
);
}
const styles = {
titulo: {
fontSize: 28,
fontWeight: 'bold',
marginBottom: 30,
textAlign: 'center',
},
historial: {
marginTop: 30,
padding: 20,
backgroundColor: '#F5F5F7',
borderRadius: 12,
},
subtitulo: {
fontSize: 20,
fontWeight: '600',
marginBottom: 15,
},
registro: {
backgroundColor: 'white',
padding: 15,
borderRadius: 8,
marginBottom: 10,
},
registroNombre: {
fontSize: 16,
fontWeight: 'bold',
},
registroEmail: {
fontSize: 14,
color: '#666',
},
registroFecha: {
fontSize: 12,
color: '#999',
fontStyle: 'italic',
},
};
export default App;
🎯 Módulo 6: Mejores Prácticas y Patrones
6.1 Patrones Recomendados para Eventos
tsx
// src/components/PatronesEventos/PatronesEventos.tsx
import React from 'react';
import {
View,
Text,
TouchableOpacity,
Alert,
StyleSheet,
} from 'react-native';
function PatronesEventos(): JSX.Element {
// ✅ PATRÓN 1: Función nombrada (mejor para debugging)
const manejarPresionNombrada = () => {
console.log('Función nombrada ejecutada');
Alert.alert('Éxito', 'Función nombrada llamada');
};
// ✅ PATRÓN 2: Arrow function para parámetros
const crearManejadorConParametro = (parametro: string) => () => {
console.log(`Parámetro recibido: ${parametro}`);
};
// ✅ PATRÓN 3: Event handler con validación
const manejarConValidacion = (dato: string) => {
if (!dato.trim()) {
console.warn('Dato vacío recibido');
return;
}
console.log(`Dato válido: ${dato}`);
};
// ✅ PATRÓN 4: Event handler que retorna otra función
const crearLogger = (contexto: string) => {
return () => {
console.log(`Evento desde: ${contexto}`, {
hora: new Date().toISOString(),
contexto,
});
};
};
// ❌ PATRÓN A EVITAR: Inline function compleja
const patronMalo = () => {
return (
<TouchableOpacity
onPress={() => {
// ❌ Demasiada lógica inline
const resultado = Math.random() * 100;
console.log(resultado);
if (resultado > 50) {
Alert.alert('Mayor a 50');
} else {
Alert.alert('Menor a 50');
}
}}
>
<Text>Patrón a evitar</Text>
</TouchableOpacity>
);
};
return (
<View style={styles.container}>
<Text style={styles.titulo}>Patrones de Manejo de Eventos</Text>
{/* Patrón 1: Función nombrada */}
<TouchableOpacity
style={styles.boton}
onPress={manejarPresionNombrada}
>
<Text style={styles.textoBoton}>Función Nombrada</Text>
</TouchableOpacity>
{/* Patrón 2: Arrow function con parámetro */}
<TouchableOpacity
style={styles.boton}
onPress={crearManejadorConParametro('miParametro')}
>
<Text style={styles.textoBoton}>Con Parámetro Predefinido</Text>
</TouchableOpacity>
{/* Patrón 3: Con validación */}
<TouchableOpacity
style={styles.boton}
onPress={() => manejarConValidacion('Dato de prueba')}
>
<Text style={styles.textoBoton}>Con Validación</Text>
</TouchableOpacity>
{/* Patrón 4: Factory function */}
<TouchableOpacity
style={styles.boton}
onPress={crearLogger('componentePatrones')}
>
<Text style={styles.textoBoton}>Factory Function</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
container: {
padding: 20,
},
titulo: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 20,
},
boton: {
backgroundColor: '#5856D6',
padding: 15,
borderRadius: 8,
alignItems: 'center',
marginBottom: 15,
},
textoBoton: {
color: 'white',
fontSize: 16,
fontWeight: '600',
},
});
6.2 Reglas de Oro para Manejo de Eventos
Separa la lógica de los handlers de la definición del componente
Usa funciones nombradas para mejor debugging
Evita lógica compleja inline en los handlers
Tipa correctamente los handlers con TypeScript
Maneja errores dentro de los event handlers
Considera performance con handlers en listas grandes
Usa debouncing/throttling para eventos frecuentes
Limpia event listeners en Class Components
📊 Módulo 7: Ejercicios Prácticos
Ejercicio 1: Contador Interactivo
Crea un componente de contador con:
Botón para incrementar
Botón para decrementar
Botón para resetear
Display que muestre el valor actual
Eventos para cada acción
Ejercicio 2: Lista de Tareas
Crea una lista de tareas interactiva con:
Añadir nueva tarea (TextInput + Botón)
Marcar tarea como completada (onPress)
Eliminar tarea (onLongPress o swipe)
Editar tarea (doble tap)
Ejercicio 3: Galería de Imágenes
Crea una galería con:
Miniaturas que se pueden presionar
Vista ampliada al presionar una imagen
Botones de navegación (anterior/siguiente)
Evento para cerrar la vista ampliada
🎓 Evaluación Final
Preguntas de Conocimiento:
¿Cuál es la diferencia entre onPress y onLongPress?
¿Cómo pasarías parámetros a un event handler?
¿Cuándo usarías TouchableOpacity vs Pressable?
¿Cómo manejarías eventos en listas grandes para optimizar performance?
¿Qué es el event bubbling y cómo funciona en React Native?
Proyecto Final:
Crea una aplicación de notas con:
Crear nueva nota (con título y contenido)
Listar todas las notas
Editar nota existente
Eliminar nota (con confirmación)
Buscar notas por texto
Cada acción debe usar eventos apropiados
🎉 Conclusión del Curso
Has aprendido a dominar los eventos en React Native:
✅ Fundamentos de eventos y handlers
✅ Componentes táctiles (TouchableOpacity, Pressable)
✅ Manejo de eventos en Functional y Class Components
✅ Tipos de eventos (onPress, onChangeText, onScroll, etc.)
✅ Patrones avanzados y mejores prácticas
Comentarios
Publicar un comentario