Cuando desarrollamos Client Extensions de React en Liferay, nos enfrentamos a desafíos únicos: múltiples microfrontends desacoplados, estado compartido complejo y, sobre todo, problemas de rendimiento derivados de re-renderizados innecesarios. Las soluciones tradicionales como Context API o Redux suelen introducir complejidad excesiva o overhead que afecta la experiencia de usuario.
En JULDITEC hemos adoptado Zustand como nuestra solución de referencia para la gestión de estado en arquitecturas modernas con Liferay. En este artículo te explicamos por qué esta librería minimalista mejora drásticamente el rendimiento de tus aplicaciones y cómo integrarla correctamente en tus Client Extensions.
¿Qué es Zustand y por qué destaca?
Zustand es una librería de gestión de estado para React que se basa en tres principios fundamentales: simplicidad, rendimiento y ausencia de boilerplate. A diferencia de Redux, no requiere providers, reducers ni acciones complejas. A diferencia de Context API, no provoca re-renderizados en cascada de toda la jerarquía de componentes.
La filosofía de Zustand es directa: creas un store con una función simple, consumes el estado mediante hooks y el componente solo se re-renderiza cuando cambian las porciones específicas del estado que está utilizando.
Comparativa rápida: Zustand vs Redux vs Context API
- •Redux: Potente pero verboso. Requiere actions, reducers, middleware. Ideal para aplicaciones grandes con lógica de estado compleja, pero excesivo para muchos casos.
- •Context API: Nativo de React pero con problemas de rendimiento. Cada cambio en el contexto re-renderiza todos los consumidores, aunque no usen la porción modificada.
- •Zustand: Ligero (menos de 1KB), API minimalista, suscripción selectiva por defecto. El equilibrio perfecto para Client Extensions.
El problema específico en Client Extensions de Liferay
Las Client Extensions en Liferay son aplicaciones frontend desacopladas que se integran en el portal mediante iframes o como Custom Elements. Esta arquitectura presenta desafíos únicos:
- •Naturaleza desacoplada: Cada Client Extension es una aplicación independiente con su propio ciclo de vida.
- •Múltiples microfrontends: En una misma página pueden coexistir varias Client Extensions que necesitan compartir estado.
- •Estado compartido complejo: Información del usuario, configuración, datos de negocio que deben sincronizarse.
- •Problemas de rendimiento: Re-renderizados innecesarios cuando usas Context API o props drilling extenso.
Imagina un dashboard con 5 widgets independientes (cada uno una Client Extension). Si usas Context API y actualizas el estado de usuario, todos los widgets se re-renderizan aunque solo uno necesite esa información. Con Zustand, solo el widget suscrito a ese dato específico se actualiza.
Cómo integrar Zustand en una Client Extension
La integración de Zustand en un proyecto de Client Extension es sorprendentemente sencilla. Primero, instalamos la dependencia:
npm install zustand
Estructura básica del store
Creamos un store específico para nuestro dominio de negocio. Por ejemplo, un store de usuario:
// stores/userStore.js
import { create } from 'zustand';
const useUserStore = create((set) => ({
user: null,
preferences: {},
setUser: (user) => set({ user }),
updatePreferences: (prefs) => set((state) => ({
preferences: { ...state.preferences, ...prefs }
})),
clearUser: () => set({ user: null, preferences: {} })
}));
export default useUserStore;
Uso en componentes
El consumo del estado es extremadamente simple y performante:
No autenticado Puedes inicializar tu store con datos globales de Liferay disponibles en La clave del rendimiento de Zustand está en su suscripción selectiva basada en selectores. Cuando haces: El componente solo se re-renderiza cuando cambia Además, Zustand tiene un menor overhead que Redux: no necesitas middleware complejo, no hay serialización de acciones, y el bundle final es significativamente más pequeño (menos de 1KB vs ~10KB de Redux). En formularios multi-paso con validaciones, Zustand permite gestionar el estado del formulario sin prop drilling y con re-renders quirúrgicos solo en los campos modificados. Cada widget (Client Extension independiente) puede suscribirse solo a la porción de estado que necesita: métricas, filtros, configuración de usuario, etc. Centraliza el estado de llamadas asíncronas, loading states y errores en un store dedicado: Puedes exponer stores como módulos compartidos entre diferentes Client Extensions, permitiendo sincronización de estado sin comunicación compleja entre iframes. No crees un store monolítico. Divide por responsabilidades: Si un store crece demasiado, considera dividirlo o usar slices (patrón de composición de stores). Zustand soporta middlewares para funcionalidades avanzadas: Zustand facilita el testing al ser funciones puras. Puedes testear las acciones del store sin necesidad de montar componentes: En JULDITEC hemos comprobado que Zustand es la solución óptima para gestión de estado en Client Extensions de Liferay. Su combinación de simplicidad, rendimiento y ausencia de boilerplate lo convierte en la herramienta perfecta para arquitecturas desacopladas y microfrontends. Las mejoras de rendimiento son medibles y significativas: reducción de re-renders innecesarios, bundles más ligeros y código más mantenible. Si estás desarrollando aplicaciones modernas sobre Liferay DXP, Zustand debería estar en tu stack tecnológico. Nuestro enfoque en JULDITEC siempre prioriza el rendimiento y la simplicidad. Zustand encaja perfectamente en esta filosofía, permitiéndonos entregar soluciones empresariales robustas sin sacrificar la experiencia de desarrollo ni la performance del usuario final. ¿Quieres implementar Client Extensions de alto rendimiento en tu proyecto Liferay? Contacta con nosotros y descubre cómo podemos ayudarte a construir arquitecturas modernas y escalables.// components/UserProfile.jsx
import useUserStore from '../stores/userStore';
function UserProfile() {
// Suscripción selectiva: solo se re-renderiza si cambia 'user'
const user = useUserStore((state) => state.user);
if (!user) return {user.name}
Integración con Liferay globals
window.Liferay:// stores/liferayStore.js
import { create } from 'zustand';
const useLiferayStore = create((set) => ({
themeDisplay: window.Liferay?.ThemeDisplay || {},
currentUser: window.Liferay?.ThemeDisplay?.getUserName() || 'Guest',
languageId: window.Liferay?.ThemeDisplay?.getLanguageId() || 'es_ES'
}));
export default useLiferayStore;
Por qué mejora drásticamente el rendimiento
const user = useUserStore((state) => state.user);
state.user, no cuando cambia cualquier otra propiedad del store. Esto contrasta radicalmente con Context API, donde cualquier cambio en el contexto provoca re-renders de todos los consumidores.Comparación práctica: antes vs después
Con Context API: 15 re-renders en un dashboard de 5 widgets al actualizar un solo dato.
Con Zustand: 1 re-render, solo del widget que consume ese dato específico.
Casos de uso reales en Client Extensions
1. Formularios complejos
2. Dashboards con múltiples widgets
3. Integración con APIs externas
const useApiStore = create((set) => ({
data: null,
loading: false,
error: null,
fetchData: async (endpoint) => {
set({ loading: true, error: null });
try {
const response = await fetch(endpoint);
const data = await response.json();
set({ data, loading: false });
} catch (error) {
set({ error: error.message, loading: false });
}
}
}));
4. Client Extensions independientes que comparten estado
Buenas prácticas al usar Zustand en Liferay
1. Separar stores por dominio
userStore, cartStore, notificationsStore, etc. Esto mejora la mantenibilidad y el rendimiento.2. Evitar stores gigantes
3. Uso de middlewares
import { create } from 'zustand';
import { persist, devtools } from 'zustand/middleware';
const useStore = create(
devtools(
persist(
(set) => ({
user: null,
setUser: (user) => set({ user })
}),
{ name: 'user-storage' }
)
)
);
4. Testing del estado
import useUserStore from './userStore';
test('setUser actualiza el usuario', () => {
const { setUser, user } = useUserStore.getState();
setUser({ name: 'Juan' });
expect(useUserStore.getState().user.name).toBe('Juan');
});
Conclusión: Zustand como solución óptima en arquitecturas modernas
