Cheat Sheet completa de React
Esta cheat sheet de React reúne los fundamentos y la capa intermedia que más aparece en proyectos reales: sintaxis, flujo de datos, hooks, Suspense, transiciones, routing y errores que conviene evitar.
Si quieres verlo paso a paso, tienes el Curso de React Fundamentals y el Curso de React Intermedio/Avanzado.
JSX y componentes
// Componente funcional
function Welcome() {
return <h1>Hola React</h1>;
}
// Uso
<Welcome />
// Un solo nodo raíz
function App() {
return (
<>
<Header />
<Main />
</>
);
}
// Expresiones dentro de JSX
const name = 'Ana';
<p>Hola, {name}</p>;
// Atributos especiales
<label htmlFor="email">Email</label>
<div className="card">Contenido</div>
Props
function UserCard({ name, role }) {
return (
<article>
<h2>{name}</h2>
<p>{role}</p>
</article>
);
}
<UserCard name="Lucía" role="Frontend" />
children
function Card({ title, children }) {
return (
<section>
<h2>{title}</h2>
{children}
</section>
);
}
<Card title="Perfil">
<p>Contenido interno</p>
</Card>
Eventos
function Button() {
function handleClick() {
console.log('click');
}
return <button onClick={handleClick}>Pulsar</button>;
}
Eventos comunes:
onClickonChangeonSubmitonFocusonBluronKeyDown
useState
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount((prev) => prev + 1)}>
{count}
</button>
);
}
Reglas rápidas
useState(valorInicial)crea estado localsetStateprovoca nuevo render- si dependes del valor anterior, usa función:
setCount(prev => prev + 1) - no mutar arrays ni objetos directamente
Estado con objetos y arrays
const [user, setUser] = useState({ name: 'Ana', admin: false });
setUser((prev) => ({
...prev,
admin: true,
}));
const [items, setItems] = useState(['HTML', 'CSS']);
setItems((prev) => [...prev, 'React']);
setItems((prev) => prev.filter((item) => item !== 'CSS'));
Renderizado condicional
if (isLoading) {
return <p>Cargando...</p>;
}
return <p>Listo</p>;
<p>{isLoggedIn ? 'Bienvenido' : 'Inicia sesión'}</p>
{error && <p>{error}</p>}
Listas y key
function TodoList({ todos }) {
return (
<ul>
{todos.map((todo) => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
}
Usa keys:
- únicas
- estables
- preferiblemente IDs reales
Evita el índice del array en listas dinámicas.
Formularios controlados
function SearchForm() {
const [query, setQuery] = useState('');
function handleSubmit(e) {
e.preventDefault();
console.log(query);
}
return (
<form onSubmit={handleSubmit}>
<input value={query} onChange={(e) => setQuery(e.target.value)} />
<button type="submit">Buscar</button>
</form>
);
}
Lifting state up
Cuando dos componentes necesitan el mismo dato, sube el estado al ancestro común.
function App() {
const [query, setQuery] = useState('');
return (
<>
<SearchBar query={query} onQueryChange={setQuery} />
<Results query={query} />
</>
);
}
useEffect
Úsalo para sincronizar con algo externo:
- peticiones
- listeners
- timers
- APIs del navegador
import { useEffect } from 'react';
useEffect(() => {
document.title = `Carrito (${count})`;
}, [count]);
Con limpieza
useEffect(() => {
function onResize() {
console.log(window.innerWidth);
}
window.addEventListener('resize', onResize);
return () => {
window.removeEventListener('resize', onResize);
};
}, []);
Cuándo no usarlo
No uses useEffect para:
- calcular valores derivados
- filtrar arrays
- concatenar strings
- sincronizar estado duplicado que no debería existir
useRef
useRef guarda un valor persistente entre renders sin provocar render cuando cambia.
import { useRef } from 'react';
function SearchInput() {
const inputRef = useRef(null);
function focusInput() {
inputRef.current?.focus();
}
return (
<>
<input ref={inputRef} />
<button onClick={focusInput}>Focus</button>
</>
);
}
Úsalo para:
useReducer
Cuando el estado tiene varias transiciones relacionadas, useReducer puede ser más claro que varios useState.
import { useReducer } from 'react';
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + 1 };
case 'reset':
return { ...state, count: 0 };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<>
<p>{state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
</>
);
}
Context y useContext
Para compartir datos transversales sin prop drilling:
import { createContext, useContext } from 'react';
const ThemeContext = createContext('light');
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar() {
const theme = useContext(ThemeContext);
return <p>Tema: {theme}</p>;
}
Bueno para:
- tema
- usuario autenticado
- idioma
- configuración global
No tan bueno para estado local que solo usan dos componentes cercanos.
Custom hooks
Un custom hook reutiliza lógica, no UI.
import { useEffect, useState } from 'react';
function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(navigator.onLine);
useEffect(() => {
function handleOnline() {
setIsOnline(true);
}
function handleOffline() {
setIsOnline(false);
}
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
return isOnline;
}
Suspense y lazy
Suspense se sigue usando. Su caso más claro es envolver componentes cargados con lazy.
import { lazy, Suspense } from 'react';
const SettingsPage = lazy(() => import('./SettingsPage'));
function App() {
return (
<Suspense fallback={<p>Cargando...</p>}>
<SettingsPage />
</Suspense>
);
}
Idea clave:
lazydivide códigoSuspensemuestra fallback mientras algo suspende
No asumas que cualquier fetch con useEffect ya está usando Suspense.
useTransition
Sirve para marcar actualizaciones no urgentes.
import { useState, useTransition } from 'react';
function Tabs() {
const [tab, setTab] = useState('home');
const [isPending, startTransition] = useTransition();
function selectTab(nextTab) {
startTransition(() => {
setTab(nextTab);
});
}
return (
<>
<button onClick={() => selectTab('posts')}>Posts</button>
{isPending && <p>Cargando vista...</p>}
<section>{tab}</section>
</>
);
}
useDeferredValue
Sirve para retrasar una parte derivada de la UI cuando el origen cambia muy rápido.
import { useDeferredValue, useState } from 'react';
function SearchPage() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
return (
<>
<input value={query} onChange={(e) => setQuery(e.target.value)} />
<Results query={deferredQuery} />
</>
);
}
Útil para búsquedas o listados pesados donde el input debe seguir respondiendo rápido.
React Router / react-router-dom
La parte de routing merece estar aquí porque en React real aparece enseguida.
La documentación oficial actual distingue tres modos:
DeclarativeDataFramework
Para la mayoría de proyectos React puros, lo primero que debes entender bien es el uso declarativo y el uso con RouterProvider.
Enfoque declarativo clásico
import { BrowserRouter, Routes, Route } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/products/:id" element={<ProductDetail />} />
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
);
}
Úsalo cuando:
Enfoque con RouterProvider
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
const router = createBrowserRouter([
{
path: '/',
element: <RootLayout />,
children: [
{ index: true, element: <Home /> },
{ path: 'about', element: <About /> },
{ path: 'products/:id', element: <ProductDetail /> },
],
},
]);
function App() {
return <RouterProvider router={router} />;
}
Úsalo cuando:
- quieres rutas más estructuradas
- quieres crecer hacia loaders/actions
- quieres centralizar mejor la configuración
Navegación
import { Link, NavLink, useNavigate } from 'react-router-dom';
<Link to="/about">About</Link>
<NavLink to="/dashboard" className={({ isActive }) => (isActive ? 'active' : '')}>
Dashboard
</NavLink>
const navigate = useNavigate();
navigate('/dashboard');
navigate(-1);
Params
<Route path="/users/:id" element={<UserDetail />} />
import { useParams } from 'react-router-dom';
function UserDetail() {
const { id } = useParams();
return <p>Usuario {id}</p>;
}
Query params
import { useSearchParams } from 'react-router-dom';
const [searchParams, setSearchParams] = useSearchParams();
const search = searchParams.get('search') ?? '';
setSearchParams({ search: 'react', page: '1' });
Útiles para:
- filtros
- paginación
- orden
- estado compartible por URL
Rutas anidadas con Outlet
import { Outlet } from 'react-router-dom';
function DashboardLayout() {
return (
<section>
<Sidebar />
<Outlet />
</section>
);
}
<Route path="/dashboard" element={<DashboardLayout />}>
<Route index element={<DashboardHome />} />
<Route path="settings" element={<Settings />} />
</Route>
Ruta protegida
import { Navigate } from 'react-router-dom';
function ProtectedRoute({ user, children }) {
return user ? children : <Navigate to="/login" replace />;
}
Regla rápida para elegir
BrowserRouter+Routes: simple y suficiente en muchas SPAsRouterProvider: mejor base cuando la app va a crecerHashRouter: solo si tienes una limitación real de servidor o hosting
Reglas de hooks
- llama hooks solo en el nivel superior del componente
- no los llames dentro de
if,foro funciones anidadas - llama hooks solo desde componentes React o desde hooks personalizados
Patrones mentales útiles
- UI = función del estado
- props bajan, eventos suben
- estado mínimo posible
- si se puede derivar, no lo guardes
- primero compón, luego abstrae
Errores comunes
- mutar estado directamente
- usar
key={index}sin criterio - pasar demasiadas props innecesarias
- meter toda la lógica en un solo componente
- usar
useEffectcomo parche universal
Snippets rápidos
// Clase condicional
<button className={isActive ? 'active' : 'idle'} />
// Renderizar nada
if (!user) return null;
// Estado booleano
const [open, setOpen] = useState(false);
setOpen((prev) => !prev);
// Prop de callback
<Modal onClose={() => setOpen(false)} />
Checklist mental antes de escribir un componente
- ¿Qué datos recibe?
- ¿Qué datos cambian?
- ¿Qué parte es estado real y qué parte se puede derivar?
- ¿Necesita efecto o solo render limpio?
- ¿Está haciendo una sola cosa o demasiadas?