Estados básicos del componente en React


Los hooks son funciones especiales que te permiten usar características como el estado y el ciclo de vida en componentes funcionales

Estados básicos del componente en React
oscar Escrito por oscar 23 April 2025 27 0

En React, los hooks son funciones especiales que te permiten usar características como el estado y el ciclo de vida en componentes funcionales. Los hooks fueron introducidos en React 16.8 y son fundamentales para escribir componentes más limpios y reutilizables.

Prerrequisitos

Crear un proyecto de prueba

Antes de explicar los Hooks, vamos a crear un proyecto react para realizar los ejercicios que explicaremos a continuación:

npm create vite@latest

Donde seleccionamos:

> npx
> create-vite
|
o  Project name:
|  practicas-react
|
o  Select a framework:
|  React
|
o  Select a variant:
|  TypeScript

Por ultimo, ejecutamos

cd practicas-react
npm install
npm run dev

Vamos a crear un directorio llamado components en el directorio src donde crearemos los componentes de los hooks

Hooks Básicos

​⏺️ useState

Permite a los componentes funcionales gestionar su estado, lo que antes se hacía solo en las clases. Al usar useState, puedes añadir una variable de estado que se mantiene entre renderizaciones y actualizarla, haciendo que tu componente sea dinámico y reaccione a las interacciones. 

import { useState } from "react";

export default function Counter() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>Contador: {count}</p>
      <button onClick={() => setCount(count + 1)}>Incrementar</button>
    </div>
  );
}
  1. Se importa el hook useState, que permite agregar estado a un componente funcional.
  2. Se declara una función que representa un componente llamado Counter.
  3. Uso de useState
    • count → Es el valor actual del estado (en este caso, el número del contador).
    • setCount → Es la función para actualizar ese estado.
    • useState(0) → Inicializa el estado con 0.
  4. Retorno del componente (JSX)
    • <p>Contador: {count}</p>: Muestra el valor actual del contador.
    • <button onClick={...}>: Es un botón que, al hacer clic, incrementa el contador en 1 usando setCount.

Siempre que necesites manejar un estado local en un componente funcional.

​⏺️ useEffect

Permite realizar efectos secundarios en componentes funcionales, como peticiones de datos, actualizaciones del DOM, o configurar suscripciones.

import { useEffect, useState } from "react";

export default function DataFetcher() {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch("https://timeapi.io/api/Time/current/zone?timeZone=America/Bogota")
      .then((response) => response.json())
      .then((data) => setData(data));
  }, []); // [] significa que solo se ejecuta en el montaje

  return <div>{data ? JSON.stringify(data) : "Cargando..."}</div>;
}
  1. Importación de hooks
    • useState: Para manejar el estado del componente.
    • useEffect: Para manejar efectos secundarios, como llamadas a APIs.
  2. Este es un componente funcional llamado DataFetcher, y se está exportando como componente predeterminado (default).
  3. Estado local useState: El estado comienza en null, lo que indica que aún no se ha recibido la información.
  4. Efecto con useEffect
    • useEffect ejecuta el bloque de código una vez que el componente se monta.
    • El array vacío [] indica que se ejecuta una sola vez, como si fuera componentDidMount en componentes de clase.
    • Dentro del efecto se hace una llamada a la API https://timeapi.io/... para obtener la hora actual en Bogotá.
    • Una vez que la respuesta llega, se convierte a JSON, y se guarda en el estado con setData(data).
  5. Renderizado condicional
    • Si data ya tiene valor, se muestra como texto usando JSON.stringify(data) (convierte el objeto en un string).
    • Si data todavía es null, se muestra "Cargando...".

​⏺️ useContext

useContext es un Hook de React que te permite acceder y suscribirte a un contexto desde tus componentes funcionales. Este Hook facilita la gestión de datos compartidos entre componentes sin necesidad de pasar props a través de múltiples niveles de jerarquía. 

import React, { createContext, useContext } from 'react';

const ThemeContext = createContext('light');

function ThemedComponent() {
  const theme = useContext(ThemeContext);
  return <div>El tema actual es {theme}</div>;
}

Cuando quieras compartir datos globales entre componentes (como temas o usuarios autenticados).

Hooks Adicionales

​⏺️ useReducer

useReducer es un Hook de React que facilita la gestión de estados complejos dentro de componentes funcionales, similar al patrón de reducción en Redux, pero integrado directamente en React. Es una alternativa a useState cuando la lógica para actualizar el estado se vuelve más compleja, permitiendo el uso de una función reductora para manejar las actualizaciones de estado. 

