Estrategias Avanzadas de Rendimiento y Arquitectura en React: Preguntas Técnicas para Entrevistas

React ofrece múltiples mecanismos para optimizar el rendimiento, estructurar lógica reutilizable y escalar aplicaciones complejas. A continuación se explican patrones clave desde una perspectiva práctica y actualizada, con ejemplos reescritos para claridad técnica y menor similitud léxica.

Caché selectiva de componentes con memoización inteligente

La memoización no es solo sobre evitar renders innecesarios: se trata de aplicar comparaciones semánticas cuando los props son objetos o funciones. En lugar de depender únicamente de React.memo, se recomienda combinarlo con funciones de comparación personalizadas:

const UserProfileCard = React.memo(({ user, onAction }) => (
  <article className="profile-card">
    <h2>{user.name}</h2>
    <button onClick={() => onAction(user.id)}>Editar</button>
  </article>
), (prevProps, nextProps) => {
  // Evita re-render si solo cambia la referencia de la función
  return prevProps.user.id === nextProps.user.id &&
         prevProps.user.updatedAt === nextProps.user.updatedAt;
});

Hooks personalizados con comportamiento contextual

Un Hook efectivo encapsula lógica con estado, efectos secundarios y condiciones de uso claras. Por ejemplo, un Hook para manejar formularios con validación integrada:

function useForm(initialState, validator) {
  const [data, setData] = useState(initialState);
  const [errors, setErrors] = useState({});

  const handleChange = useCallback((field, value) => {
    setData(prev => ({ ...prev, [field]: value }));
    if (errors[field]) {
      setErrors(prev => ({ ...prev, [field]: null }));
    }
  }, [errors]);

  const validate = useCallback(() => {
    const validation = validator(data);
    setErrors(validation);
    return Object.keys(validation).length === 0;
  }, [data, validator]);

  return { data, errors, handleChange, validate };
}

// Uso en componente
function RegistrationForm() {
  const { data, errors, handleChange, validate } = useForm(
    { email: '', password: '' },
    (values) => ({
      email: !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(values.email) ? 'Email inválido' : null,
      password: values.password.length < 8 ? 'Mínimo 8 caracteres' : null
    })
  );

  return (
    <form onSubmit={(e) => { e.preventDefault(); validate() ? submit() : null; }}>
      <input 
        value={data.email} 
        onChange={(e) => handleChange('email', e.target.value)} 
      />
      {errors.email && <span className="error">{errors.email}</span>}
      <button type="submit">Enviar</button>
    </form>
  );
}

Renderizado del lado del servidor con hidratación robusta

Para SSR realista, no basta con renderToString: se requiere sincronización de estado entre servidor y cliente. Un enfoque moderno usa renderToPipeableStream (en React 18+) junto con createRoot y manejo de carga diferida:

// Servidor (Node.js + Express)
app.get('*', async (req, res) => {
  const stream = renderToPipeableStream(
    <ServerApp url={req.url} />,
    {
      bootstrapScripts: ['/static/client.js'],
      onShellReady() {
        res.statusCode = 200;
        res.setHeader('Content-type', 'text/html');
        stream.pipe(res);
      }
    }
  );
});

Composición mediante render props y contextos especializados

Más allá de HOCs obsoletos, la composición moderna prioriza flexibilidad y tipado explícito. Un patrón efectivo combina Context API con render props para inyectar comportamientos sin acoplamiento fuerte:

const ThemeContext = createContext();

function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

function ThemedButton({ children, asChild = false }) {
  const { theme } = useContext(ThemeContext);
  const baseClass = `btn btn--${theme}`;
  
  return asChild ? (
    <>{children({ className: baseClass })}</>
  ) : (
    <button className={baseClass}>{children}</button>
  );
}

Gestión de estado distribuido sin librerías externas

Con useReducer y useContext, es posible construir un sistema de estado global ligero y escalable. Se evita el overhead de Redux mientras se mantiene previsibilidad:

const AppContext = createContext();

function AppProvider({ children }) {
  const [state, dispatch] = useReducer(appReducer, initialState);

  return (
    <AppContext.Provider value={{ state, dispatch }}>
      {children}
    </AppContext.Provider>
  );
}

// Reducer tipo "ducks"
function appReducer(state, action) {
  switch (action.type) {
    case 'SET_USER':
      return { ...state, user: action.payload };
    case 'TOGGLE_DARK_MODE':
      return { ...state, darkMode: !state.darkMode };
    default:
      return state;
  }
}

Carga diferida con soporte para código dividido y fallbacks dinámicos

El patrón React.lazy debe combinarse con estrategias de precarga y manejo de errores para experiencia robusta:

const LazyDashboard = lazy(() => import('./dashboard/Dashboard').catch(() => ({
  default: () => <div className="fallback-error">Error al cargar panel</div>
}));

function DashboardRoute() {
  return (
    <Suspense fallback={<Spinner size="large" />} >
      <LazyDashboard />
    </Suspense>
  );
}

Integración con elementos personalizados (Web Components)

React puede interoperar con Web Components estándar mediante envolturas que gestionen propiedades y eventos correctamente:

function CustomAlert({ message, visible, onClose }) {
  useEffect(() => {
    const element = document.querySelector('custom-alert');
    if (element) {
      element.message = message;
      element.visible = visible;
      element.addEventListener('close', onClose);
      return () => element.removeEventListener('close', onClose);
    }
  }, [message, visible, onClose]);

  return <custom-alert />;
}

Etiquetas: React ssr Memoization custom-hooks web-components

Publicado el 9-2 20:56