Skip to content

Top 100 Preguntas Completas de React

Esta guía cubre desde fundamentos hasta temas avanzados. Las preguntas 1-50 están en las guías anteriores.


Estado Global y Gestión de Estado (51-65)

Section titled “Estado Global y Gestión de Estado (51-65)”

¿Qué es? Librería de gestión de estado global con flujo unidireccional: Action → Reducer → Store → View.

¿Cuándo usarlo?

  • Estado compartido entre muchos componentes
  • Lógica de estado compleja
  • Necesitas debugging avanzado (time-travel)
// Store
const store = createStore(rootReducer);
// Reducer
function counterReducer(state = { count: 0 }, action) {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
default:
return state;
}
}
// Dispatch
store.dispatch({ type: 'INCREMENT' });

¿Qué es? La forma oficial y moderna de usar Redux. Reduce el boilerplate significativamente.

Incluye: createSlice (reducer + actions), configureStore (setup automático), Immer (mutaciones inmutables).

import { createSlice, configureStore } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: (state) => {
state.value += 1; // Immer permite mutación
},
incrementByAmount: (state, action) => {
state.value += action.payload;
}
}
});
export const { increment, incrementByAmount } = counterSlice.actions;
const store = configureStore({
reducer: {
counter: counterSlice.reducer
}
});

¿Qué es? Librería de estado global minimalista. Solo 1KB, sin boilerplate, basada en hooks.

¿Para qué sirve? Alternativa simple a Redux cuando no necesitas toda su complejidad.

import { create } from 'zustand';
const useStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 })
}));
// Uso en componente
function Counter() {
const { count, increment } = useStore();
return <button onClick={increment}>{count}</button>;
}

¿Qué es? Librería de estado atómico. Cada pieza de estado es un “atom” independiente.

¿Para qué sirve? Estado granular sin re-renders innecesarios. Ideal para estado derivado.

import { atom, useAtom } from 'jotai';
// Atoms - unidades de estado
const countAtom = atom(0);
const doubleAtom = atom((get) => get(countAtom) * 2);
function Counter() {
const [count, setCount] = useAtom(countAtom);
const [double] = useAtom(doubleAtom);
return (
<div>
<p>{count} (double: {double})</p>
<button onClick={() => setCount(c => c + 1)}>+</button>
</div>
);
}

¿Qué es? Librería de estado atómico creada por Facebook. Similar a Jotai pero con más features.

Conceptos: atom (estado), selector (estado derivado), keys únicos obligatorios.

import { atom, selector, useRecoilState, useRecoilValue } from 'recoil';
const textState = atom({
key: 'textState',
default: ''
});
const charCountState = selector({
key: 'charCountState',
get: ({ get }) => get(textState).length
});
function CharacterCount() {
const count = useRecoilValue(charCountState);
return <p>Caracteres: {count}</p>;
}

56. ¿Cuál es la diferencia entre Redux, Zustand, Jotai y Recoil?

Section titled “56. ¿Cuál es la diferencia entre Redux, Zustand, Jotai y Recoil?”

Resumen: Redux es robusto pero complejo. Zustand es simple. Jotai/Recoil son atómicos (estado granular).

CaracterísticaReduxZustandJotaiRecoil
BoilerplateAltoBajoBajoMedio
DevToolsExcelenteBuenoBuenoBueno
Bundle sizeGrandePequeñoPequeñoMedio
Curva aprendizajeAltaBajaBajaMedia
PatrónFluxFlux-likeAtómicoAtómico

57. ¿Qué es React Query / TanStack Query?

Section titled “57. ¿Qué es React Query / TanStack Query?”

¿Qué es? Librería para manejar “server state” (datos del servidor). NO es para estado local.

¿Para qué sirve? Caching, refetch automático, loading/error states, invalidación, paginación.

import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
function Posts() {
const { data, isLoading, error } = useQuery({
queryKey: ['posts'],
queryFn: () => fetch('/api/posts').then(res => res.json())
});
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: (newPost) => fetch('/api/posts', {
method: 'POST',
body: JSON.stringify(newPost)
}),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['posts'] });
}
});
if (isLoading) return <Loading />;
if (error) return <Error />;
return <PostList posts={data} />;
}

¿Qué es? Librería de Vercel para data fetching. Nombre viene de “stale-while-revalidate”.

¿Para qué sirve? Similar a React Query pero más simple. Ideal para proyectos Next.js.

