Solucionando problemas de actualización de componentes en Sitecore XM Cloud usando Higher-Order Components
Introducción
Al trabajar con Sitecore XM Cloud y herramientas como Chakra UI, los desarrolladores pueden encontrar problemas relacionados con la actualización de componentes dentro de Sitecore Pages. Una de las principales molestias es que algunos componentes no se actualizan automáticamente cuando se realizan cambios, lo que puede ser frustrante, especialmente cuando se busca un flujo de trabajo fluido que permita la edición en tiempo real.
Para abordar este problema, nos pusimos en contacto con el soporte de Sitecore y participamos en la comunidad de Slack de Sitecore, donde Jeff L'Heureux y David Ly amablemente ofrecieron una solución. En este artículo, exploraremos el problema, describiremos la solución propuesta y proporcionaremos una guía paso a paso para mejorar la experiencia de edición en Sitecore Pages.
Mejores Prácticas: Requisitos de Nombres de Clase de Componentes
Según las mejores prácticas de Sitecore, cada componente debe incluir un nombre de clase para soportar las funciones avanzadas de edición de XM Cloud. Los nombres de clase se pasan a través de los parámetros de renderizado y deben aplicarse a los componentes.
La siguiente estructura demuestra cómo incorporar los nombres de clase de manera compatible:
<div className={`component promo ${props.params.styles}`} id={id ? id : undefined}>
<p>Component Markup Here</p>
</div>
Sin embargo, en nuestro proyecto, estamos utilizando Chakra UI para el estilo de los componentes. Chakra UI ofrece un enfoque altamente modular y basado en CSS-in-JS para el estilo, que proporciona un mayor control sobre nuestros estilos y elimina la necesidad de gestionar manualmente los nombres de clase de CSS. Esto reduce el riesgo de conflictos y hace que nuestro código sea más limpio y fácil de mantener.
En nuestro caso, no queremos sobrescribir el atributo className porque rompería la estructura e integración proporcionada por Chakra UI, que se basa en props para aplicar estilos dinámicamente, como Box, Flex y otros componentes de Chakra. En lugar de sobrescribir o gestionar manualmente los nombres de clase, utilizamos el enfoque de estilo basado en props de Chakra para el diseño y la apariencia, lo que ayuda a mantener la consistencia y flexibilidad en todo el proyecto.
Por lo tanto, aunque respetamos el espíritu de las mejores prácticas de Sitecore, optamos por no aplicar directamente el atributo className. En su lugar, utilizamos una solución personalizada para garantizar que se conserven las funcionalidades esenciales requeridas por Sitecore, incluyendo los parámetros de renderizado y las capacidades de edición, sin comprometer el enfoque de estilización que ofrece Chakra UI.
El Problema
El problema principal surge cuando los componentes no se actualizan correctamente en Sitecore Pages al utilizar Chakra UI para gestionar los estilos. Esta limitación afecta a los cambios en tiempo real mientras se edita la página, lo que puede ser bastante disruptivo. Esto sucede incluso en una implementación nativa de Sitecore, donde algunos contenedores y otros componentes no se actualizan como se espera. Para resolver esto, necesitábamos una forma confiable de forzar la actualización de estos componentes.
Aquí hay un ejemplo del problema antes de aplicar la solución:
Objetivo
Nuestro objetivo es implementar una solución que obligue a los componentes de React a actualizarse cuando sus parámetros o estilos cambien, asegurando que los cambios se reflejen automáticamente en Sitecore Pages sin requerir ajustes manuales.
Solución: Usar un Higher-Order Component (HOC) para Forzar Actualizaciones
Para resolver este problema, utilizamos un Higher-Order Component (HOC), un patrón común en React que permite extender la funcionalidad de un componente sin modificar su código central. En este caso, creamos un HOC que detecta cambios en los estilos y fuerza una actualización.
Implementación Paso a Paso de la Solución
1. Crear el HOC para Detectar Cambios de Estilo
El primer paso es crear un componente llamado paramsWatcher. Este HOC es responsable de envolver cualquier componente al que queramos aplicar la lógica de actualización.
// Author: David Ly
import { useSitecoreContext } from '@sitecore-jss/sitecore-jss-nextjs'; import { ComponentProps } from 'lib/component-props'; import { useRef, useState, useEffect } from 'react'; export function paramsWatcher<P extends ComponentProps>( WrappedComponent: React.ComponentType<P> ) { function WatcherComponent(props: P) { const ref = useRef<HTMLDivElement>(null); const [styles, setStyles] = useState(props.params.Styles); const context = useSitecoreContext(); const isEditing = context?.sitecoreContext?.pageEditing; useEffect(() => { if (!ref.current || !isEditing) { return; } console.log('Configurando el observador de mutaciones...'); const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === 'attributes' && mutation.attributeName === 'class') { const [, ...classes] = ref.current?.classList.value.split(' ') ?? []; console.log('Cambio detectado en las clases:', classes); setStyles(classes.join(' ')); } }); }); observer.observe(ref.current, { attributes: true }); return () => { console.log('Desconectando el observador de mutaciones...'); observer.disconnect(); }; }, [isEditing, props.params]); if (!isEditing) { return <WrappedComponent {...props} />; } // Actualizar los estilos en los parámetros antes de renderizar props.params.Styles = styles; return ( <> <div ref={ref} className={'component ' + styles} style={{ display: 'none' }} /> <WrappedComponent {...props} /> </> ); } return WatcherComponent; }
2. Envolver el Componente que Queremos Refrescar
Ahora que tenemos el HOC, podemos usarlo para envolver nuestro HeroComponent, que tiene problemas de actualización en Sitecore Pages.
import React from 'react';
import { Image as JssImage, Text as JssText } from '@sitecore-jss/sitecore-jss-nextjs';
import { Box, Text, Flex } from '@chakra-ui/react';
import {
AlignmentStyle,
ColorStyle,
HeroModel,
UpdateHeroParams,
} from 'lib/classes/Components/Hero/HeroLib';
import { paramsWatcher } from 'src/util/paramsWatcher';
const HeroDefaultComponent = (): JSX.Element => (
<Box textAlign="center">
<Text fontSize="xl">No hay contenido disponible. Asigne una fuente de datos.</Text>
</Box>
);
const HeroComponent = (
model: HeroModel & { heroStyles?: { Alignment: AlignmentStyle; Color: ColorStyle } }
): JSX.Element => {
const params =
model.heroStyles && model.heroStyles.Alignment && model.heroStyles.Color
? model.heroStyles
: UpdateHeroParams(model);
if (model.fields && model.fields.Image && model.fields.Title) {
return (
<Box position="relative">
<JssImage field={model.fields.Image} />
<Flex
color={{ base: 'uniblue', md: params.Color.textColor }}
position={{ base: 'static', md: 'absolute' }}
height="100%"
width="100%"
top="0"
flexDirection="column"
justifyContent="center"
>
<Box
ml={params.Alignment.marginLeft}
mr={params.Alignment.marginRight}
maxW={{ base: '100%', md: '50%' }}
textAlign="center"
>
<Text variant="h1" mb={0}>
<JssText field={model.fields.Title} />
</Text>
<Text variant="subtitle">
<JssText field={model.fields.Description} />
</Text>
</Box>
</Flex>
</Box>
);
}
return <HeroDefaultComponent />;
};
export default paramsWatcher(HeroComponent);
3. Probar la Solución en Sitecore Pages
Una vez que el HOC fue implementado y el componente envuelto, probamos la solución en Sitecore XM Cloud Pages. El componente ahora se actualiza automáticamente, como se esperaba.
Conclusión
La solución presentada utiliza un Higher-Order Component para observar cambios en los estilos de los componentes y forzar una actualización cuando sea necesario. Este método, que aprovecha React junto con la API MutationObserver, resuelve eficazmente el problema de los componentes que no se actualizan en Sitecore XM Cloud Pages.
Aunque este enfoque es eficiente, puede ser necesario realizar más pruebas e investigación, especialmente para componentes complejos como contenedores. También es importante destacar que adherirse a las mejores prácticas de Sitecore en cuanto a nomenclatura de clases es clave para asegurar una integración sin problemas con las funciones avanzadas de edición de XM Cloud.
Agradecemos especialmente a Jeff L'Heureux, a David Ly y a la comunidad de Slack de Sitecore por ayudarnos a encontrar y refinar esta solución.
Referencias
