¿Qué es React?
React es una librería de JavaScript para construir interfaces de usuario a partir de componentes. Un componente es una función que devuelve marcado, y puedes componer varios componentes para construir una página. En lugar de indicarle al navegador cómo cambiar el DOM paso a paso, describes cómo debería verse la UI según los datos actuales y React se encarga de las actualizaciones.
Este cambio se denomina renderizado declarativo, y es la razón por la cual React se sintió diferente en 2013. Dejas de escribir “busca este elemento, cambia su texto, alterna esa clase” y empiezas a escribir “dado este estado, así es la pantalla”. La parte compleja —comparar y actualizar el DOM de manera eficiente— pasa a ser tarea de React.
Componentes y JSX
Un componente de React es una función cuyo nombre comienza con una letra mayúscula y que devuelve JSX.
// Welcome.jsx
function Welcome({ name }) {
return <h1>Hello, {name}!</h1>;
}
export default function App() {
return (
<main>
<Welcome name="Ada" />
<Welcome name="Grace" />
</main>
);
}
JSX se parece al HTML pero es JavaScript. Las llaves permiten incrustar expresiones y el compilador traduce cada elemento en una llamada a una función. Debido a que los componentes son simplemente funciones, puedes componerlos, anidarlos y reutilizarlos de la misma manera que reutilizarías cualquier otra función.
Props
Las props son las entradas de un componente. Fluyen hacia abajo, del padre al hijo, y son de solo lectura; un hijo nunca debe mutar sus props.
// Card.jsx
function Card({ title, children, variant = "default" }) {
return (
<article className={`card card--${variant}`}>
<h3>{title}</h3>
{children}
</article>
);
}
La prop children es cualquier cosa que anides entre las etiquetas de apertura y cierre, lo que convierte a los componentes en envoltorios (wrappers) composibles. Los valores por defecto permiten que quienes llamen al componente omitan las props que normalmente no cambian.
Estado con useState
Las props son fijas para un renderizado determinado. El estado son datos que pertenecen a un componente y que pueden cambiar con el tiempo. El hook useState crea un valor de estado y una función setter.
// Search.jsx
import { useState } from "react";
function Search({ onSearch }) {
const [query, setQuery] = useState("");
return (
<form onSubmit={(e) => { e.preventDefault(); onSearch(query); }}>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
</form>
);
}
Llamar al setter programa un re-renderizado con el nuevo valor. Las actualizaciones de estado son asíncronas y se procesan en lotes (batched), y reemplazan el valor en lugar de fusionarlo; por lo tanto, crea siempre un nuevo objeto o array al actualizar un estado estructurado.
// update.jsx
setUser((prev) => ({ ...prev, name: "Grace" }));
setItems((prev) => [...prev, newItem]);
Usar la forma de función actualizadora (updater function) te mantiene seguro cuando el siguiente valor depende del anterior.
Renderizado de listas y condiciones
React no tiene un lenguaje de plantillas para bucles y ramificaciones; se utiliza JavaScript puro.
// List.jsx
function List({ items }) {
if (items.length === 0) return <p>Nothing here yet.</p>;
return (
<ul>
{items.map((item) => (
<li key={item.id}>{item.label}</li>
))}
</ul>
);
}
La prop key es obligatoria para los elementos de una lista. Asígnale un valor estable y único proveniente de tus datos para que React pueda emparejar los elementos correctamente entre renderizados. El operador ternario y && cubren las condiciones en línea, aunque los early returns suelen leerse mejor.
Eventos
Los eventos en React utilizan props en formato camelCase y reciben un objeto de evento sintético.
// Button.jsx
<button
onClick={(event) => {
event.preventDefault();
submit();
}}
>
Save
</button>
Es importante pasar una función a la prop; onClick={submit()} la llamaría inmediatamente durante el renderizado. Cuando un manejador necesite un argumento, envuélvelo en una función de flecha (arrow function).
Efectos con useEffect
useEffect sincroniza tu componente con sistemas externos a React: peticiones de red, suscripciones, temporizadores y manipulación directa del DOM.
// Users.jsx
import { useEffect, useState } from "react";
function Users() {
const [users, setUsers] = useState([]);
useEffect(() => {
const controller = new AbortController();
fetch("/api/users", { signal: controller.signal })
.then((res) => res.json())
.then(setUsers)
.catch((err) => {
if (err.name !== "AbortError") console.error(err);
});
return () => controller.abort();
}, []);
return <ul>{users.map((u) => <li key={u.id}>{u.name}</li>)}</ul>;
}
El array de dependencias controla cuándo se vuelve a ejecutar el efecto: un array vacío se ejecuta una sola vez después del montaje, una lista de valores se ejecuta cuando cualquiera de ellos cambia, y la ausencia de un array hace que se ejecute después de cada renderizado. La función retornada es la de limpieza (cleanup), la cual se ejecuta antes del siguiente efecto y al desmontar el componente. Limpia siempre las suscripciones y las peticiones que estén en curso.
Context y refs
Context comparte un valor con todo un subárbol sin necesidad de recurrir al prop drilling.
// theme.jsx
const ThemeContext = createContext("light");
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
function Button() {
const theme = useContext(ThemeContext);
return <button className={theme}>Click</button>;
}
Refs mantienen un valor mutable o un nodo del DOM que no debe provocar un re-render.
// focus.jsx
const inputRef = useRef(null);
useEffect(() => inputRef.current?.focus(), []);
return <input ref={inputRef} />;
Flujo de datos y elevación de estado
El flujo de datos en React es unidireccional. Cuando dos componentes hermanos necesitan compartir un estado, este se eleva (lift) al padre común más cercano; luego, se pasa el valor hacia abajo como una prop y la función para actualizarlo como un callback. Esto mantiene una única fuente de verdad y hace que el flujo de cambios sea fácil de seguir.
Para árboles de componentes más profundos, el uso de context o una librería de estado dedicada como Zustand o Redux puede justificar la complejidad adicional. Recurre a ellos cuando el paso de props se vuelva tedioso, pero no antes.
Hooks y hooks personalizados
Los hooks son funciones que permiten a los componentes aprovechar las funcionalidades de React. Deben llamarse en el nivel superior, nunca dentro de condiciones o bucles. Una vez que los comprendas, podrás extraer la lógica compartida en un hook personalizado.
// useFetch.js
function useFetch(url) {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
let active = true;
fetch(url)
.then((res) => res.json())
.then((json) => active && setData(json))
.catch((err) => active && setError(err));
return () => { active = false; };
}, [url]);
return { data, error };
}
Un hook personalizado es simplemente una función cuyo nombre comienza con use y que llama a otros hooks. Comparte la lógica, no el estado: cada componente que lo utiliza obtiene su propia copia independiente.
Mejores prácticas
- Mantén los componentes pequeños y enfocados en una sola responsabilidad.
- Calcula los valores derivados durante el renderizado en lugar de almacenarlos en el estado.
- Utiliza la forma de actualización (updater form) de un setter cuando el siguiente valor dependa del anterior.
- Asigna a cada elemento de una lista una key estable proveniente de tus datos.
- Limpia los efectos, especialmente las suscripciones y las solicitudes pendientes.
- Eleva el estado (lift state) solo hasta donde sea estrictamente necesario.
- Extrae la lógica repetida en custom hooks.
- Recurre a la memoización (
useMemo,useCallback,memo) solo después de haber medido un problema real.
Errores comunes
- Mutar el estado directamente en lugar de crear un nuevo valor.
- Usar el índice del array como key, lo que rompe el reordenamiento.
- Guardar datos derivados en el estado y sincronizarlos mediante un efecto.
- Olvidar el array de dependencias y provocar bucles infinitos.
- Llamar a un hook de forma condicional, rompiendo las reglas de los hooks.
- Optimizar con memoización antes de que exista un problema de rendimiento.
Próximos pasos
React es la capa de vista; Next.js añade enrutamiento, obtención de datos y renderizado en el servidor. Compara este modelo con Vue y Svelte, y refuerza tus bases con JavaScript y TypeScript. El tutorial interactivo de React recorre estas mismas ideas paso a paso.