import useSWR from 'swr';
const fetcher = (url) => fetch(url).then(res => res.json());
function Profile() {
const { data, error, isLoading, mutate } = useSWR('/api/user', fetcher);
// Revalidar manualmente
const refresh = () => mutate();
// Actualización optimista
const updateName = async (newName) => {
mutate({ ...data, name: newName }, false);
await fetch('/api/user', { method: 'PUT', body: JSON.stringify({ name: newName }) });
mutate();
};
if (isLoading) return <Loading />;
return <div>{data.name}</div>;
}

Regla simple: React Query para datos del servidor, Redux para estado de la UI.

React QueryRedux
Server stateClient state
Caching automáticoCache manual
Refetch automáticoManual
Datos remotosDatos locales

60. ¿Qué es el patrón de estado derivado?

Section titled “60. ¿Qué es el patrón de estado derivado?”

¿Qué es? Calcular valores a partir de otro estado en vez de duplicar.

Regla: Si un valor puede calcularse de otro, NO lo guardes en estado separado.

// MAL - duplicar estado
const [items, setItems] = useState([]);
const [filteredItems, setFilteredItems] = useState([]); // Duplicado
// BIEN - derivar estado
const [items, setItems] = useState([]);
const [filter, setFilter] = useState('');
const filteredItems = useMemo(() =>
items.filter(item => item.name.includes(filter)),
[items, filter]
);

61. ¿Qué es el patrón de estado normalizado?

Section titled “61. ¿Qué es el patrón de estado normalizado?”

¿Qué es? Estructurar estado como base de datos: entidades separadas con IDs como referencias.

¿Para qué sirve? Evitar duplicación, facilitar actualizaciones, mejor performance.

// Desnormalizado (evitar)
const state = {
posts: [
{ id: 1, author: { id: 1, name: 'Juan' }, comments: [...] }
]
};
// Normalizado (preferido)
const state = {
posts: {
byId: { 1: { id: 1, authorId: 1, commentIds: [1, 2] } },
allIds: [1]
},
users: {
byId: { 1: { id: 1, name: 'Juan' } },
allIds: [1]
},
comments: {
byId: { 1: {...}, 2: {...} },
allIds: [1, 2]
}
};

¿Qué es? Guardar estado en localStorage/sessionStorage para que sobreviva recargas de página.

¿Cómo? Usar middleware de persistencia (Zustand, Redux Persist) o custom hooks.

// Con Zustand
import { persist } from 'zustand/middleware';
const useStore = create(
persist(
(set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 }))
}),
{
name: 'counter-storage',
storage: createJSONStorage(() => localStorage)
}
)
);

¿Qué es? Funciones que extraen y transforman datos del estado global.

¿Para qué sirve? Encapsular lógica de acceso, memoizar cálculos, desacoplar componentes del shape del estado.

// Selector - función que extrae datos del estado
const selectTodos = (state) => state.todos;
const selectCompletedTodos = (state) =>
state.todos.filter(todo => todo.completed);
// Con reselect (memoizado)
import { createSelector } from 'reselect';
const selectCompletedTodos = createSelector(
[selectTodos],
(todos) => todos.filter(todo => todo.completed)
);

¿Qué es? Funciones que interceptan actions antes de llegar al reducer.

¿Para qué sirve? Logging, async operations, analytics, validaciones.

// Middleware personalizado
const loggerMiddleware = (store) => (next) => (action) => {
console.log('Dispatching:', action);
const result = next(action);
console.log('Next state:', store.getState());
return result;
};
// Configurar
const store = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(loggerMiddleware)
});

Thunk: Middleware simple para async. Actions pueden ser funciones.

Saga: Middleware avanzado con generators. Para flujos complejos (cancelación, retry, race conditions).

// Thunk - funciones async simples
const fetchUser = (id) => async (dispatch) => {
dispatch({ type: 'FETCH_START' });
try {
const user = await api.getUser(id);
dispatch({ type: 'FETCH_SUCCESS', payload: user });
} catch (error) {
dispatch({ type: 'FETCH_ERROR', error });
}
};
// Saga - generators para flujos complejos
function* fetchUserSaga(action) {
try {
const user = yield call(api.getUser, action.payload);
yield put({ type: 'FETCH_SUCCESS', payload: user });
} catch (error) {
yield put({ type: 'FETCH_ERROR', error });
}
}

¿Qué es? React 18 agrupa múltiples actualizaciones de estado en UN solo re-render.

Antes (React 17): Solo en event handlers. Ahora: En todas partes (setTimeout, promises, etc.).