import React, { useReducer } from 'react';

function reducer(state, action) {
  switch (action.type) {
    case "increment":
      return { count: state.count + 1 };
    case "decrement":
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

export default function Counter() {
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  return (
    <div>
      <p>Contador: {state.count}</p>
      <button onClick={() => dispatch({ type: "increment" })}>
        Incrementar
      </button>
      <button onClick={() => dispatch({ type: "decrement" })}>
        Decrementar
      </button>
    </div>
  );
}

​⏺️ useRef

useRef devuelve un objeto ref mutable cuya propiedad . current se inicializa con el argumento pasado ( initialValue ). El objeto devuelto se mantendrá persistente durante la vida completa del componente. En esencia, useRef es como una “caja” que puedes mantener en una variable mutable en su propiedad .

import React, { useRef } from 'react';

function InputFocus() {

  const inputRef = useRef(null);

  const focusInput = () => {
    inputRef.current.focus();
  };

  return (
    <div>
      <input ref={inputRef} />
      <button onClick={focusInput}>Focalizar Input</button>
    </div>
  );

}

Para acceder a elementos del DOM o almacenar valores mutables.

​⏺️ useMemo

Memoriza un valor calculado para evitar recomputaciones costosas.

import React, { useMemo } from 'react';

function ExpensiveCalculation({ num }) {

  const factorial = useMemo(() => {
    console.log('Calculando factorial...');
    return num <= 0 ? 1 : num * factorial(num - 1);
  }, [num]);

  return <div>Factorial: {factorial}</div>;
}

​⏺️ useCallback

Memoriza una función para evitar su recreación en cada render.

import React, { useCallback } from 'react';

function Parent({ onClick }) {

  const handleClick = useCallback(() => {
    console.log('Clickeado');
  }, []);

  return <button onClick={handleClick}>Click me</button>;
}

Cuando necesites pasar funciones a componentes hijos para evitar renders innecesarios.

Hooks Avanzados

​⏺️ useImperativeHandle

Personaliza la instancia del componente expuesta al componente padre cuando se usa React.forwardRef.

src/
├── components/
│   └── CustomInput.tsx
├── App.tsx
└── main.tsx

Creamos CustomInput.tsx – Componente con useImperativeHandle

// src/components/CustomInput.tsx
import React, { useImperativeHandle, useRef, forwardRef } from 'react';

// Definimos los métodos que el padre podrá usar
export interface CustomInputHandle {
  focus: () => void;
  clear: () => void;
}

// Usamos forwardRef para pasar el ref desde el padre
const CustomInput = forwardRef<CustomInputHandle>((props, ref) => {
  const inputRef = useRef<HTMLInputElement>(null);

  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current?.focus();
    },
    clear: () => {
      if (inputRef.current) {
        inputRef.current.value = '';
      }
    }
  }));

  return <input ref={inputRef} placeholder="Escribe algo..." />;
});

export default CustomInput;

Modificamos el App.tsx – El componente padre usa el ref

import { useRef } from "react";
import "./App.css";
import CustomInput, { type CustomInputHandle } from "./components/CustomInput";
function App() {
  const inputRef = useRef<CustomInputHandle>(null);

  const handleFocus = () => {
    inputRef.current?.focus();
  };

  const handleClear = () => {
    inputRef.current?.clear();
  };
  return (
    <div style={{ padding: "40px" }}>
      <h1>useImperativeHandle Example</h1>
      <CustomInput ref={inputRef} />
      <div style={{ marginTop: "10px" }}>
        <button onClick={handleFocus} style={{ marginRight: "10px" }}>
          Focus
        </button>
        <button onClick={handleClear}>Clear</button>
      </div>
    </div>
  );
}

export default App;

Tendrás un campo de texto con dos botones:

  • Al hacer clic en "Focus", el input se enfoca.
  • Al hacer clic en "Clear", se borra su contenido.

​⏺️ useLayoutEffect

useLayoutEffect es un Hook de React similar a useEffect, pero se ejecuta de forma sincrónica después de que se actualiza el DOM, pero antes de que el navegador repinte la pantalla. Esto lo hace ideal para situaciones donde necesitas medir o manipular el DOM y asegurarte de que los cambios se vean antes de que el navegador renderice el siguiente fotograma. 

Ejemplo 

Estructura de archivos

src/
├── components/
│   └── BoxWithSize.tsx
├── App.tsx
└── main.tsx

