21. Props
Tutorial: Props (Propiedades) en React Native
En este tutorial aprenderás a usar Props (propiedades) en React Native, un concepto fundamental para pasar datos entre componentes y hacerlos dinámicos y reutilizables.
🎯 Objetivo del Tutorial
Comprender cómo definir y usar Props para crear componentes dinámicos y reutilizables en React Native.
📚 ¿Qué son las Props?
Props (abreviatura de "properties") son datos que se pasan de un componente padre a un componente hijo. Son inmutables (no se pueden modificar en el componente hijo) y permiten la comunicación entre componentes.
🔧 Parte 1: Props en Functional Components
1.1 Crear una interfaz para las Props
tsx
// src/components/MiPrimerComponente/MiPrimerComponente.tsx
import React from 'react';
import {ColorValue, StyleSheet, View, ViewStyle} from 'react-native';
// Definir interfaz para las Props
interface MiPrimerComponenteProps {
color: ColorValue; // Tipo específico para colores en React Native
}
function MiPrimerComponente(props: MiPrimerComponenteProps): JSX.Element {
// Desestructurar props para acceder fácilmente a sus valores
const {color} = props;
return (
<View
style={[
styles.mainComponent,
{
backgroundColor: color, // Usar el color de las props
},
]}
/>
);
}
const styles = StyleSheet.create({
mainComponent: {
width: 50,
height: 50,
borderRadius: 25,
borderColor: 'green',
borderWidth: 5,
} as ViewStyle,
});
export default MiPrimerComponente;
1.2 Desestructuración directa (forma más común)
tsx
// Desestructurar directamente en los parámetros
function MiPrimerComponente({color}: MiPrimerComponenteProps): JSX.Element {
return (
<View
style={[
styles.mainComponent,
{backgroundColor: color}, // Estilo inline dinámico
]}
/>
);
}
1.3 Props con valores por defecto
tsx
interface MiPrimerComponenteProps {
color?: ColorValue; // Prop opcional con ?
tamaño?: number;
}
// Valores por defecto usando desestructuración
function MiPrimerComponente({
color = 'red', // Valor por defecto
tamaño = 50, // Valor por defecto
}: MiPrimerComponenteProps): JSX.Element {
return (
<View
style={[
styles.mainComponent,
{
backgroundColor: color,
width: tamaño,
height: tamaño,
borderRadius: tamaño / 2,
},
]}
/>
);
}
🏗️ Parte 2: Props en Class Components
2.1 Definir Props en una clase
tsx
// src/components/MiPrimerComponenteClass/MiPrimerComponenteClass.tsx
import React from 'react';
import {ColorValue, StyleSheet, View, ViewStyle} from 'react-native';
// Definir interfaz para las Props
interface MiPrimerComponenteClassProps {
color: ColorValue;
}
// Especificar tipos de Props y State en los genéricos
// React.Component<Props, State, Snapshot>
class MiPrimerComponenteClass extends React.Component<MiPrimerComponenteClassProps> {
render(): React.ReactNode {
// Desestructurar props usando this.props
const {color} = this.props;
return (
<View
style={[
styles.mainComponent,
{
backgroundColor: color, // Usar color de las props
},
]}
/>
);
}
}
const styles = StyleSheet.create({
mainComponent: {
width: 50,
height: 50,
borderRadius: 25,
borderColor: 'green',
borderWidth: 5,
} as ViewStyle,
});
export default MiPrimerComponenteClass;
2.2 Class Component con Props y State
tsx
interface ComponenteProps {
color: ColorValue;
tamañoInicial?: number;
}
interface ComponenteState {
tamañoActual: number;
}
class MiComponente extends React.Component<ComponenteProps, ComponenteState> {
constructor(props: ComponenteProps) {
super(props);
this.state = {
tamañoActual: props.tamañoInicial || 50,
};
}
render() {
const {color} = this.props;
const {tamañoActual} = this.state;
return (
<View
style={{
backgroundColor: color,
width: tamañoActual,
height: tamañoActual,
}}
/>
);
}
}
📱 Parte 3: Usar Componentes con Props
3.1 App.tsx usando Props
tsx
// App.tsx
import React from 'react';
import {SafeAreaView} from 'react-native';
import MiPrimerComponente from './src/components/MiPrimerComponente/MiPrimerComponente';
import MiPrimerComponenteClass from './src/components/MiPrimerComponenteClass/MiPrimerComponenteClass';
function App(): JSX.Element {
return (
<SafeAreaView style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
{/* Pasar props como atributos HTML */}
<MiPrimerComponente color="blue" />
<MiPrimerComponenteClass color="pink" />
{/* Reutilizar con diferentes props */}
<MiPrimerComponente color="red" />
<MiPrimerComponente color="green" />
<MiPrimerComponente color="orange" />
<MiPrimerComponente color="purple" />
</SafeAreaView>
);
}
export default App;
3.2 Sintaxis para pasar Props
tsx
// Strings (comillas dobles o simples)
<MiComponente color="red" texto="Hola" />
// Números (sin comillas)
<MiComponente tamaño={100} cantidad={5} />
// Booleanos (sin comillas)
<MiComponente activo={true} visible={false} />
// Arrays
<MiComponente items={[1, 2, 3]} />
// Objetos
<MiComponente config={{tamaño: 100, color: 'red'}} />
// Funciones
<MiComponente onPress={() => console.log('Presionado')} />
// Variables
const colorFavorito = 'blue';
<MiComponente color={colorFavorito} />
🎨 Parte 4: Props Avanzadas
4.1 Children Prop (contenido anidado)
tsx
interface TarjetaProps {
titulo: string;
children: React.ReactNode; // Para contenido anidado
}
function Tarjeta({titulo, children}: TarjetaProps): JSX.Element {
return (
<View style={styles.tarjeta}>
<Text style={styles.titulo}>{titulo}</Text>
{/* Renderizar contenido anidado */}
{children}
</View>
);
}
// Uso con children
<Tarjeta titulo="Mi Tarjeta">
<Text>Contenido de la tarjeta</Text>
<Button title="Presionar" />
</Tarjeta>
4.2 Props con tipos complejos
tsx
import {ImageSourcePropType} from 'react-native';
interface PerfilProps {
// Tipos específicos de React Native
imagen: ImageSourcePropType;
color: ColorValue;
estilo?: ViewStyle;
// Tipos TypeScript estándar
nombre: string;
edad: number;
activo: boolean;
habilidades: string[];
config: {
mostrarEmail: boolean;
tema: 'claro' | 'oscuro';
};
// Funciones con tipado específico
onPress: () => void;
onChange: (nuevoValor: string) => void;
}
function Perfil(props: PerfilProps): JSX.Element {
// Implementación...
}
4.3 Props de estilo dinámico
tsx
interface BotonProps {
texto: string;
tipo?: 'primario' | 'secundario' | 'peligro';
deshabilitado?: boolean;
}
function Boton({texto, tipo = 'primario', deshabilitado = false}: BotonProps): JSX.Element {
// Determinar estilo basado en props
const colorFondo = {
primario: '#007AFF',
secundario: '#5856D6',
peligro: '#FF3B30',
}[tipo];
return (
<View
style={[
styles.boton,
{
backgroundColor: colorFondo,
opacity: deshabilitado ? 0.5 : 1,
},
]}
>
<Text style={styles.texto}>{texto}</Text>
</View>
);
}
🔄 Parte 5: Combinar Múltiples Estilos con Props
5.1 Estilos condicionales con Props
tsx
interface CajaProps {
color?: ColorValue;
tamaño?: number;
redondeada?: boolean;
conBorde?: boolean;
conSombra?: boolean;
}
function Caja({
color = 'white',
tamaño = 100,
redondeada = false,
conBorde = false,
conSombra = false,
}: CajaProps): JSX.Element {
return (
<View
style={[
// Estilo base
{width: tamaño, height: tamaño, backgroundColor: color},
// Estilos condicionales
redondeada && {borderRadius: tamaño / 4},
conBorde && {borderWidth: 2, borderColor: '#000'},
conSombra && {
shadowColor: '#000',
shadowOffset: {width: 0, height: 2},
shadowOpacity: 0.25,
shadowRadius: 3.84,
elevation: 5,
},
]}
/>
);
}
// Uso con diferentes combinaciones
<Caja color="blue" tamaño={80} redondeada conSombra />
<Caja color="red" tamaño={120} conBorde />
<Caja color="green" tamaño={60} redondeada conBorde conSombra />
5.2 Props para estilos completos
tsx
interface ComponenteFlexibleProps {
estiloContenedor?: ViewStyle;
estiloTexto?: TextStyle;
contenido: string;
}
function ComponenteFlexible({
estiloContenedor,
estiloTexto,
contenido,
}: ComponenteFlexibleProps): JSX.Element {
return (
<View style={[styles.contenedorBase, estiloContenedor]}>
<Text style={[styles.textoBase, estiloTexto]}>{contenido}</Text>
</View>
);
}
// Pasar estilos completos como props
<ComponenteFlexible
contenido="Hola Mundo"
estiloContenedor={{padding: 30, backgroundColor: '#f0f0f0'}}
estiloTexto={{fontSize: 24, color: 'red', fontWeight: 'bold'}}
/>
🧪 Parte 6: Ejercicio Práctico - Componente Botón Reutilizable
6.1 Crear un botón con múltiples props
tsx
// src/components/Boton/Boton.tsx
import React from 'react';
import {
TouchableOpacity,
Text,
StyleSheet,
ViewStyle,
TextStyle,
ColorValue,
} from 'react-native';
interface BotonProps {
// Props básicas
titulo: string;
onPress: () => void;
// Props de estilo
colorFondo?: ColorValue;
colorTexto?: ColorValue;
estiloPersonalizado?: ViewStyle;
textoEstilo?: TextStyle;
// Props de comportamiento
deshabilitado?: boolean;
loading?: boolean;
// Props de variante
tipo?: 'primario' | 'secundario' | 'outline' | 'text';
tamaño?: 'pequeño' | 'medio' | 'grande';
}
function Boton({
titulo,
onPress,
colorFondo,
colorTexto = 'white',
estiloPersonalizado,
textoEstilo,
deshabilitado = false,
loading = false,
tipo = 'primario',
tamaño = 'medio',
}: BotonProps): JSX.Element {
// Determinar colores basados en el tipo
const colorPorTipo = {
primario: '#007AFF',
secundario: '#5856D6',
outline: 'transparent',
text: 'transparent',
}[tipo];
const fondoFinal = colorFondo || colorPorTipo;
// Determinar tamaño
const paddingPorTamaño = {
pequeño: {paddingVertical: 8, paddingHorizontal: 16},
medio: {paddingVertical: 12, paddingHorizontal: 24},
grande: {paddingVertical: 16, paddingHorizontal: 32},
}[tamaño];
// Determinar si tiene borde
const tieneBorde = tipo === 'outline';
return (
<TouchableOpacity
style={[
styles.boton,
paddingPorTamaño,
{
backgroundColor: fondoFinal,
borderWidth: tieneBorde ? 2 : 0,
borderColor: tipo === 'outline' ? colorPorTipo : undefined,
opacity: deshabilitado || loading ? 0.6 : 1,
},
estiloPersonalizado,
]}
onPress={onPress}
disabled={deshabilitado || loading}
activeOpacity={0.8}
>
<Text
style={[
styles.texto,
{
color: tipo === 'outline' || tipo === 'text'
? colorPorTipo
: colorTexto,
},
textoEstilo,
]}
>
{loading ? 'Cargando...' : titulo}
</Text>
</TouchableOpacity>
);
}
const styles = StyleSheet.create({
boton: {
borderRadius: 8,
alignItems: 'center',
justifyContent: 'center',
},
texto: {
fontSize: 16,
fontWeight: '600',
},
});
export default Boton;
6.2 Usar el botón con diferentes props
tsx
// En App.tsx
import Boton from './src/components/Boton/Boton';
function App(): JSX.Element {
const manejarPresion = () => {
console.log('Botón presionado!');
};
return (
<SafeAreaView style={{padding: 20}}>
{/* Botón primario */}
<Boton
titulo="Guardar"
onPress={manejarPresion}
tipo="primario"
/>
{/* Botón secundario */}
<Boton
titulo="Cancelar"
onPress={manejarPresion}
tipo="secundario"
colorTexto="white"
/>
{/* Botón outline */}
<Boton
titulo="Editar"
onPress={manejarPresion}
tipo="outline"
colorFondo="transparent"
/>
{/* Botón deshabilitado */}
<Boton
titulo="Deshabilitado"
onPress={manejarPresion}
deshabilitado
/>
{/* Botón con estilo personalizado */}
<Boton
titulo="Personalizado"
onPress={manejarPresion}
estiloPersonalizado={{
marginTop: 20,
borderRadius: 20,
backgroundColor: 'purple',
}}
textoEstilo={{
fontSize: 18,
fontStyle: 'italic',
}}
/>
</SafeAreaView>
);
}
⚠️ Parte 7: Errores Comunes y Soluciones
7.1 Error: "Property does not exist on type"
tsx
// ❌ INCORRECTO
<MiComponente colorr="red" /> // Typo en el nombre de la prop
// ✅ CORRECTO
<MiComponente color="red" />
7.2 Error: TypeScript - tipo incorrecto
tsx
// ❌ INCORRECTO - string donde se espera number
<MiComponente tamaño="100" />
// ✅ CORRECTO - number sin comillas
<MiComponente tamaño={100} />
7.3 Error: Props requeridas faltantes
tsx
interface Props {
titulo: string; // Requerida (sin ?)
subtitulo?: string; // Opcional (con ?)
}
// ❌ INCORRECTO - falta prop requerida
<MiComponente />
// ✅ CORRECTO - incluye prop requerida
<MiComponente titulo="Hola" />
7.4 Solución: Prop spreading (esparcir props)
tsx
interface InputProps {
placeholder: string;
value: string;
onChangeText: (text: string) => void;
[key: string]: any; // Permite props adicionales
}
function Input(props: InputProps): JSX.Element {
return (
<TextInput
{...props} // Esparcir todas las props al TextInput
style={styles.input}
/>
);
}
// Uso con props adicionales
<Input
placeholder="Escribe algo"
value={texto}
onChangeText={setTexto}
keyboardType="email-address" // Prop adicional
autoCapitalize="none" // Prop adicional
/>
✅ Parte 8: Buenas Prácticas con Props
8.1 Nomenclatura consistente
tsx
// Usar camelCase para props
<Componente
colorFondo="blue" // ✅ camelCase
tamañoTexto={16} // ✅ camelCase
onPressHandler={() => {}} // ✅ prefijo 'on' para eventos
/>
8.2 Documentación con JSDoc
tsx
interface BotonProps {
/**
* Texto que se muestra en el botón
* @default 'Presionar'
*/
titulo?: string;
/**
* Color de fondo del botón
* @default '#007AFF'
*/
color?: ColorValue;
/**
* Callback que se ejecuta al presionar el botón
*/
onPress: () => void;
}
8.3 Props para temas y configuraciones globales
tsx
// src/context/ThemeContext.tsx
import React, {createContext, useContext} from 'react';
interface Theme {
colores: {
primario: string;
secundario: string;
fondo: string;
texto: string;
};
espaciado: {
pequeño: number;
medio: number;
grande: number;
};
}
const ThemeContext = createContext<Theme>(defaultTheme);
// Componente que usa el tema
function BotonConTema(): JSX.Element {
const tema = useContext(ThemeContext);
return (
<TouchableOpacity
style={{backgroundColor: tema.colores.primario}}
>
<Text style={{color: tema.colores.texto}}>Botón</Text>
</TouchableOpacity>
);
}
📊 Parte 9: Props vs Children - ¿Cuándo usar cada uno?
Usar Props cuando:
✅ Datos simples: strings, numbers, booleans
✅ Configuración: opciones, banderas, modos
✅ Eventos: callbacks, handlers
✅ Estilos: colores, tamaños, variantes
Usar Children cuando:
✅ Contenido complejo: múltiples elementos
✅ Layouts: contenedores genéricos
✅ Composición: anidamiento de componentes
✅ Renderizado condicional: contenido dinámico
tsx
// Ejemplo: Tarjeta que acepta children
function Tarjeta({titulo, children}: {titulo: string; children: React.ReactNode}) {
return (
<View style={styles.tarjeta}>
<Text style={styles.titulo}>{titulo}</Text>
{children}
</View>
);
}
// Uso con children
<Tarjeta titulo="Perfil">
<Text>Nombre: Juan</Text>
<Text>Edad: 25</Text>
<Boton titulo="Editar" />
</Tarjeta>
🎉 ¡Felicidades!
Has aprendido a usar Props en React Native:
✅ Definir Props con interfaces TypeScript
✅ Usar Props en Functional y Class Components
✅ Pasar diferentes tipos de datos como props
✅ Crear componentes reutilizables con props dinámicas
✅ Combinar estilos usando props
✅ Evitar errores comunes con props
✅ Seguir buenas prácticas de diseño
📚 Recursos Adicionales
¡Excelente trabajo! Ahora puedes crear componentes altamente reutilizables y configurables. En el próximo tutorial aprenderás sobre State (estado) para crear componentes interactivos
Comentarios
Publicar un comentario