// React 17 - solo batching en event handlers
// React 18 - batching automático en todas partes
function handleClick() {
setCount(c => c + 1);
setFlag(f => !f);
// React 18: un solo re-render
}
// Incluso en async
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// React 18: un solo re-render
}, 1000);
// Para forzar render inmediato
import { flushSync } from 'react-dom';
flushSync(() => setCount(c => c + 1));

¿Qué es? Componentes que se ejecutan SOLO en el servidor. No se envían al cliente.

Beneficios: Acceso directo a BD, bundle más pequeño, mejor SEO, datos sensibles seguros.

// Server Component (por defecto en Next.js 13+)
// - Se ejecuta en el servidor
// - No tiene estado ni efectos
// - Puede acceder directamente a BD
async function Posts() {
const posts = await db.posts.findMany();
return (
<ul>
{posts.map(post => <li key={post.id}>{post.title}</li>)}
</ul>
);
}
// Client Component
'use client';
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}

68. ¿Cuál es la diferencia entre Server y Client Components?

Section titled “68. ¿Cuál es la diferencia entre Server y Client Components?”

Regla simple: Server por defecto, Client solo cuando necesitas interactividad (estado, eventos, hooks de browser).

Server ComponentsClient Components
Se ejecutan en servidorSe ejecutan en cliente
Sin estado/efectosCon estado/efectos
Acceso directo a BDFetch API
No incluidos en bundleIncluidos en bundle
Por defectoRequieren ‘use client’

¿Qué es? Enviar HTML progresivamente mientras se renderiza, en vez de esperar a que todo esté listo.

Beneficio: El usuario ve contenido más rápido. Suspense muestra fallbacks mientras carga.

// Permite enviar HTML progresivamente
import { Suspense } from 'react';
function Page() {
return (
<div>
<Header /> {/* Se envía inmediatamente */}
<Suspense fallback={<PostsSkeleton />}>
<Posts /> {/* Se envía cuando esté listo */}
</Suspense>
<Suspense fallback={<CommentsSkeleton />}>
<Comments /> {/* Se envía cuando esté listo */}
</Suspense>
</div>
);
}

¿Qué es? Hook de React 19 que puede leer promises y context directamente.

¿Para qué sirve? Simplificar data fetching con Suspense. Puede usarse condicionalmente (a diferencia de otros hooks).

// React 19 - hook use para promises y context
import { use } from 'react';
function Comments({ commentsPromise }) {
// Suspende hasta que la promise resuelva
const comments = use(commentsPromise);
return comments.map(c => <Comment key={c.id} {...c} />);
}
// También para context
function Theme() {
const theme = use(ThemeContext);
return <div style={{ color: theme.color }}>...</div>;
}

¿Qué es? Funciones asíncronas que se ejecutan en el servidor, llamadas desde el cliente.

¿Para qué sirve? Mutaciones de datos sin crear API routes. Se marcan con 'use server'.

// Server Action - función que se ejecuta en servidor
async function createPost(formData) {
'use server';
const title = formData.get('title');
await db.posts.create({ data: { title } });
revalidatePath('/posts');
}
// Uso en componente
function NewPostForm() {
return (
<form action={createPost}>
<input name="title" />
<button type="submit">Crear</button>
</form>
);
}

¿Qué es? Hook que da información sobre el estado de un form padre (pending, data, method, action).

¿Para qué sirve? Mostrar loading en botones de submit mientras el form se procesa.

'use client';
import { useFormStatus } from 'react-dom';
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? 'Enviando...' : 'Enviar'}
</button>
);
}

¿Qué es? Hook que maneja estado basado en el resultado de una Server Action.

¿Para qué sirve? Mostrar errores de validación o mensajes de éxito después de submit.

'use client';
import { useFormState } from 'react-dom';
async function createUser(prevState, formData) {
'use server';
const name = formData.get('name');
if (!name) {
return { error: 'Nombre requerido' };
}
await db.users.create({ data: { name } });
return { success: true };
}
function Form() {
const [state, formAction] = useFormState(createUser, {});
return (
<form action={formAction}>
<input name="name" />
{state.error && <p>{state.error}</p>}
<button>Crear</button>
</form>
);
}

¿Qué es? Hook para actualizaciones optimistas: mostrar el resultado esperado ANTES de que el servidor confirme.

¿Para qué sirve? UI instantánea. Si falla, se revierte automáticamente.