Creamos BoxWithSize.tsx (componente que usa useLayoutEffect)

import React, { useLayoutEffect, useRef, useState } from "react";

const BoxWithSize: React.FC = () => {
  const boxRef = useRef<HTMLDivElement>(null);
  const [size, setSize] = useState({ width: 0, height: 0 });

  useLayoutEffect(() => {
    if (boxRef.current) {
      const { width, height } = boxRef.current.getBoundingClientRect();
      setSize({ width, height });
    }
  }, []); // Solo se ejecuta una vez después del montaje y antes del repintado

  return (
    <div>
      <div
        ref={boxRef}
        style={{
          width: "200px",
          height: "150px",
          backgroundColor: "lightblue",
          margin: "20px",
        }}
      >
        Soy una caja
      </div>
      <p>
        Mi tamaño es: {size.width}px × {size.height}px
      </p>
    </div>
  );
};

export default BoxWithSize;

✅ Explicación paso a paso

  1. useRef crea una referencia al <div> con el tamaño que queremos medir.
  2. useLayoutEffect se ejecuta después del DOM renderizado pero antes de que el navegador pinte la pantalla. Es ideal para leer o modificar el layout.
  3. Usamos getBoundingClientRect() para obtener el ancho y alto de la caja.
  4. Guardamos esas dimensiones en el estado para mostrarlas en pantalla.
  5. Se renderiza el contenido con los valores ya leídos, sin parpadeos visuales.

Hooks Personalizados

Puedes crear tus propios hooks para reutilizar lógica que involucre otros hooks. Por ejemplo:

import { useState, useEffect } from 'react';

function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch(url)
      .then((response) => response.json())
      .then((data) => {
        setData(data);
        setLoading(false);
      });
  }, [url]);

  return { data, loading };
}

Fases del ciclo de vida de un componente

El ciclo de vida de un componente en React describe las etapas por las que pasa un componente desde que es creado, actualizado y eventualmente eliminado. Aunque en los componentes funcionales se gestiona principalmente con hooks, el concepto sigue siendo el mismo.

A continuación, te explico cómo funciona el ciclo de vida tanto en componentes de clase como en componentes funcionales con hooks:

​⏺️ Montaje (Mounting)

Es el proceso de creación e inserción del componente en el DOM.

🟢 Métodos en Componentes de Clase

  • constructor(props): Se ejecuta antes de que el componente se monte. Se usa para inicializar el estado o enlazar métodos.
  • static getDerivedStateFromProps(props, state): Se llama justo antes del render, útil para sincronizar el estado con las props.
  • render(): Genera el árbol de elementos React que se inserta en el DOM.
  • componentDidMount(): Se llama después de que el componente se haya montado en el DOM. Aquí puedes realizar llamadas a APIs o configurar eventos.

🟢 Hooks en Componentes Funcionales

useEffect(() => { ... }, []): El equivalente a componentDidMount. El efecto se ejecuta después del montaje si el segundo argumento es un array vacío.

​⏺️ Actualización (Updating)

Se da cuando el componente detecta cambios en sus props o estado.

🟢 Métodos en Componentes de Clase

  • static getDerivedStateFromProps(props, state): Se llama cada vez que cambian las props.
  • shouldComponentUpdate(nextProps, nextState): Controla si el componente debe renderizarse nuevamente.
  • render(): Se llama para generar el nuevo árbol de elementos React.
  • getSnapshotBeforeUpdate(prevProps, prevState): Captura información antes de que se actualice el DOM (como el scroll).
  • componentDidUpdate(prevProps, prevState, snapshot): Se llama después de que el componente se haya actualizado. Útil para manejar efectos secundarios basados en cambios en props o estado.

🟢 Hooks en Componentes Funcionales

  • useEffect(() => { ... }, [dependencias]): Se ejecuta cada vez que cambian las dependencias especificadas.
  • Sin equivalente directo: No hay hooks específicos como shouldComponentUpdate, pero puedes optimizar renderizados con React.memo.

​⏺️ Desmontaje (Unmounting)

Es el proceso de eliminar un componente del DOM.

🟢 Métodos en Componentes de Clase

componentWillUnmount(): Se llama justo antes de que el componente sea eliminado del DOM. Se usa para limpiar eventos, timers o suscripciones.

🟢 Hooks en Componentes Funcionales

useEffect(() => { return () => { ... } }, []): La función de limpieza (cleanup) es el equivalente a componentWillUnmount.


Comentario

Debe aceptar antes de enviar