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:

  • onClick
  • onChange
  • onSubmit
  • onFocus
  • onBlur
  • onKeyDown

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 local
  • setState provoca 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:

  • referencias al DOM
  • timers
  • instancias externas
  • valores mutables que no pintan UI

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:

  • lazy divide código
  • Suspense muestra 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:

  • Declarative
  • Data
  • Framework

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:

  • quieres simplicidad
  • la app es una SPA estándar
  • la carga de datos la llevas fuera del router

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
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 SPAs
  • RouterProvider: mejor base cuando la app va a crecer
  • HashRouter: 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, for o 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 useEffect como 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

  1. ¿Qué datos recibe?
  2. ¿Qué datos cambian?
  3. ¿Qué parte es estado real y qué parte se puede derivar?
  4. ¿Necesita efecto o solo render limpio?
  5. ¿Está haciendo una sola cosa o demasiadas?