'use client';
import { useOptimistic } from 'react';
function Messages({ messages }) {
const [optimisticMessages, addOptimisticMessage] = useOptimistic(
messages,
(state, newMessage) => [...state, { ...newMessage, sending: true }]
);
async function sendMessage(formData) {
const message = formData.get('message');
addOptimisticMessage({ text: message });
await deliverMessage(message);
}
return (
<>
{optimisticMessages.map(m => (
<div key={m.id} style={{ opacity: m.sending ? 0.5 : 1 }}>
{m.text}
</div>
))}
<form action={sendMessage}>
<input name="message" />
</form>
</>
);
}

75. ¿Qué es React Compiler (React Forget)?

Section titled “75. ¿Qué es React Compiler (React Forget)?”

¿Qué es? Compilador que automáticamente memoriza componentes y valores.

¿Para qué sirve? Elimina la necesidad de useMemo, useCallback y React.memo manuales. Optimización automática.

¿Qué es? Funciones async que se pasan a startTransition. Manejan pending states automáticamente.

¿Para qué sirve? Simplificar manejo de loading/error en operaciones asíncronas.

// Actions - funciones async que manejan transiciones
function UpdateName() {
const [name, setName] = useState('');
const [error, setError] = useState(null);
const [isPending, startTransition] = useTransition();
const handleSubmit = () => {
startTransition(async () => {
const error = await updateName(name);
if (error) {
setError(error);
return;
}
redirect('/profile');
});
};
}

77. ¿Qué es Document Metadata en React 19?

Section titled “77. ¿Qué es Document Metadata en React 19?”

¿Qué es? Capacidad de definir <title>, <meta>, <link> directamente en componentes.

¿Para qué sirve? SEO dinámico sin librerías externas. React los mueve automáticamente al <head>.

// Metadata directamente en componentes
function BlogPost({ post }) {
return (
<article>
<title>{post.title}</title>
<meta name="description" content={post.excerpt} />
<link rel="canonical" href={post.url} />
<h1>{post.title}</h1>
<p>{post.content}</p>
</article>
);
}

¿Qué es? APIs para precargar recursos (DNS, conexiones, fonts, scripts).

¿Para qué sirve? Mejorar performance cargando recursos antes de necesitarlos.

// Precargar recursos
import { prefetchDNS, preconnect, preload, preinit } from 'react-dom';
function Component() {
prefetchDNS('https://api.example.com');
preconnect('https://api.example.com');
preload('/fonts/font.woff2', { as: 'font' });
preinit('/scripts/analytics.js', { as: 'script' });
return <div>...</div>;
}

¿Qué es? En React 19, ref es una prop normal. Ya no necesitas forwardRef.

Beneficio: Código más simple y menos boilerplate.

// React 19 - ref como prop normal (sin forwardRef)
function Input({ ref, ...props }) {
return <input ref={ref} {...props} />;
}
// Uso
function Form() {
const inputRef = useRef(null);
return <Input ref={inputRef} />;
}

80. ¿Qué es el Context como provider en React 19?

Section titled “80. ¿Qué es el Context como provider en React 19?”

¿Qué es? En React 19, el Context se usa directamente como provider sin .Provider.

Antes: <ThemeContext.Provider>. Ahora: <ThemeContext>.

// React 19 - Context directamente como provider
const ThemeContext = createContext('light');
function App() {
return (
<ThemeContext value="dark">
<Page />
</ThemeContext>
);
}
// Antes (React 18)
<ThemeContext.Provider value="dark">
<Page />
</ThemeContext.Provider>

¿Qué es? Framework full-stack de React creado por Vercel. El más popular para producción.

Incluye:

  • Server-Side Rendering (SSR)
  • Static Site Generation (SSG)
  • API Routes
  • File-based routing
  • Server Components

82. ¿Cuál es la diferencia entre SSR, SSG e ISR?

Section titled “82. ¿Cuál es la diferencia entre SSR, SSG e ISR?”

SSR (Server-Side Rendering): HTML generado en cada request. SSG (Static Site Generation): HTML generado en build time. ISR (Incremental Static Regeneration): SSG + revalidación periódica.

MétodoCuándo generaUso
SSRCada requestDatos dinámicos
SSGBuild timeContenido estático
ISRBuild + revalidaciónEstático con actualizaciones

¿Qué es? Framework de React enfocado en web standards y progressive enhancement.

Características:

  • Web standards (forms, fetch)
  • Progressive enhancement
  • Nested routing
  • Data loading/mutations

¿Qué es? Build tool moderno que reemplaza a Create React App. Creado por Evan You (Vue).

