Top 50 Preguntas Frecuentes de React
Las 50 Preguntas Más Frecuentes de React
Section titled “Las 50 Preguntas Más Frecuentes de React”Guía completa con las preguntas que aparecen en la mayoría de entrevistas técnicas.
1. ¿Qué es JSX?
Section titled “1. ¿Qué es JSX?”Definición profesional:
Extensión de sintaxis para JavaScript que permite escribir estructura similar a HTML directamente en JS. Babel lo transpila a llamadas React.createElement().
// JSXconst element = <h1 className="titulo">Hola</h1>;
// Equivalente compiladoconst element = React.createElement('h1', { className: 'titulo' }, 'Hola');
// JSX soporta expresiones JSconst nombre = "Juan";const greeting = <h1>Hola, {nombre.toUpperCase()}</h1>;Cómo responder en entrevista:
“JSX es azúcar sintáctico sobre
React.createElement(). No es HTML sino JavaScript: usaclassNameen vez declass,htmlForen vez defor, y todos los atributos son camelCase. Las llaves{}permiten insertar cualquier expresión JavaScript. Se compila en tiempo de build, no en runtime.”
2. ¿Por qué usamos className en vez de class?
Section titled “2. ¿Por qué usamos className en vez de class?”Definición profesional:
class es una palabra reservada de JavaScript (para definir clases). JSX al ser JavaScript usa className para el atributo CSS y htmlFor para el atributo for de los labels.
// HTML<div class="contenedor"><label for="email">Email</label></div>
// JSX equivalente<div className="contenedor"><label htmlFor="email">Email</label></div>Cómo responder en entrevista:
“JSX se compila a JavaScript, donde
classes una palabra reservada para definir clases. React usaclassNamepara evitar el conflicto. Similarmente,foren labels se convierte enhtmlFor. Estos son los dos casos más comunes donde JSX difiere del HTML estándar.”
3. ¿Qué son los Fragments?
Section titled “3. ¿Qué son los Fragments?”Definición profesional:
Componente especial <React.Fragment> (o <>) que agrupa múltiples elementos sin añadir nodos extra al DOM. Útil cuando un componente debe retornar varios elementos sin un wrapper innecesario.
// Sin Fragment - agrega un <div> innecesario al DOMfunction Ejemplo() {return ( <div> <h1>Título</h1> <p>Párrafo</p> </div>);}
// Con Fragment - sin nodo extra en el DOMfunction Ejemplo() {return ( <> <h1>Título</h1> <p>Párrafo</p> </>);}Cómo responder en entrevista:
“Los Fragments resuelven que React requiere un único elemento raíz sin agregar divs inútiles al DOM. La sintaxis corta
<>es preferible. Uso<React.Fragment key={id}>(con key) cuando necesito iterar sobre ellos en listas.”
4. ¿Qué es el renderizado condicional?
Section titled “4. ¿Qué es el renderizado condicional?”Definición profesional:
Técnicas para renderizar JSX condicionalmente según el estado o props: operador &&, ternario ? :, return temprano, o variable auxiliar.
function Componente({ isLoggedIn }) {// Return temprano - para casos muy diferentesif (!isLoggedIn) return <Login />;
return ( <div> {/* && - cuando solo hay un caso positivo */} {isLoggedIn && <UserPanel />}
{/* Ternario - cuando hay dos alternativas */} {isLoggedIn ? <UserPanel /> : <Login />} </div>);}Cómo responder en entrevista:
“Prefiero el return temprano para condiciones que cambian completamente el componente. El operador
&¶ mostrar/ocultar algo. El ternario para alternar entre dos opciones. Cuidado con&&: si la condición es0, React renderiza0. Mejor usarBoolean(condition) &&o ternario.”
5. ¿Cómo renderizar listas?
Section titled “5. ¿Cómo renderizar listas?”Definición profesional:
Con .map() transformando arrays en elementos JSX. Cada elemento requiere una prop key única y estable para que React identifique cambios eficientemente.
function Lista({ items }) {return ( <ul> {items.map(item => ( <li key={item.id}>{item.nombre}</li> ))} </ul>);}
// NO usar índice como key - causa bugs con reordenamiento// {items.map((item, i) => <li key={i}>{item.nombre}</li>)}Cómo responder en entrevista:
“Uso
.map()y cada elemento necesitakeyúnica y estable. Lakeyle dice a React qué elementos cambiaron, se agregaron o eliminaron. Nunca uso el índice como key: si reordeno la lista, React reutiliza nodos incorrectamente causando bugs de estado. Prefiero IDs de la base de datos.”
6. ¿Qué es prop drilling y cómo evitarlo?
Section titled “6. ¿Qué es prop drilling y cómo evitarlo?”Definición profesional: Anti-patrón donde los props pasan por múltiples componentes intermedios que no los usan, solo para hacer llegar datos a un componente profundo.
// Prop drilling - componentes intermedios no usan user<App user={user}><Layout user={user}> <Sidebar user={user}> <UserMenu user={user} /> {/* Solo este lo usa */} </Sidebar></Layout></App>
// Solución: Context APIconst UserContext = createContext();// Proveer en App, consumir directamente en UserMenuCómo responder en entrevista:
“Prop drilling hace el código frágil: cambiar el tipo de dato requiere modificar todos los componentes intermedios. Las soluciones son: Context API (para datos globales como tema, usuario), estado global con Zustand/Redux, o composición de componentes (pasar elementos ya construidos como children o props).“
7. ¿Qué es Context API?
Section titled “7. ¿Qué es Context API?”Definición profesional:
Sistema de React para compartir datos entre componentes sin prop drilling. Basado en tres piezas: createContext, Provider y useContext.
const UserContext = createContext(null);
// Provider - envuelve los componentes que necesitan el datofunction App() {const [user, setUser] = useState({ nombre: 'Juan' });return ( <UserContext.Provider value={{ user, setUser }}> <Router /> </UserContext.Provider>);}
// Consumer - cualquier nivel sin prop drillingfunction UserMenu() {const { user } = useContext(UserContext);return <span>Hola, {user.nombre}</span>;}Cómo responder en entrevista:
“Context API permite compartir datos sin prop drilling. Crea el contexto con
createContext, envuelve conProvidery consume conuseContext. El problema: cuando el valor del Provider cambia, TODOS los consumidores se re-renderizan. Para estado con muchas actualizaciones prefiero Zustand o Redux.”
8. ¿Cuándo usar Context vs Redux?
Section titled “8. ¿Cuándo usar Context vs Redux?”Definición profesional: Context es nativo pero provoca re-renders globales. Redux (y Zustand) ofrecen suscripciones selectivas, middleware y DevTools.
| Context | Redux/Zustand |
|---|---|
| Estado simple | Estado complejo |
| Pocas actualizaciones | Actualizaciones frecuentes |
| Sin middleware | Necesitas middleware |
| Built-in | Dependencia extra |
Cómo responder en entrevista:
“Uso Context para datos que cambian poco: tema, locale, usuario autenticado. Para estado con actualizaciones frecuentes (carrito, UI state) prefiero Zustand: más ligero que Redux, sin boilerplate. Redux sigue siendo válido para apps grandes que necesitan DevTools avanzados y middleware.”
9. ¿Qué es useRef?
Section titled “9. ¿Qué es useRef?”Definición profesional:
Hook que retorna un objeto mutable { current } que persiste entre renders. Cambiar .current no provoca re-render. Tiene dos usos principales: acceder al DOM y almacenar valores mutables sin causar renders.
function Formulario() {const inputRef = useRef(null);const renderCount = useRef(0);
useEffect(() => { inputRef.current.focus(); // Acceso directo al DOM renderCount.current++; // Contador sin re-render});
return <input ref={inputRef} />;}Cómo responder en entrevista:
“
useReftiene dos casos: acceso al DOM (focus, scroll, medidas) y valores mutables que no deben causar re-render (timers, contadores de render, valores previos). La diferencia conuseState: cambiar.currentno re-renderiza. Para guardar el valor anterior de una prop, usouseRefen unuseEffect.”
10. ¿Qué es useCallback?
Section titled “10. ¿Qué es useCallback?”Definición profesional:
Hook que memoriza una función, retornando la misma referencia entre renders mientras sus dependencias no cambien. Evita que componentes hijos con React.memo se re-rendericen por recibir una nueva referencia de función.
const Boton = React.memo(({ onClick, texto }) => {console.log('Boton renderizado');return <button onClick={onClick}>{texto}</button>;});
function Formulario() {const [nombre, setNombre] = useState('');
// Sin useCallback: nueva función cada render → Boton se re-renderiza// Con useCallback: misma función → Boton NO se re-renderiza al escribirconst handleSubmit = useCallback(() => { console.log('Enviado:', nombre);}, [nombre]);
return ( <> <input value={nombre} onChange={e => setNombre(e.target.value)} /> <Boton onClick={handleSubmit} texto="Enviar" /> </>);}Cómo responder en entrevista:
“
useCallbackmemoriza la referencia de una función. Úsalo cuando pasas funciones a componentes conReact.memoo cuando una función es dependencia deuseEffect. Sin él, cada render crea una nueva función, rompiendo la memoización.useCallback(fn, deps)equivale auseMemo(() => fn, deps).”
11. ¿Qué es useMemo?
Section titled “11. ¿Qué es useMemo?”Definición profesional: Hook que memoriza el resultado de un cálculo costoso, recomputándolo solo cuando sus dependencias cambian.
function ListaUsuarios({ usuarios, filtro }) {// Sin useMemo: filtra en CADA render aunque filtro no cambie// Con useMemo: solo filtra cuando usuarios o filtro cambianconst usuariosFiltrados = useMemo(() => { return usuarios.filter(user => user.nombre.toLowerCase().includes(filtro.toLowerCase()) );}, [usuarios, filtro]);
return ( <ul> {usuariosFiltrados.map(user => ( <li key={user.id}>{user.nombre}</li> ))} </ul>);}Cómo responder en entrevista:
“
useMemomemoriza el valor retornado por una función. Úsalo para cálculos costosos (filtrar listas grandes, transformaciones de datos) o para crear objetos que serán props de componentes memorizados. No abusar: tiene overhead propio. React puede descartar el caché en casos especiales (optimización de memoria).“
12. ¿Cuál es la diferencia entre useMemo y useCallback?
Section titled “12. ¿Cuál es la diferencia entre useMemo y useCallback?”Definición profesional:
useMemo memoriza un valor (resultado de ejecutar la función). useCallback memoriza la función en sí misma.
function Ejemplo({ items }) {// useMemo - memoriza el VALOR (número)const total = useMemo( () => items.reduce((sum, item) => sum + item.precio, 0), [items]); // total = 150
// useCallback - memoriza la FUNCIÓNconst calcularTotal = useCallback( () => items.reduce((sum, item) => sum + item.precio, 0), [items]); // calcularTotal = () => {...}
// Son equivalentes:// useCallback(fn, deps) === useMemo(() => fn, deps)}Cómo responder en entrevista:
“
useMemoretorna el resultado del cálculo;useCallbackretorna la función.useCallback(fn, deps)es equivalente auseMemo(() => fn, deps). UsauseMemopara valores derivados costosos. UsauseCallbackpara funciones que pasas como props o dependencias de efectos.”
13. ¿Qué es React.memo?
Section titled “13. ¿Qué es React.memo?”Definición profesional: Higher-Order Component que envuelve un componente y evita que se re-renderice si sus props no han cambiado (comparación superficial). Optimización de rendimiento.
const ProductoItem = React.memo(function ProductoItem({ producto, onAgregar }) {console.log('Renderizando:', producto.nombre);return ( <div> <h3>{producto.nombre}</h3> <button onClick={() => onAgregar(producto.id)}>Agregar</button> </div>);});
function ListaProductos({ productos }) {// useCallback necesario: sin él, onAgregar es nueva función cada render// y React.memo no sirve de nadaconst onAgregar = useCallback(id => setCarrito(p => [...p, id]), []);return productos.map(p => <ProductoItem key={p.id} producto={p} onAgregar={onAgregar} />);}Cómo responder en entrevista:
“
React.memohace comparación superficial de props. Si las props no cambian, salta el render. Para que funcione con funciones/objetos como props, necesitouseCallback/useMemoen el padre. Acepta un segundo argumento de comparación personalizada. No memoizar todo: tiene overhead. Solo donde hay renders costosos confirmados.”
14. ¿Qué es useReducer?
Section titled “14. ¿Qué es useReducer?”Definición profesional:
Hook alternativo a useState que centraliza la lógica de actualización de estado complejo en una función reducer pura. Inspirado en el patrón Redux.
function formReducer(state, action) {switch (action.type) { case 'SET_FIELD': return { ...state, [action.field]: action.value }; case 'SUBMIT_START': return { ...state, isSubmitting: true }; case 'SUBMIT_SUCCESS': return { ...initialState }; default: return state;}}
function Formulario() {const [state, dispatch] = useReducer(formReducer, { nombre: '', email: '', isSubmitting: false});
return ( <input value={state.nombre} onChange={e => dispatch({ type: 'SET_FIELD', field: 'nombre', value: e.target.value })} />);}Cómo responder en entrevista:
“
useReducercentraliza la lógica de estado en un reducer puro:(state, action) => newState. Úsalo cuando el estado tiene múltiples valores relacionados, cuando las transiciones son complejas, o cuando el siguiente estado depende del anterior. El reducer es testeable de forma aislada. Es la base conceptual de Redux.”
15. ¿Cuándo usar useState vs useReducer?
Section titled “15. ¿Cuándo usar useState vs useReducer?”Definición profesional:
useState para valores simples e independientes. useReducer cuando varios estados están relacionados y sus transiciones son complejas.
| useState | useReducer |
|---|---|
| Estado simple | Estado complejo |
| Transiciones simples | Muchas transiciones |
| Valores independientes | Valores relacionados |
// Señal de migrar a useReducer:const [nombre, setNombre] = useState('');const [email, setEmail] = useState('');const [loading, setLoading] = useState(false);const [error, setError] = useState(null);// 4+ useState relacionados → useReducer
const [state, dispatch] = useReducer(formReducer, {nombre: '', email: '', loading: false, error: null});Cómo responder en entrevista:
“La regla práctica: cuando tengo 3 o más
useStateque se actualizan juntos o son interdependientes, migro auseReducer. También cuando la lógica de actualización es compleja y quiero testearla de forma aislada.useStatesigue siendo preferible para valores simples como un booleano o un string.”
16. ¿Cuál es el ciclo de vida de un componente?
Section titled “16. ¿Cuál es el ciclo de vida de un componente?”Definición profesional:
Tres fases: Montaje (se crea e inserta en el DOM), Actualización (re-render por cambio de props o estado) y Desmontaje (se elimina). Con hooks funcionales, useEffect maneja las tres fases.
// Montaje: [] vacío = solo al montaruseEffect(() => {console.log('Montado');}, []);
// Actualización: con dependenciasuseEffect(() => {console.log('dependency cambió');}, [dependency]);
// Desmontaje: función de cleanupuseEffect(() => {const sub = subscribe();return () => sub.unsubscribe(); // Cleanup = desmontaje}, []);Cómo responder en entrevista:
“En componentes funcionales,
useEffectcon[]vacío equivale acomponentDidMount. Con dependencias escomponentDidUpdate. La función de retorno escomponentWillUnmount. A diferencia de las clases, un componente puede tener múltiplesuseEffectseparados por preocupación.”
17. ¿Por qué useEffect se ejecuta dos veces en desarrollo?
Section titled “17. ¿Por qué useEffect se ejecuta dos veces en desarrollo?”Definición profesional:
React 18 con StrictMode monta, desmonta y vuelve a montar componentes intencionalmente en desarrollo para detectar efectos sin cleanup correcto. En producción solo se ejecuta una vez.
// StrictMode en index.jsx causa doble ejecución en desarrolloroot.render(<React.StrictMode> <App /></React.StrictMode>);
// Solución: siempre incluir cleanupuseEffect(() => {const controller = new AbortController();fetchData(controller.signal);return () => controller.abort(); // Cleanup correcto}, []);Cómo responder en entrevista:
“React 18 StrictMode ejecuta efectos dos veces en desarrollo a propósito: monta, desmonta y remonta para verificar que el cleanup funciona correctamente. Si tu efecto se rompe con doble ejecución, le falta cleanup. En producción no ocurre.”
18. ¿Cómo hacer fetch de datos correctamente?
Section titled “18. ¿Cómo hacer fetch de datos correctamente?”Definición profesional:
El patrón correcto incluye: manejo de loading/error, cancelación con AbortController si el componente se desmonta, y evitar state updates en componentes desmontados.
function useFetch(url) {const [data, setData] = useState(null);const [loading, setLoading] = useState(true);const [error, setError] = useState(null);
useEffect(() => { const controller = new AbortController();
async function fetchData() { try { const response = await fetch(url, { signal: controller.signal }); if (!response.ok) throw new Error('HTTP ' + response.status); setData(await response.json()); } catch (err) { if (err.name !== 'AbortError') setError(err); } finally { setLoading(false); } }
fetchData(); return () => controller.abort(); // Cancelar si desmonta}, [url]);
return { data, loading, error };}Cómo responder en entrevista:
“El fetch en
useEffectdebe cancelarse si el componente se desmonta conAbortController. Manejo tres estados: loading, data, error. Para proyectos reales prefiero React Query o SWR: gestionan caché, revalidación, deduplicación de requests y estados de forma declarativa.”
19. ¿Qué es useLayoutEffect?
Section titled “19. ¿Qué es useLayoutEffect?”Definición profesional:
Versión síncrona de useEffect que se ejecuta después de mutaciones del DOM pero antes de que el navegador pinte. Útil para mediciones del DOM y cambios visuales que deben ser inmediatos.
function Tooltip({ targetRef }) {const tooltipRef = useRef(null);
// useEffect: puede causar parpadeo (paint → efecto → repaint)// useLayoutEffect: sin parpadeo (DOM muta → efecto → paint)useLayoutEffect(() => { const target = targetRef.current.getBoundingClientRect(); tooltipRef.current.style.top = target.bottom + 'px';}, []);
return <div ref={tooltipRef} className="tooltip">...</div>;}Cómo responder en entrevista:
“
useLayoutEffectse ejecuta síncronamente antes del paint del navegador. Úsalo solo cuando necesitas leer o mutar el DOM antes de que sea visible (posicionamiento, animaciones, scroll). La regla: si ves parpadeo conuseEffect, cambia auseLayoutEffect. En SSR,useLayoutEffectda warnings; usauseEffectcon un flag de hydration.”
20. ¿Cómo evitar memory leaks en useEffect?
Section titled “20. ¿Cómo evitar memory leaks en useEffect?”Definición profesional: Un memory leak ocurre cuando un efecto actualiza el estado de un componente ya desmontado. La solución es siempre retornar una función de cleanup que cancele operaciones pendientes.
useEffect(() => {// 1. Fetch con AbortControllerconst controller = new AbortController();fetch(url, { signal: controller.signal });
// 2. Timersconst timerId = setInterval(callback, 1000);
// 3. Event listenerswindow.addEventListener('resize', handler);
// 4. Subscripcionesconst sub = store.subscribe(handler);
return () => { controller.abort(); clearInterval(timerId); window.removeEventListener('resize', handler); sub.unsubscribe();};}, []);Cómo responder en entrevista:
“Siempre retorno una función de cleanup en
useEffect. Las operaciones que hay que limpiar: fetch (AbortController), timers (clearTimeout/clearInterval), event listeners (removeEventListener) y subscripciones (unsubscribe). React 18 StrictMode ayuda a detectar efectos sin cleanup al montar/desmontar dos veces.”
21. ¿Qué son los custom hooks?
Section titled “21. ¿Qué son los custom hooks?”Definición profesional:
Funciones que comienzan con use y encapsulan lógica con estado (hooks) para reutilizarla entre componentes. Permiten extraer patrones comunes sin cambiar la estructura de componentes.
function useLocalStorage(key, initialValue) {const [value, setValue] = useState(() => { const stored = localStorage.getItem(key); return stored ? JSON.parse(stored) : initialValue;});
useEffect(() => { localStorage.setItem(key, JSON.stringify(value));}, [key, value]);
return [value, setValue];}
// Uso - igual interfaz que useStateconst [theme, setTheme] = useLocalStorage('theme', 'dark');const [cart, setCart] = useLocalStorage('cart', []);Cómo responder en entrevista:
“Los custom hooks son la forma moderna de compartir lógica con estado. Deben empezar con
usepara que el linter de hooks funcione. Encapsulan el estado y los efectos relacionados, dejando los componentes solo con renderizado. Reemplazan HOCs y Render Props para compartir lógica.”
22. ¿Cuáles son las reglas de los Hooks?
Section titled “22. ¿Cuáles son las reglas de los Hooks?”Definición profesional: Dos reglas fundamentales: llamar hooks solo en el nivel superior (nunca dentro de condiciones o loops) y solo desde componentes de React o custom hooks. React rastrea hooks por orden de llamada.
// ❌ MAL - orden condicional rompe el tracking de Reactif (condition) {useEffect(() => {});}
// ✅ BIEN - condición dentro del hookuseEffect(() => {if (condition) { // lógica}}, [condition]);
// ❌ MAL - en función normalfunction helper() {useState(0); // Error}
// ✅ BIEN - en componente o custom hookfunction useHelper() {useState(0); // OK - es custom hook}Cómo responder en entrevista:
“Las reglas existen porque React identifica cada hook por su posición en la lista de llamadas. Si los llamo condicionalmente, el orden puede cambiar entre renders y React asignará el estado al hook equivocado. El plugin eslint-plugin-react-hooks detecta violaciones automáticamente.”
23. ¿Qué es useId?
Section titled “23. ¿Qué es useId?”Definición profesional: Hook (React 18) que genera IDs únicos y estables que coinciden entre servidor y cliente, resolviendo problemas de hidratación en SSR al asociar labels con inputs.
function FormField({ label, type }) {const id = useId();
return ( <div> <label htmlFor={id}>{label}</label> <input id={id} type={type} /> </div>);}
// Múltiples instancias generan IDs únicos<FormField label="Email" type="email" /> // id=":r0:"<FormField label="Password" type="password" /> // id=":r1:"Cómo responder en entrevista:
“
useIdgenera IDs estables y únicos. El problema que resuelve: en SSR, si generaba IDs conMath.random()o contadores, diferían entre servidor y cliente causando errores de hidratación.useIdgarantiza el mismo ID en ambos lados. Nunca usarlo para keys de listas.”
24. ¿Qué es useTransition?
Section titled “24. ¿Qué es useTransition?”Definición profesional: Hook de React 18 que marca actualizaciones de estado como no urgentes. React puede interrumpir y posponer estos renders para priorizar actualizaciones urgentes (input, clicks).
function BuscadorGrande() {const [query, setQuery] = useState('');const [results, setResults] = useState([]);const [isPending, startTransition] = useTransition();
const handleChange = (e) => { setQuery(e.target.value); // Urgente: actualiza el input inmediatamente
startTransition(() => { setResults(filterLargeList(e.target.value)); // No urgente: puede esperar });};
return ( <> <input value={query} onChange={handleChange} /> {isPending ? <Spinner /> : <ResultList items={results} />} </>);}Cómo responder en entrevista:
“
useTransitiones parte del Concurrent Mode de React 18. Marca actualizaciones como no urgentes para que React pueda interrumpirlas y renderizar primero las urgentes (input del usuario).isPendingindica si hay una transición en progreso para mostrar feedback. Ideal para filtros, búsquedas y navegaciones costosas.”
25. ¿Qué es useDeferredValue?
Section titled “25. ¿Qué es useDeferredValue?”Definición profesional:
Hook de React 18 que retrasa la actualización de un valor hasta que el navegador tenga tiempo libre. Similar a useTransition pero para valores que vienen de props o de fuera del componente.
function SearchResults({ query }) {// deferredQuery se actualiza cuando el navegador tiene tiempoconst deferredQuery = useDeferredValue(query);
const results = useMemo( () => filterItems(deferredQuery), // Solo recalcula con el valor diferido [deferredQuery]);
// Mostrar estado "stale" mientras se actualizaconst isStale = query !== deferredQuery;
return ( <div style={{ opacity: isStale ? 0.7 : 1 }}> <ResultList items={results} /> </div>);}Cómo responder en entrevista:
“
useDeferredValuees el complemento deuseTransition: úsalo cuando no controlas quién actualiza el valor (viene de props).useTransitionenvuelve elsetState;useDeferredValueenvuelve el valor resultante. Ambos permiten que la UI siga respondiendo durante renders costosos.”
26. ¿Qué es el patrón Container/Presentational?
Section titled “26. ¿Qué es el patrón Container/Presentational?”Definición profesional: Separación entre componentes que gestionan lógica y datos (Container) y componentes que solo renderizan UI (Presentational). Mejora la reutilización y el testing.
// Container - lógica, estado, efectosfunction UserListContainer() {const [users, setUsers] = useState([]);useEffect(() => { fetchUsers().then(setUsers); }, []);return <UserList users={users} />;}
// Presentational - solo UI, fácil de testearfunction UserList({ users }) {return ( <ul> {users.map(user => <li key={user.id}>{user.name}</li>)} </ul>);}Cómo responder en entrevista:
“El patrón Container/Presentational separa el ‘qué mostrar’ del ‘cómo obtenerlo’. Los componentes presentacionales son funciones puras: dado unas props, retornan JSX. Fáciles de testear y reutilizar. Hoy los custom hooks a menudo reemplazan los containers, pero el principio de separación sigue siendo válido.”
27. ¿Qué es el patrón Compound Components?
Section titled “27. ¿Qué es el patrón Compound Components?”Definición profesional:
Patrón donde un componente padre comparte estado implícito con sus hijos via Context, permitiendo APIs declarativas y flexibles (similar a select/option del HTML nativo).
const TabsContext = createContext();
function Tabs({ children, defaultTab }) {const [activeTab, setActiveTab] = useState(defaultTab);return ( <TabsContext.Provider value={{ activeTab, setActiveTab }}> {children} </TabsContext.Provider>);}
Tabs.Tab = function Tab({ id, children }) {const { activeTab, setActiveTab } = useContext(TabsContext);return ( <button className={activeTab === id ? 'active' : ''} onClick={() => setActiveTab(id)}>{children}</button>);};
// Uso declarativo y flexible<Tabs defaultTab="home"><Tabs.Tab id="home">Inicio</Tabs.Tab><Tabs.Tab id="about">Acerca</Tabs.Tab></Tabs>Cómo responder en entrevista:
“Compound Components usan Context para compartir estado entre componentes relacionados sin props explícitos. Crean APIs declarativas donde el consumidor tiene control total sobre la estructura y orden. Headless UI y Radix UI usan este patrón extensamente.”
28. ¿Qué es el patrón Render Props?
Section titled “28. ¿Qué es el patrón Render Props?”Definición profesional:
Patrón donde un componente recibe una función como prop (render o children) y la llama con su estado interno, delegando el renderizado al consumidor.
function MouseTracker({ render }) {const [position, setPosition] = useState({ x: 0, y: 0 });
useEffect(() => { const handleMove = (e) => setPosition({ x: e.clientX, y: e.clientY }); window.addEventListener('mousemove', handleMove); return () => window.removeEventListener('mousemove', handleMove);}, []);
return render(position); // Delega el renderizado}
// Uso<MouseTracker render={({ x, y }) => <p>Posición: {x}, {y}</p>} />Cómo responder en entrevista:
“Render Props compartían lógica con estado antes de los hooks. Hoy los custom hooks son más simples para el mismo propósito. Sin embargo, el patrón sigue siendo válido para casos donde el consumidor necesita control total sobre el renderizado, como en bibliotecas de componentes.”
29. ¿Qué son los Higher-Order Components (HOC)?
Section titled “29. ¿Qué son los Higher-Order Components (HOC)?”Definición profesional: Función que recibe un componente y retorna un nuevo componente con funcionalidad adicional. Patrón de composición para reutilizar lógica transversal.
function withAuth(WrappedComponent) {return function AuthenticatedComponent(props) { const { user } = useAuth();
if (!user) return <Navigate to="/login" />;
return <WrappedComponent {...props} user={user} />;};}
const ProtectedDashboard = withAuth(Dashboard);Cómo responder en entrevista:
“Los HOCs son funciones de orden superior aplicadas a componentes. Útiles para lógica transversal: autenticación, logging, theming. La desventaja es el wrapper hell y dificultad para debuggear. Hoy los custom hooks son preferibles para compartir lógica. Los HOCs siguen siendo útiles en patrones de librería (connect de Redux, withRouter).“
30. ¿Cuándo usar HOC vs Custom Hooks?
Section titled “30. ¿Cuándo usar HOC vs Custom Hooks?”Definición profesional: Los Custom Hooks comparten lógica sin modificar el árbol de componentes. Los HOCs modifican la estructura del árbol y pueden interceptar props. Hoy los Custom Hooks son la opción preferida.
| HOC | Custom Hooks |
|---|---|
| Modifica árbol de componentes | Solo comparte lógica |
| Wrapper hell potencial | Sin overhead de árbol |
| Legacy pattern | Moderno (preferido) |
Cómo responder en entrevista:
“Custom Hooks son preferibles en la mayoría de casos: sin wrapper hell, fáciles de componer, mejor TypeScript. Los HOCs siguen siendo válidos cuando necesitas interceptar o modificar el ciclo de vida del componente (ej:
React.memo,ErrorBoundary). Muchas librerías ofrecen ambas opciones.”
31. ¿Qué es forwardRef?
Section titled “31. ¿Qué es forwardRef?”Definición profesional:
API que permite a un componente recibir una ref como segundo argumento y pasarla a un elemento DOM interno. Necesario porque React no pasa ref como prop normal.
const Input = forwardRef(function Input({ label, ...props }, ref) {return ( <div> <label>{label}</label> <input ref={ref} {...props} /> </div>);});
function Form() {const inputRef = useRef(null);return ( <> <Input ref={inputRef} label="Email" type="email" /> <button onClick={() => inputRef.current.focus()}>Focus</button> </>);}Cómo responder en entrevista:
“
forwardRefes necesario cuando un componente wrapper necesita exponer el DOM interno al padre. Sin él,refno llega alinputreal. Se usa junto conuseImperativeHandlepara exponer solo una API controlada en lugar del nodo DOM completo.”
32. ¿Qué es useImperativeHandle?
Section titled “32. ¿Qué es useImperativeHandle?”Definición profesional:
Hook que personaliza el valor expuesto al padre cuando usa forwardRef. Permite exponer solo métodos específicos en lugar de todo el nodo DOM.
const FancyInput = forwardRef(function FancyInput(props, ref) {const inputRef = useRef(null);
useImperativeHandle(ref, () => ({ focus: () => inputRef.current.focus(), clear: () => { inputRef.current.value = ''; }, getValue: () => inputRef.current.value // No expone el nodo DOM completo}));
return <input ref={inputRef} {...props} />;});
// Padre solo puede llamar focus, clear, getValueconst ref = useRef(null);ref.current.focus();ref.current.clear();Cómo responder en entrevista:
“
useImperativeHandlelimita la API imperativamente expuesta al padre. En lugar de dar acceso completo al nodo DOM (anti-patrón), expone solo métodos específicos. Es parte del principio de encapsulación. Útil en librerías de componentes donde quieres una API pública estable.”
33. ¿Qué es Suspense?
Section titled “33. ¿Qué es Suspense?”Definición profesional:
Componente que muestra un fallback mientras algún hijo está en estado “suspendido” (cargando código con lazy() o datos con librerías compatibles como React Query o Relay).
const Dashboard = lazy(() => import('./Dashboard'));
function App() {return ( <Suspense fallback={<Skeleton />}> <Dashboard /> </Suspense>);}
// Anidado - fallback más granular<Suspense fallback={<PageSkeleton />}><Header /><Suspense fallback={<ContentSkeleton />}> <MainContent /></Suspense></Suspense>Cómo responder en entrevista:
“
Suspensepermite loading states declarativos. Conlazy()maneja el code splitting. En React 18 también soporta data fetching con librerías como React Query (usandosuspense: true). LosSuspenseanidados permiten granularidad en los estados de carga.”
34. ¿Qué son los Error Boundaries?
Section titled “34. ¿Qué son los Error Boundaries?”Definición profesional:
Componentes de clase que implementan getDerivedStateFromError/componentDidCatch para capturar errores en su árbol de hijos y mostrar UI de fallback en lugar de romper toda la aplicación.
class ErrorBoundary extends React.Component {state = { hasError: false, error: null };
static getDerivedStateFromError(error) { return { hasError: true, error };}
componentDidCatch(error, errorInfo) { logError(error, errorInfo); // Enviar a Sentry, etc.}
render() { if (this.state.hasError) { return <ErrorFallback error={this.state.error} />; } return this.props.children;}}
// Uso - aislar secciones de la UI<ErrorBoundary><RiskyComponent /></ErrorBoundary>Cómo responder en entrevista:
“Los Error Boundaries son la única forma de capturar errores de render en React. Solo pueden ser componentes de clase (hooks no pueden implementarlo aún). Úsalos en producción para aislar secciones: un error en el sidebar no debe romper el contenido principal. La librería
react-error-boundaryofrece una versión lista para usar.”
35. ¿Qué es React.lazy?
Section titled “35. ¿Qué es React.lazy?”Definición profesional:
Función que permite importar componentes dinámicamente (code splitting), cargándolos solo cuando se necesitan. Debe usarse con Suspense.
// Code splitting por rutaconst Home = lazy(() => import('./pages/Home'));const Dashboard = lazy(() => import('./pages/Dashboard'));
function App() {return ( <Suspense fallback={<Loading />}> <Routes> <Route path="/" element={<Home />} /> <Route path="/dashboard" element={<Dashboard />} /> </Routes> </Suspense>);}
// Precargar en hover para mejor UXconst preload = () => import('./pages/Dashboard');<Link to="/dashboard" onMouseEnter={preload}>Dashboard</Link>Cómo responder en entrevista:
“
React.lazyhabilita code splitting dinámico. El bundler (Webpack/Vite) separa el componente en un chunk separado que se descarga solo cuando se necesita. Combinado conSuspensey rutas, reduce el bundle inicial significativamente. Precargar enonMouseEntermejora la UX percibida.”
36. ¿Cómo configurar React Router?
Section titled “36. ¿Cómo configurar React Router?”Definición profesional:
Librería estándar para routing declarativo en SPAs React. Versión 6 introdujo Routes/Route con elementos JSX y anidamiento más intuitivo.
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
function App() {return ( <BrowserRouter> <nav> <Link to="/">Inicio</Link> <Link to="/about">Acerca</Link> </nav> <Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />} /> <Route path="/users/:id" element={<User />} /> <Route path="*" element={<NotFound />} /> </Routes> </BrowserRouter>);}Cómo responder en entrevista:
“React Router v6 usa
Routes(en lugar deSwitch) yRouteconelement(JSX en lugar decomponent).BrowserRouterusa la History API para URLs limpias sin hash.Linkpreviene recargas de página. Las rutas se emparejan por especificidad, no por orden.”
37. ¿Cómo obtener parámetros de la URL?
Section titled “37. ¿Cómo obtener parámetros de la URL?”Definición profesional:
React Router v6 provee hooks para acceder a diferentes partes de la URL: useParams para segmentos dinámicos y useSearchParams para query strings.
import { useParams, useSearchParams } from 'react-router-dom';
function User() {const { id } = useParams(); // /users/:id → { id: "123" }const [params, setParams] = useSearchParams(); // /users?page=2&sort=nameconst page = params.get('page'); // "2"const sort = params.get('sort'); // "name"
// Actualizar query params sin navegarconst changePage = (p) => setParams({ page: p, sort });
return <div>Usuario {id}, página {page}</div>;}Cómo responder en entrevista:
“
useParamsda acceso a los segmentos dinámicos de la ruta (:id,:slug).useSearchParamsgestiona los query params como un objetoURLSearchParams.setSearchParamsactualiza los parámetros sin recargar la página. Para obtener el pathname completo, usouseLocation.”
38. ¿Cómo hacer navegación programática?
Section titled “38. ¿Cómo hacer navegación programática?”Definición profesional:
Navegar mediante código (no con clicks en Link) usando el hook useNavigate que retorna una función de navegación imperativa.
import { useNavigate } from 'react-router-dom';
function Login() {const navigate = useNavigate();
const handleLogin = async (credentials) => { await loginAPI(credentials); navigate('/dashboard'); // Navegar adelante navigate('/login', { replace: true }); // Sin entrada en historial navigate(-1); // Atrás navigate('/profile', { state: { from: '/login' } }); // Con estado};}Cómo responder en entrevista:
“
useNavigateretorna una función para navegar imperativamente, útil después de acciones asíncronas (login, submit).replace: trueevita que el usuario vuelva atrás con el botón del navegador.navigate(-1)equivale ahistory.back(). El estado se puede pasar y leer conuseLocation().state.”
39. ¿Cómo proteger rutas?
Section titled “39. ¿Cómo proteger rutas?”Definición profesional: Componente wrapper que verifica autenticación antes de renderizar la ruta protegida, redirigiendo a login si no hay sesión activa.
function ProtectedRoute({ children }) {const { user, isLoading } = useAuth();const location = useLocation();
if (isLoading) return <Spinner />; // Evitar parpadeo
if (!user) { // Guardar la ruta original para redirigir después del login return <Navigate to="/login" state={{ from: location }} replace />;}
return children;}
// Uso<Route path="/dashboard" element={<ProtectedRoute><Dashboard /></ProtectedRoute>} />Cómo responder en entrevista:
“El
ProtectedRouteverifica autenticación y redirige si no hay usuario. Guardo la ruta original enstatepara redirigir al destino original después del login. ElisLoadingevita el parpadeo mientras se verifica la sesión. Para roles, extiendo el patrón conallowedRolescomo prop.”
40. ¿Qué son las rutas anidadas?
Section titled “40. ¿Qué son las rutas anidadas?”Definición profesional:
Rutas definidas dentro de otras rutas que comparten layout. El componente Outlet en el layout renderiza la ruta hija activa.
<Routes><Route path="/dashboard" element={<DashboardLayout />}> <Route index element={<DashboardHome />} /> {/* /dashboard */} <Route path="settings" element={<Settings />} /> {/* /dashboard/settings */} <Route path="profile" element={<Profile />} /> {/* /dashboard/profile */}</Route></Routes>
function DashboardLayout() {return ( <div className="layout"> <Sidebar /> <main> <Outlet /> {/* Aquí se renderiza la ruta hija */} </main> </div>);}Cómo responder en entrevista:
“Las rutas anidadas permiten layouts compartidos.
Outletes el slot donde se renderiza la ruta hija activa.indexdefine la ruta por defecto del layout. Es el patrón base para dashboards y apps con navegación lateral. En React Router v6 mejoró mucho respecto a v5.”
41. ¿Cómo testear componentes con React Testing Library?
Section titled “41. ¿Cómo testear componentes con React Testing Library?”Definición profesional: Librería que testea componentes desde la perspectiva del usuario: busca elementos por roles accesibles, texto visible y labels, no por detalles de implementación.
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
test('muestra mensaje de error si el form está vacío', async () => {render(<LoginForm />);
fireEvent.click(screen.getByRole('button', { name: /iniciar sesión/i }));
await waitFor(() => { expect(screen.getByText(/email es requerido/i)).toBeInTheDocument();});});Cómo responder en entrevista:
“RTL sigue la filosofía: testear el comportamiento, no la implementación.
getByRolees el selector preferido (accesibilidad).findBy*para elementos asíncronos.userEventsobrefireEventpara interacciones más realistas. No consulto por className o testId salvo como último recurso.”
42. ¿Cómo testear hooks personalizados?
Section titled “42. ¿Cómo testear hooks personalizados?”Definición profesional:
Con renderHook de Testing Library, que renderiza el hook en un componente mínimo. Los cambios de estado deben envolverse en act().
import { renderHook, act } from '@testing-library/react';
test('useCounter incrementa y decrementa', () => {const { result } = renderHook(() => useCounter(0));
expect(result.current.count).toBe(0);
act(() => { result.current.increment(); });expect(result.current.count).toBe(1);
act(() => { result.current.decrement(); });expect(result.current.count).toBe(0);});Cómo responder en entrevista:
“
renderHookrenderiza el hook en un componente anónimo.act()es necesario para actualizaciones de estado síncronas. Para hooks con Context, paso unwrappercon el Provider.rerenderpermite cambiar las props del hook entre tests.”
43. ¿Cómo mockear fetch en tests?
Section titled “43. ¿Cómo mockear fetch en tests?”Definición profesional:
Reemplazar fetch global con un mock de Jest para controlar las respuestas sin hacer llamadas HTTP reales.
beforeEach(() => {global.fetch = jest.fn().mockResolvedValue({ ok: true, json: () => Promise.resolve({ users: [{ id: 1, name: 'Juan' }] })});});
afterEach(() => jest.restoreAllMocks());
test('carga y muestra usuarios', async () => {render(<UserList />);
await waitFor(() => { expect(screen.getByText('Juan')).toBeInTheDocument();});
expect(global.fetch).toHaveBeenCalledWith('/api/users');});Cómo responder en entrevista:
“Mockeo
fetchconjest.fn()para tests unitarios. Para tests más completos usomsw(Mock Service Worker): intercepta requests a nivel de red, funciona en tests y en desarrollo.mswes la solución recomendada porque el código no necesita saber que está siendo mockeado.”
44. ¿Qué es snapshot testing?
Section titled “44. ¿Qué es snapshot testing?”Definición profesional: Test que serializa el output del componente y lo guarda en un archivo. Futuros tests comparan contra ese snapshot para detectar cambios inesperados.
test('Button renderiza correctamente', () => {const { asFragment } = render( <Button variant="primary" onClick={jest.fn()}> Guardar </Button>);expect(asFragment()).toMatchSnapshot();// Guarda el HTML serializado en __snapshots__/});
// Actualizar snapshot si el cambio es intencional:// jest --updateSnapshotCómo responder en entrevista:
“Los snapshots detectan cambios involuntarios en la UI. La desventaja: se actualizan fácilmente con
--updateSnapshotsin revisar si el cambio es correcto, dando falsa seguridad. Prefiero tests de comportamiento específicos. Los snapshots son útiles para componentes estables y puramente visuales.”
45. ¿Cómo testear Context?
Section titled “45. ¿Cómo testear Context?”Definición profesional:
Pasar un wrapper a render que envuelve el componente con el Provider del Context con valores de prueba.
const mockUser = { name: 'Test', role: 'admin' };
const Wrapper = ({ children }) => (<UserContext.Provider value={{ user: mockUser, logout: jest.fn() }}> {children}</UserContext.Provider>);
test('muestra nombre del usuario', () => {render(<UserMenu />, { wrapper: Wrapper });expect(screen.getByText('Test')).toBeInTheDocument();});
test('llama logout al hacer clic', () => {const logout = jest.fn();render(<UserMenu />, { wrapper: ({ children }) => ( <UserContext.Provider value={{ user: mockUser, logout }}> {children} </UserContext.Provider> )});fireEvent.click(screen.getByRole('button', { name: /cerrar sesión/i }));expect(logout).toHaveBeenCalled();});Cómo responder en entrevista:
“Paso el Provider como
wrapperenrender. Para tests con múltiples providers (Router, Context, Theme), creo unAllProviderswrapper reutilizable. La clave es aislar el componente con los valores mínimos necesarios, no toda la configuración de producción.”
46. ¿Cómo identificar problemas de performance?
Section titled “46. ¿Cómo identificar problemas de performance?”Definición profesional: React DevTools Profiler muestra qué componentes se re-renderizan, con qué frecuencia y cuánto tardan. Es la herramienta principal para diagnóstico de rendimiento.
// 1. React DevTools Profiler - grabar y analizar renders// 2. Por qué se re-renderizó (DevTools muestra la causa)
// 3. Detectar renders innecesarios con consoleconst Componente = React.memo(function Componente(props) {console.count('Componente renderizado'); // Cuántas vecesreturn <div>{props.value}</div>;});
// 4. React.StrictMode - detecta side effects problemáticosCómo responder en entrevista:
“Primero mido con React DevTools Profiler para identificar qué componentes se re-renderizan innecesariamente. No optimizo prematuramente. El Profiler muestra la causa del render (prop cambió, state cambió, parent renderizó). Con esa información decido si aplicar
React.memo,useMemoouseCallback.”
47. ¿Qué causa re-renders innecesarios?
Section titled “47. ¿Qué causa re-renders innecesarios?”Definición profesional: Las causas más comunes son: funciones u objetos creados inline (nueva referencia cada render), keys incorrectas en listas, y estado colocado demasiado arriba en el árbol.
// ❌ Nueva función cada render - rompe React.memo<Child onClick={() => handleClick(id)} />
// ✅ Referencia estable con useCallbackconst handleClick = useCallback(() => {}, []);<Child onClick={handleClick} />
// ❌ Nuevo objeto cada render<Child style={{ color: 'red' }} />
// ✅ Objeto estable con useMemo o constante fuera del componenteconst style = useMemo(() => ({ color: 'red' }), []);
// ❌ Índice como key{items.map((item, i) => <Item key={i} />)}
// ✅ ID estable{items.map(item => <Item key={item.id} />)}Cómo responder en entrevista:
“Las principales causas: funciones/objetos inline crean nuevas referencias cada render (rompen
React.memo), índice como key causa remontajes en reordenamiento, y estado demasiado arriba provoca renders en cascada. La solución esuseCallback/useMemopara referencias estables y colocar el estado cerca de donde se usa.”
48. ¿Cómo optimizar listas grandes?
Section titled “48. ¿Cómo optimizar listas grandes?”Definición profesional:
Virtualización: solo renderizar los elementos visibles en la ventana. Las librerías react-window y @tanstack/virtual implementan este patrón.
import { FixedSizeList } from 'react-window';
function VirtualList({ items }) {const Row = ({ index, style }) => ( <div style={style}>{items[index].nombre}</div>);
return ( <FixedSizeList height={400} // Altura del contenedor visible itemCount={items.length} itemSize={35} // Altura de cada fila width="100%" > {Row} </FixedSizeList>);// Solo renderiza ~12 filas en lugar de 10,000}Cómo responder en entrevista:
“Para listas de miles de elementos, la virtualización renderiza solo los elementos visibles (~20-30).
react-windowes ligero;@tanstack/virtuales más flexible (soporta tamaños dinámicos). También aplicoReact.memoen el componente de cada fila para evitar re-renders al hacer scroll.”
49. ¿Qué es React Concurrent Mode?
Section titled “49. ¿Qué es React Concurrent Mode?”Definición profesional:
Conjunto de características de React 18 que permiten interrumpir renders largos para priorizar actualizaciones urgentes. Habilita useTransition, useDeferredValue, Suspense para datos y renderizado concurrente.
// Activar Concurrent Mode (React 18)import { createRoot } from 'react-dom/client';const root = createRoot(document.getElementById('root'));root.render(<App />);
// Features de Concurrent Mode:// - useTransition: marcar renders como no urgentes// - useDeferredValue: diferir actualizaciones de valor// - Suspense para data fetching// - Batching automático de actualizacionesCómo responder en entrevista:
“Concurrent Mode (React 18) permite que React interrumpa un render en progreso para procesar una actualización más urgente.
createRootlo activa (vsReactDOM.renderlegacy). Incluye batching automático desetState,useTransition,useDeferredValuey mejor integración de Suspense.”
50. ¿Cómo hacer code splitting efectivo?
Section titled “50. ¿Cómo hacer code splitting efectivo?”Definición profesional: Dividir el bundle JavaScript en chunks que se cargan bajo demanda. La estrategia más impactante es el splitting por ruta: cada página es un chunk separado.
// Por ruta - el más impactanteconst Home = lazy(() => import('./pages/Home'));const Dashboard = lazy(() => import('./pages/Dashboard'));
// Por feature - componentes pesados opcionalesconst RichEditor = lazy(() => import('./components/RichEditor'));
// Precargar en hover para UX sin latencia percibidaconst preload = () => import('./pages/Dashboard');<Link to="/dashboard" onMouseEnter={preload}>Dashboard</Link>
// Vite/Webpack agrupan en chunks automáticamente// Verificar con: npx vite-bundle-visualizerCómo responder en entrevista:
“El code splitting por ruta es lo más impactante: reduce el bundle inicial que el usuario descarga.
React.lazy+Suspenselo hacen declarativo. El precarg en hover elimina la latencia percibida. Para analizar el bundle usovite-bundle-visualizerowebpack-bundle-analyzere identifico chunks grandes candidatos a splitting.”