9.6-Ciclo de Vida de un Componente React

 

Ciclo de Vida de un Componente React

Fases del Ciclo de Vida:

1. Montaje (Mounting)

El componente se crea y se inserta en el DOM.

2. Actualización (Updating)

El componente se re-renderiza debido a cambios en props o estado.

3. Desmontaje (Unmounting)

El componente se elimina del DOM.


Diagrama Completo del Ciclo de Vida

En Componentes de Clase:

javascript

class MiComponente extends React.Component {

  // 1. CONSTRUCTOR (montaje)

  constructor(props) {

    super(props);

    this.state = { contador: 0 };

    console.log('constructor');

  }


  // 2. getDerivedStateFromProps (montaje/actualización)

  static getDerivedStateFromProps(props, state) {

    console.log('getDerivedStateFromProps');

    return null; // Retorna nuevo estado o null

  }


  // 3. RENDER (montaje/actualización)

  render() {

    console.log('render');

    return <div>Contador: {this.state.contador}</div>;

  }


  // 4. componentDidMount (montaje)

  componentDidMount() {

    console.log('componentDidMount');

    // Aquí haces peticiones API, suscripciones, etc.

  }


  // 5. shouldComponentUpdate (actualización)

  shouldComponentUpdate(nextProps, nextState) {

    console.log('shouldComponentUpdate');

    return true; // true para re-renderizar, false para no hacerlo

  }


  // 6. getSnapshotBeforeUpdate (actualización)

  getSnapshotBeforeUpdate(prevProps, prevState) {

    console.log('getSnapshotBeforeUpdate');

    return null; // Puede retornar un valor para componentDidUpdate

  }


  // 7. componentDidUpdate (actualización)

  componentDidUpdate(prevProps, prevState, snapshot) {

    console.log('componentDidUpdate');

    // Aquí puedes hacer operaciones después del update

  }


  // 8. componentWillUnmount (desmontaje)

  componentWillUnmount() {

    console.log('componentWillUnmount');

    // Aquí limpias suscripciones, temporizadores, etc.

  }

}

En Componentes Funcionales (con Hooks):

javascript

import { useState, useEffect } from 'react';


function MiComponente() {

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

  

  // Equivalente a componentDidMount + componentDidUpdate

  useEffect(() => {

    console.log('Efecto ejecutado (montaje/actualización)');

    

    // Función de cleanup (equivalente a componentWillUnmount)

    return () => {

      console.log('Cleanup ejecutado');

    };

  }, [contador]); // Dependencias: se ejecuta cuando contador cambia

  

  // useEffect sin dependencias = solo en montaje

  useEffect(() => {

    console.log('Solo en montaje (como componentDidMount)');

    

    return () => {

      console.log('Cleanup al desmontar');

    };

  }, []); // Array vacío = solo montaje/desmontaje


  console.log('render');

  return <div>Contador: {contador}</div>;

}

Cómo se Relaciona Todo:

1. Estado (State) → Virtual DOM → DOM Real

javascript

// Cuando el ESTADO cambia:

class Componente extends React.Component {

  state = { valor: 0 };

  

  handleClick = () => {

    // 1. Cambio de estado

    this.setState({ valor: 1 });

    

    // 2. React marca el componente como "necesita actualización"

    // 3. Se ejecuta shouldComponentUpdate()

    // 4. Se ejecuta render() → crea NUEVO Virtual DOM

    // 5. React compara Virtual DOM viejo vs nuevo (Diffing)

    // 6. React calcula cambios mínimos necesarios

    // 7. React aplica esos cambios al DOM REAL

    // 8. Se ejecuta componentDidUpdate()

  }

}

2. Enganchar/Desenganchar Componentes

¿Qué significa "enganchar"?

Significa conectar un componente al árbol de componentes de React y al DOM.

javascript

// Cuando un componente se "engancha":

// 1. Se crea la instancia

// 2. Se inicializa el estado

// 3. Se renderiza por primera vez

// 4. Se inserta en el DOM

// 5. Se ejecutan efectos (useEffect o componentDidMount)


// Ejemplo de enganche con useEffect:

useEffect(() => {

  // Esto se ejecuta cuando el componente se ENGANCHA

  const suscripcion = API.suscribir();

  

  return () => {

    // Esto se ejecuta cuando el componente se DESENGANCHA

    suscripcion.cancelar();

  };

}, []);

Ejemplo Práctico: Suscripción a Datos

javascript

function ChatComponent() {

  const [mensajes, setMensajes] = useState([]);

  

  useEffect(() => {

    // ENGANCHAR: Cuando el componente se monta

    console.log('🔌 Conectando al servidor de chat...');

    const socket = conectarSocket();

    

    // Escuchar mensajes nuevos

    socket.on('nuevoMensaje', (mensaje) => {

      setMensajes(prev => [...prev, mensaje]);

    });

    

    // DESENGANCHAR: Cuando el componente se desmonta

    return () => {

      console.log('🔌 Desconectando del servidor...');

      socket.desconectar(); // Limpieza

    };

  }, []); // [] = solo en montaje/desmontaje

  

  return <div>Chat: {mensajes.length} mensajes</div>;

}


Flujo Completo con Todos los Conceptos

Escenario: Componente de Contador

javascript

// 1. DECLARACIÓN DEL COMPONENTE