Ventajas:

  • Dev server ultra rápido (ESM nativo)
  • HMR instantáneo
  • Build optimizado con Rollup
  • Soporte TypeScript nativo

¿Qué es? Herramienta para desarrollar y documentar componentes UI en aislamiento.

¿Para qué sirve? Crear catálogo de componentes, testing visual, documentación interactiva.

Button.stories.jsx
export default {
title: 'Components/Button',
component: Button
};
export const Primary = {
args: {
variant: 'primary',
children: 'Click me'
}
};
export const Secondary = {
args: {
variant: 'secondary',
children: 'Click me'
}
};

86. ¿Qué es Playwright/Cypress para testing E2E?

Section titled “86. ¿Qué es Playwright/Cypress para testing E2E?”

¿Qué es? Herramientas para testing end-to-end (simular usuario real en browser).

Playwright: Más rápido, multi-browser. Cypress: Más fácil de usar, mejor DX.

// Playwright
import { test, expect } from '@playwright/test';
test('login flow', async ({ page }) => {
await page.goto('/login');
await page.fill('[name="email"]', 'test@test.com');
await page.fill('[name="password"]', 'password');
await page.click('button[type="submit"]');
await expect(page).toHaveURL('/dashboard');
});

¿Qué es? Librería para mockear APIs interceptando requests a nivel de red.

¿Para qué sirve? Testing sin backend real, desarrollo offline, simular errores.

import { rest } from 'msw';
import { setupServer } from 'msw/node';
const server = setupServer(
rest.get('/api/users', (req, res, ctx) => {
return res(ctx.json([{ id: 1, name: 'John' }]));
})
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

¿Qué es? Framework para construir apps móviles nativas usando React.

¿Cómo funciona? Comparte lógica con web pero usa componentes nativos (View, Text, etc. en vez de div, p).

¿Qué es? Plataforma que simplifica React Native. Recomendada para empezar.

Incluye:

  • Sin configuración nativa
  • OTA updates
  • SDK con APIs comunes
  • Expo Go para testing

¿Qué es? Librería de animaciones declarativas para React.

¿Para qué sirve? Animaciones complejas con API simple: entrada, salida, gestos, layout animations.

import { motion } from 'framer-motion';
function Component() {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.3 }}
>
Contenido animado
</motion.div>
);
}

91. ¿Cómo estructurar un proyecto React grande?

Section titled “91. ¿Cómo estructurar un proyecto React grande?”

Principio: Agrupar por feature/dominio, no por tipo de archivo.

Estructura recomendada:

src/
├── components/ # Componentes reutilizables
│ ├── ui/ # Componentes UI básicos
│ └── forms/ # Componentes de formulario
├── features/ # Features por dominio
│ ├── auth/
│ │ ├── components/
│ │ ├── hooks/
│ │ ├── api/
│ │ └── index.js
│ └── posts/
├── hooks/ # Hooks globales
├── lib/ # Utilidades
├── services/ # API clients
├── stores/ # Estado global
└── pages/ # Páginas/rutas

¿Qué es? Arquitectura que organiza código por features y capas con reglas estrictas de dependencias.

Capas (de mayor a menor):

  • app/ - Configuración global
  • pages/ - Composición de features
  • widgets/ - Bloques de UI complejos
  • features/ - Funcionalidades de negocio
  • entities/ - Entidades de dominio
  • shared/ - Código reutilizable

Patrón común: Context + Provider que maneja estado de usuario, login, logout, y verificación de sesión.

// Context de autenticación
const AuthContext = createContext(null);
function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Verificar sesión al cargar
checkAuth().then(setUser).finally(() => setLoading(false));
}, []);
const login = async (credentials) => {
const user = await api.login(credentials);
setUser(user);
};
const logout = async () => {
await api.logout();
setUser(null);
};
return (
<AuthContext.Provider value={{ user, login, logout, loading }}>
{children}
</AuthContext.Provider>
);
}

94. ¿Cómo implementar internacionalización (i18n)?

Section titled “94. ¿Cómo implementar internacionalización (i18n)?”

¿Qué es? Soporte para múltiples idiomas en la aplicación.

Librería popular: react-i18next - archivos JSON con traducciones + hook useTranslation.

// Con react-i18next
import { useTranslation } from 'react-i18next';
function Welcome() {
const { t, i18n } = useTranslation();
return (
<div>
<h1>{t('welcome.title')}</h1>
<button onClick={() => i18n.changeLanguage('es')}>
Español
</button>
</div>
);
}
// translations/es.json
{
"welcome": {
"title": "Bienvenido"
}
}

