20. Formas de crear Componentes

 

Tutorial: Formas de Crear Componentes en React Native

En este tutorial aprenderás las dos formas principales de crear componentes en React Native: Functional Components y Class Components, y cuándo usar cada una.


🎯 Objetivo del Tutorial

Comprender las dos formas de crear componentes en React Native, sus diferencias, ventajas y casos de uso.


📊 Comparación de las Dos Formas

Característica

Functional Component

Class Component

Sintaxis

Función JavaScript

Clase ES6

Estado

Hooks (useState)

this.state

Ciclo de vida

Hooks (useEffect)

Métodos (componentDidMount)

this

No usa

Usa this

Performance

Ligero

Más pesado

Tendencia actual

✅ Recomendado

⚠️ Legacy


🛠️ Parte 1: Functional Components

Ejemplo Básico:

tsx

// src/components/MiPrimerComponente/MiPrimerComponente.tsx

import React from 'react';

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


// Functional Component

function MiPrimerComponente(): JSX.Element {

  return <View style={styles.mainComponent} />;

}


// Estilos

export const styles = StyleSheet.create({

  mainComponent: {

    width: 50,

    height: 50,

    backgroundColor: 'red',

    borderRadius: 25,

    borderColor: 'green',

    borderWidth: 5,

  } as ViewStyle,

});


export default MiPrimerComponente;

Características:

  1. Función simple que retorna JSX

  2. No usa this

  3. Más fácil de leer y probar

  4. Hooks para estado y efectos

Con Props:

tsx

// Componente con propiedades

interface MiComponenteProps {

  color?: string;

  tamaño?: number;

}


function MiComponente({color = 'red', tamaño = 50}: MiComponenteProps): JSX.Element {

  return (

    <View

      style={{

        width: tamaño,

        height: tamaño,

        backgroundColor: color,

        borderRadius: tamaño / 2,

      }}

    />

  );

}


🏗️ Parte 2: Class Components

Ejemplo Básico:

tsx

// src/components/MiPrimerComponenteClass/MiPrimerComponenteClass.tsx

import React from 'react';

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


// Class Component

class MiPrimerComponenteClass extends React.Component {

  render(): React.ReactNode {

    return <View style={styles.mainComponent} />;

  }

}


// Estilos

export const styles = StyleSheet.create({

  mainComponent: {

    width: 50,

    height: 50,

    backgroundColor: 'blue', // Diferente color para distinguir

    borderRadius: 25,

    borderColor: 'green',

    borderWidth: 5,

  } as ViewStyle,

});


export default MiPrimerComponenteClass;

Características:

  1. Clase que extiende React.Component

  2. Usa this para acceder a props y state

  3. Método render() obligatorio

  4. Métodos de ciclo de vida

Estructura Completa:

tsx

import React from 'react';

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


interface Props {

  mensaje: string;

}


interface State {

  contador: number;

  activo: boolean;

}


class MiClaseComponent extends React.Component<Props, State> {

  constructor(props: Props) {

    super(props);

    this.state = {

      contador: 0,

      activo: true,

    };

  }


  componentDidMount() {

    console.log('Componente montado');

  }


  componentDidUpdate() {

    console.log('Componente actualizado');

  }


  componentWillUnmount() {

    console.log('Componente desmontado');

  }


  incrementar = () => {

    this.setState(prevState => ({

      contador: prevState.contador + 1,

    }));

  };


  render() {

    return (

      <View>

        <Text>{this.props.mensaje}</Text>

        <Text>Contador: {this.state.contador}</Text>

      </View>

    );

  }

}


🔄 Parte 3: Usar Ambos Componentes en App.tsx

Importar y usar ambos componentes:

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'}}>

      {/* Functional Component */}

      <MiPrimerComponente />

      

      {/* Class Component */}

      <MiPrimerComponenteClass />

      

      {/* Reutilizar componentes */}

      <MiPrimerComponente />

      <MiPrimerComponente />

    </SafeAreaView>

  );

}


export default App;

Resultado Visual:

  • Círculo rojo: Functional Component

  • Círculo azul: Class Component

  • Misma sintaxis de uso: <NombreComponente />


📝 Parte 4: Diferencias Detalladas

1. Manejo de Props:

Functional Component:

tsx

// Props como parámetro de función

function Saludo({nombre, edad}: {nombre: string; edad: number}) {

  return <Text>Hola {nombre}, tienes {edad} años</Text>;

}

Class Component:

tsx

// Props accesibles via this.props

class Saludo extends React.Component<{nombre: string; edad: number}> {

  render() {

    return (

      <Text>

        Hola {this.props.nombre}, tienes {this.props.edad} años

      </Text>

    );

  }

}

2. Manejo de Estado:

Functional Component (useState Hook):

tsx

import React, {useState} from 'react';