function Contador({ inicial = 0 }) {

  // 2. ESTADO (memoria interna del componente)

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

  

  // 3. EFECTOS (ciclo de vida)

  useEffect(() => {

    // FASE DE MONTAJE (enganche)

    console.log('✅ Componente MONTADO en el DOM');

    document.title = `Contador: ${contador}`;

    

    return () => {

      // FASE DE DESMONTAJE (desenganche)

      console.log('❌ Componente DESMONTADO del DOM');

      document.title = 'React App'; // Restaurar título

    };

  }, []); // Solo en montaje/desmontaje

  

  useEffect(() => {

    // FASE DE ACTUALIZACIÓN

    console.log('🔄 Contador actualizado:', contador);

  }, [contador]); // Solo cuando contador cambia

  

  // 4. RENDER (crea Virtual DOM)

  console.log('🎨 Renderizando Virtual DOM...');

  

  return (

    <div>

      <p>Valor: {contador}</p>

      <button onClick={() => setContador(c => c + 1)}>

        Incrementar

      </button>

    </div>

  );

}


// 5. USO DEL COMPONENTE

function App() {

  const [mostrar, setMostrar] = useState(true);

  

  return (

    <div>

      <button onClick={() => setMostrar(!mostrar)}>

        {mostrar ? 'Ocultar' : 'Mostrar'} Contador

      </button>

      

      {/* Cuando 'mostrar' es true, el componente se ENGANCHA */}

      {/* Cuando 'mostrar' es false, el componente se DESENGANCHA */}

      {mostrar && <Contador inicial={5} />}

    </div>

  );

}

Secuencia de Eventos:

Al hacer clic en "Mostrar Contador":

text

1. React crea el componente Contador

2. useState() inicializa el estado (contador = 5)

3. Console: "🎨 Renderizando Virtual DOM..."

4. React crea elemento Virtual DOM

5. React compara con DOM anterior

6. React inserta el div en DOM real

7. useEffect() se ejecuta: "✅ Componente MONTADO"

Al hacer clic en "Incrementar":

text

1. setContador() actualiza el estado

2. React marca componente para re-render

3. Console: "🎨 Renderizando Virtual DOM..."

4. React crea NUEVO Virtual DOM con contador = 6

5. React COMPARA con Virtual DOM anterior

6. React detecta que solo cambió el texto "Valor: 5" → "Valor: 6"

7. React ACTUALIZA solo ese texto en el DOM real

8. Console: "🔄 Contador actualizado: 6"

Al hacer clic en "Ocultar Contador":

text

1. React prepara desmontaje

2. Ejecuta cleanup de useEffect: "❌ Componente DESMONTADO"

3. React elimina el elemento del DOM real

4. Estado del componente se pierde (a menos que esté elevado)


Relaciones Clave:

Estado ↔ Virtual DOM

  • El estado determina QUÉ se renderiza

  • Cambios en estado → Nuevo Virtual DOM → Actualización DOM real

Ciclo de Vida ↔ DOM Virtual

  • Montaje: Virtual DOM → DOM real

  • Actualización: Virtual DOM anterior vs nuevo → Cambios mínimos

  • Desmontaje: Eliminar del DOM real

Enganchar/Desenganchar ↔ Efectos

  • Enganchar = Montar + Configurar recursos

  • Desenganchar = Desmontar + Limpiar recursos


Buenas Prácticas:

1. Limpieza en Desmontaje:

javascript

useEffect(() => {

  const intervalo = setInterval(() => {}, 1000);

  const suscripcion = api.suscribir();

  

  return () => {

    clearInterval(intervalo); // 🔴 IMPORTANTE!

    suscripcion.cancelar();   // Evitar memory leaks

  };

}, []);

2. Actualizaciones Optimizadas:

javascript

// ❌ Mal: Re-render en cada actualización

useEffect(() => {

  console.log('Se ejecuta siempre');

}); // Sin array de dependencias


// ✅ Bien: Solo cuando 'data' cambia

useEffect(() => {

  console.log('Solo cuando data cambia');

}, [data]); // Con dependencias específicas

3. Manejo de Suscripciones:

javascript

function UsuarioEnVivo({ usuarioId }) {

  const [estaEnLinea, setEstaEnLinea] = useState(null);

  

  useEffect(() => {

    // Enganchar suscripción

    const unsubscribe = firebase

      .suscribirEstadoUsuario(usuarioId, (estado) => {

        setEstaEnLinea(estado);

      });

    

    // Desenganchar suscripción

    return () => unsubscribe();

  }, [usuarioId]); // Se re-engancha si usuarioId cambia

  

  return <div>Usuario {estaEnLinea ? 'en línea' : 'offline'}</div>;

}


Resumen Visual:

text

[Estado/Props Cambian]

        ↓

[Componente Re-renderiza]

        ↓

[Nuevo Virtual DOM Creado]

        ↓

[React Compara con Anterior]

        ↓

[Cálculo de Cambios Mínimos]

        ↓

[Aplicación al DOM Real]

        ↓

[Efectos (DidMount/DidUpdate)]

        ↓

[Espera Nuevos Cambios...]

        ↑

        └─────────┐

                  ↓

          [Usuario Interactúa]

                  ↓

           [setState() Llamado]

El ciclo de vida, estado, Virtual DOM y hooks están íntimamente conectados: El estado dicta el renderizado, el Virtual DOM optimiza las actualizaciones, y los hooks te permiten "enganchar" código en puntos específicos del ciclo de vida para crear componentes interactivos y eficientes.


Comentarios

Entradas más populares de este blog

9.3-Tutorial de Componentes React con Props -SSS

9-Componentes en React-estado