95. ¿Cómo implementar temas (dark/light mode)?

Section titled “95. ¿Cómo implementar temas (dark/light mode)?”

Patrón: Context + CSS variables. Guardar preferencia en localStorage.

const ThemeContext = createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = useState(() =>
localStorage.getItem('theme') || 'light'
);
useEffect(() => {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
}, [theme]);
const toggle = () => setTheme(t => t === 'light' ? 'dark' : 'light');
return (
<ThemeContext.Provider value={{ theme, toggle }}>
{children}
</ThemeContext.Provider>
);
}

96. ¿Cómo manejar formularios complejos?

Section titled “96. ¿Cómo manejar formularios complejos?”

Librerías recomendadas: React Hook Form (performance) + Zod (validación con TypeScript).

// Con React Hook Form
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
const schema = z.object({
email: z.string().email(),
password: z.string().min(8)
});
function LoginForm() {
const { register, handleSubmit, formState: { errors } } = useForm({
resolver: zodResolver(schema)
});
const onSubmit = (data) => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register('email')} />
{errors.email && <span>{errors.email.message}</span>}
<input type="password" {...register('password')} />
{errors.password && <span>{errors.password.message}</span>}
<button type="submit">Login</button>
</form>
);
}

¿Qué es? Cargar más contenido automáticamente cuando el usuario llega al final de la lista.

¿Cómo? Usar IntersectionObserver para detectar cuando el último elemento es visible.

function InfiniteList() {
const [items, setItems] = useState([]);
const [page, setPage] = useState(1);
const [hasMore, setHasMore] = useState(true);
const observerRef = useRef();
const lastItemRef = useRef();
useEffect(() => {
observerRef.current = new IntersectionObserver(entries => {
if (entries[0].isIntersecting && hasMore) {
setPage(p => p + 1);
}
});
if (lastItemRef.current) {
observerRef.current.observe(lastItemRef.current);
}
return () => observerRef.current?.disconnect();
}, [hasMore, items]);
useEffect(() => {
fetchItems(page).then(newItems => {
setItems(prev => [...prev, ...newItems]);
setHasMore(newItems.length > 0);
});
}, [page]);
return (
<ul>
{items.map((item, i) => (
<li key={item.id} ref={i === items.length - 1 ? lastItemRef : null}>
{item.name}
</li>
))}
</ul>
);
}

98. ¿Cómo implementar WebSockets en React?

Section titled “98. ¿Cómo implementar WebSockets en React?”

¿Qué es? Conexión bidireccional en tiempo real entre cliente y servidor.

¿Para qué sirve? Chat, notificaciones en vivo, actualizaciones en tiempo real.

function useWebSocket(url) {
const [messages, setMessages] = useState([]);
const [isConnected, setIsConnected] = useState(false);
const wsRef = useRef(null);
useEffect(() => {
wsRef.current = new WebSocket(url);
wsRef.current.onopen = () => setIsConnected(true);
wsRef.current.onclose = () => setIsConnected(false);
wsRef.current.onmessage = (e) => {
setMessages(prev => [...prev, JSON.parse(e.data)]);
};
return () => wsRef.current?.close();
}, [url]);
const send = useCallback((data) => {
wsRef.current?.send(JSON.stringify(data));
}, []);
return { messages, isConnected, send };
}

Estrategia: Error Boundary para errores de render + event listener para errores async.

// Error Boundary global
class GlobalErrorBoundary extends React.Component {
state = { hasError: false, error: null };
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, info) {
// Enviar a servicio de monitoreo
errorService.log(error, info);
}
render() {
if (this.state.hasError) {
return <ErrorPage error={this.state.error} />;
}
return this.props.children;
}
}
// Para errores async
window.addEventListener('unhandledrejection', (event) => {
errorService.log(event.reason);
});

100. ¿Cuáles son las mejores prácticas de React?

Section titled “100. ¿Cuáles son las mejores prácticas de React?”

Resumen de buenas prácticas:

  1. Componentes pequeños y enfocados
  2. Composición sobre herencia
  3. Estado lo más local posible
  4. Evitar prop drilling con Context/estado global
  5. Usar TypeScript
  6. Testing desde el principio
  7. Memoización solo cuando sea necesario
  8. Separar lógica de UI (custom hooks)
  9. Nombrar componentes descriptivamente
  10. Documentar componentes complejos

🐝