function Contador() {

  const [contador, setContador] = useState(0);

  

  return (

    <View>

      <Text>Contador: {contador}</Text>

      <Button title="Incrementar" onPress={() => setContador(contador + 1)} />

    </View>

  );

}

Class Component (this.state):

tsx

class Contador extends React.Component {

  constructor(props) {

    super(props);

    this.state = {

      contador: 0,

    };

  }

  

  incrementar = () => {

    this.setState({contador: this.state.contador + 1});

  };

  

  render() {

    return (

      <View>

        <Text>Contador: {this.state.contador}</Text>

        <Button title="Incrementar" onPress={this.incrementar} />

      </View>

    );

  }

}

3. Ciclo de Vida:

Functional Component (useEffect Hook):

tsx

import React, {useEffect} from 'react';


function MiComponente() {

  useEffect(() => {

    // componentDidMount

    console.log('Componente montado');

    

    return () => {

      // componentWillUnmount

      console.log('Componente desmontado');

    };

  }, []);

  

  return <View />;

}

Class Component (Métodos):

tsx

class MiComponente extends React.Component {

  componentDidMount() {

    console.log('Componente montado');

  }

  

  componentDidUpdate(prevProps, prevState) {

    console.log('Componente actualizado');

  }

  

  componentWillUnmount() {

    console.log('Componente desmontado');

  }

  

  render() {

    return <View />;

  }

}


🎯 Parte 5: Cuándo Usar Cada Uno

Usar Functional Components cuando:

✅ Proyectos nuevos - Es la forma moderna
✅ Componentes simples - Sin lógica compleja
✅ Reutilización de lógica - Con Custom Hooks
✅ Performance - Más ligeros que las clases
✅ Testing - Más fáciles de probar

Usar Class Components cuando:

⚠️ Mantener código legacy - Proyectos existentes
⚠️ Necesitas Error Boundaries - Manejo de errores
⚠️ Componentes muy complejos - Con mucha lógica de ciclo de vida
⚠️ Preferencia personal - Si te sientes más cómodo


🔧 Parte 6: Convertir entre Ambos

De Class a Functional Component:

tsx

// Class Component original

class ContadorClase extends React.Component {

  state = {contador: 0};

  

  incrementar = () => {

    this.setState({contador: this.state.contador + 1});

  };

  

  render() {

    return (

      <View>

        <Text>{this.state.contador}</Text>

        <Button title="+" onPress={this.incrementar} />

      </View>

    );

  }

}


// Functional Component equivalente

function ContadorFuncional() {

  const [contador, setContador] = useState(0);

  

  const incrementar = () => {

    setContador(contador + 1);

  };

  

  return (

    <View>

      <Text>{contador}</Text>

      <Button title="+" onPress={incrementar} />

    </View>

  );

}

De Functional a Class Component:

tsx

// Functional Component original

function SaludoFuncional({nombre}: {nombre: string}) {

  return <Text>Hola {nombre}</Text>;

}


// Class Component equivalente

class SaludoClase extends React.Component<{nombre: string}> {

  render() {

    return <Text>Hola {this.props.nombre}</Text>;

  }

}


🧪 Parte 7: Ejercicio Práctico - Crear Ambos Tipos

Ejercicio 1: Botón con Contador

Functional Component:

tsx

// src/components/ContadorFuncional/ContadorFuncional.tsx

import React, {useState} from 'react';

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


function ContadorFuncional(): JSX.Element {

  const [contador, setContador] = useState(0);

  

  const incrementar = () => setContador(contador + 1);

  const decrementar = () => setContador(contador - 1);

  const reset = () => setContador(0);

  

  return (

    <View style={styles.container}>

      <Text style={styles.texto}>Contador: {contador}</Text>

      <View style={styles.botones}>

        <Button title="-" onPress={decrementar} />

        <Button title="Reset" onPress={reset} />

        <Button title="+" onPress={incrementar} />

      </View>

    </View>

  );

}


const styles = StyleSheet.create({

  container: {

    padding: 20,

    margin: 10,

    backgroundColor: '#f0f0f0',

    borderRadius: 10,

  },

  texto: {

    fontSize: 24,

    textAlign: 'center',

    marginBottom: 20,

  },

  botones: {

    flexDirection: 'row',

    justifyContent: 'space-around',

  },

});


export default ContadorFuncional;

Class Component:

tsx

// src/components/ContadorClase/ContadorClase.tsx

import React from 'react';

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


interface State {

  contador: number;

}


class ContadorClase extends React.Component<{}, State> {

  constructor(props: {}) {

    super(props);

    this.state = {

      contador: 0,

    };

  }

  

  incrementar = () => {

    this.setState(prevState => ({

      contador: prevState.contador + 1,

    }));

  };

  

  decrementar = () => {

    this.setState(prevState => ({

      contador: prevState.contador - 1,

    }));

  };

  

  reset = () => {

    this.setState({contador: 0});

  };

  

  render() {

    return (

      <View style={styles.container}>

        <Text style={styles.texto}>Contador: {this.state.contador}</Text>

        <View style={styles.botones}>

          <Button title="-" onPress={this.decrementar} />

          <Button title="Reset" onPress={this.reset} />

          <Button title="+" onPress={this.incrementar} />

        </View>

      </View>

    );

  }

}


// Mismos estilos que el componente funcional

const styles = StyleSheet.create({

  container: {

    padding: 20,

    margin: 10,

    backgroundColor: '#f0f0f0',

    borderRadius: 10,

  },

  texto: {

    fontSize: 24,

    textAlign: 'center',

    marginBottom: 20,

  },

  botones: {

    flexDirection: 'row',

    justifyContent: 'space-around',

  },

});


export default ContadorClase;

Ejercicio 2: Mostrar Ambos en App.tsx

tsx

// App.tsx

import React from 'react';

import {SafeAreaView, ScrollView} from 'react-native';

import ContadorFuncional from './src/components/ContadorFuncional/ContadorFuncional';

import ContadorClase from './src/components/ContadorClase/ContadorClase';


function App(): JSX.Element {

  return (

    <SafeAreaView style={{flex: 1}}>

      <ScrollView contentContainerStyle={{padding: 20}}>

        <ContadorFuncional />

        <ContadorClase />

      </ScrollView>

    </SafeAreaView>

  );

}


export default App;


⚠️ Parte 8: Consideraciones y Advertencias

Error Boundaries (Solo Class Components):

tsx

// Solo posible con Class Components

class ErrorBoundary extends React.Component {

  constructor(props) {

    super(props);

    this.state = {hasError: false};

  }

  

  static getDerivedStateFromError(error) {

    return {hasError: true};

  }

  

  componentDidCatch(error, errorInfo) {

    console.log('Error:', error, errorInfo);

  }

  

  render() {

    if (this.state.hasError) {

      return <Text>Algo salió mal</Text>;

    }

    

    return this.props.children;

  }

}

Refs (Referencias):

tsx

// Functional Component con useRef

function MiComponente() {

  const inputRef = useRef(null);

  

  useEffect(() => {

    inputRef.current.focus();

  }, []);

  

  return <TextInput ref={inputRef} />;

}


// Class Component con createRef

class MiComponente extends React.Component {

  inputRef = React.createRef();

  

  componentDidMount() {

    this.inputRef.current.focus();

  }

  

  render() {

    return <TextInput ref={this.inputRef} />;

  }

}


📚 Parte 9: Migración y Mejores Prácticas

Migrar de Class a Functional:

  1. Reemplazar this.state con useState

  2. Reemplazar métodos de ciclo de vida con useEffect

  3. Convertir métodos a funciones dentro del componente

  4. Eliminar constructor y super()

  5. Usar props directamente como parámetro

Código Legacy vs Moderno:

tsx

// Legacy (2018) - Class Components

class App extends React.Component {

  render() {

    return <View>Hola</View>;

  }

}


// Modern (2023+) - Functional Components

const App = () => {

  return <View>Hola</View>;

};


✅ Resumen y Recomendaciones

Para Nuevos Desarrolladores:

  1. Aprende Functional Components primero

  2. Domina Hooks (useState, useEffect)

  3. Solo estudia Class Components si encuentras código legacy

Para Proyectos Existentes:

  1. Nuevos componentes: Usar Functional

  2. Componentes existentes: Mantener como están

  3. Refactorizar solo si hay beneficio claro

Regla de Oro:

"Usa Functional Components para todo lo nuevo, aprende Class Components para entender código existente"


🎉 ¡Felicidades!

Has aprendido las dos formas de crear componentes en React Native:

✅ Functional Components - La forma moderna con Hooks
✅ Class Components - La forma tradicional con métodos
✅ Diferencias entre ambos enfoques
✅ Cuándo usar cada uno según el contexto
✅ Cómo convertir entre ambos tipos
✅ Ejercicios prácticos para aplicar el conocimiento


🔮 Tendencia del Mercado:

  • 2018-2020: Transición de Class a Functional

  • 2021+: Functional Components dominantes

  • Hooks: Standard para estado y efectos

  • Class Components: Solo para código legacy o casos específicos


¡Excelente trabajo! Ahora tienes el conocimiento completo sobre componentes en React Native. En los próximos tutoriales profundizaremos en Props, State y Eventos. 


Comentarios

Entradas más populares de este blog

Guía Paso a Paso para Entender React Native (antes del Tutorial)

Tutorial: Aplicación React Native para Agregar Tareas - Minimalista

5. Vista Rapida: Estructura Projecto Base