From 28a947e17dbcccb14d93d4c42fe019f54d6711e6 Mon Sep 17 00:00:00 2001 From: Lukas Harbarth Date: Fri, 7 Feb 2025 13:26:16 +0100 Subject: [PATCH 1/4] fix: suppress Next.js hydration warning --- packages/main/src/internal/withWebComponent.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/main/src/internal/withWebComponent.tsx b/packages/main/src/internal/withWebComponent.tsx index e715771ee59..a6bb38bb558 100644 --- a/packages/main/src/internal/withWebComponent.tsx +++ b/packages/main/src/internal/withWebComponent.tsx @@ -45,6 +45,7 @@ export const withWebComponent = , RefType = Ui >; const [isDefined, setIsDefined] = useState(definedWebComponents.has(Component)); + const [isClient, setIsClient] = useState(typeof window === 'undefined'); // regular props (no booleans, no slots and no events) const regularProps = regularProperties.reduce((acc, name) => { @@ -188,7 +189,11 @@ export const withWebComponent = , RefType = Ui }); }, [Component, ...propsToApply]); - if ((waitForDefine && !isDefined) || typeof window === 'undefined') { + useEffect(() => { + setIsClient(true); + }, []); + + if (waitForDefine && !isDefined) { return null; } @@ -203,7 +208,7 @@ export const withWebComponent = , RefType = Ui ref={componentRef} {...booleanProps} {...restRegularProps} - {...eventHandlers} + {...(isClient ? eventHandlers : {})} {...nonWebComponentRelatedProps} overflow-mode={overflowMode ?? (showOverflowInPopover ? 'Popover' : 'InPlace')} // @ts-expect-error: text is available @@ -222,7 +227,7 @@ export const withWebComponent = , RefType = Ui ref={componentRef} {...booleanProps} {...regularProps} - {...eventHandlers} + {...(isClient ? eventHandlers : {})} {...nonWebComponentRelatedProps} class={className} suppressHydrationWarning From fd5be8269d2376e3be2325fdee153ad7b71e3bb1 Mon Sep 17 00:00:00 2001 From: Lukas Harbarth Date: Fri, 7 Feb 2025 13:52:03 +0100 Subject: [PATCH 2/4] Update withWebComponent.tsx --- packages/main/src/internal/withWebComponent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/main/src/internal/withWebComponent.tsx b/packages/main/src/internal/withWebComponent.tsx index a6bb38bb558..4c1b4571712 100644 --- a/packages/main/src/internal/withWebComponent.tsx +++ b/packages/main/src/internal/withWebComponent.tsx @@ -45,7 +45,7 @@ export const withWebComponent = , RefType = Ui >; const [isDefined, setIsDefined] = useState(definedWebComponents.has(Component)); - const [isClient, setIsClient] = useState(typeof window === 'undefined'); + const [isClient, setIsClient] = useState(typeof window !== 'undefined'); // regular props (no booleans, no slots and no events) const regularProps = regularProperties.reduce((acc, name) => { From 13ffaa8b7f028cb0f606833235b1ab3f73f9947d Mon Sep 17 00:00:00 2001 From: Lukas Harbarth Date: Fri, 7 Feb 2025 14:43:17 +0100 Subject: [PATCH 3/4] fix logic --- packages/main/src/internal/withWebComponent.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/main/src/internal/withWebComponent.tsx b/packages/main/src/internal/withWebComponent.tsx index 4c1b4571712..07b3dbb3fba 100644 --- a/packages/main/src/internal/withWebComponent.tsx +++ b/packages/main/src/internal/withWebComponent.tsx @@ -5,7 +5,7 @@ import { useIsomorphicLayoutEffect, useSyncRef } from '@ui5/webcomponents-react- import type { ComponentType, ReactElement, ReactNode, Ref } from 'react'; import { cloneElement, forwardRef, Fragment, isValidElement, useEffect, useState, version } from 'react'; import type { CommonProps, Ui5DomRef } from '../types/index.js'; -import { camelToKebabCase, capitalizeFirstLetter, kebabToCamelCase, parseSemVer } from './utils.js'; +import { camelToKebabCase, capitalizeFirstLetter, kebabToCamelCase, parseSemVer } from './utils.ts'; const createEventPropName = (eventName: string) => `on${capitalizeFirstLetter(kebabToCamelCase(eventName))}`; @@ -43,10 +43,7 @@ export const withWebComponent = , RefType = Ui const Component = (tagNameSuffix ? `${tagName}-${tagNameSuffix}` : tagName) as unknown as ComponentType< CommonProps & { class?: string; ref?: Ref } >; - const [isDefined, setIsDefined] = useState(definedWebComponents.has(Component)); - const [isClient, setIsClient] = useState(typeof window !== 'undefined'); - // regular props (no booleans, no slots and no events) const regularProps = regularProperties.reduce((acc, name) => { if (Object.prototype.hasOwnProperty.call(rest, name) && isPrimitiveAttribute(rest[name])) { @@ -153,6 +150,9 @@ export const withWebComponent = , RefType = Ui return events; }, {}); + // In React 19 events aren't correctly attached after hydration + const [attachEvents, setAttachEvents] = useState(!webComponentsSupported || !Object.keys(eventHandlers).length); // apply workaround only for React19 and if event props are defined + // non web component related props, just pass them const nonWebComponentRelatedProps = Object.entries(rest) .filter(([key]) => !regularProperties.includes(key)) @@ -190,7 +190,7 @@ export const withWebComponent = , RefType = Ui }, [Component, ...propsToApply]); useEffect(() => { - setIsClient(true); + setAttachEvents(true); }, []); if (waitForDefine && !isDefined) { @@ -208,7 +208,7 @@ export const withWebComponent = , RefType = Ui ref={componentRef} {...booleanProps} {...restRegularProps} - {...(isClient ? eventHandlers : {})} + {...(attachEvents ? eventHandlers : {})} {...nonWebComponentRelatedProps} overflow-mode={overflowMode ?? (showOverflowInPopover ? 'Popover' : 'InPlace')} // @ts-expect-error: text is available @@ -227,7 +227,7 @@ export const withWebComponent = , RefType = Ui ref={componentRef} {...booleanProps} {...regularProps} - {...(isClient ? eventHandlers : {})} + {...(attachEvents ? eventHandlers : {})} {...nonWebComponentRelatedProps} class={className} suppressHydrationWarning From de947bd37f686726f0f524b65199c234e654f858 Mon Sep 17 00:00:00 2001 From: Lukas Harbarth Date: Fri, 7 Feb 2025 14:51:49 +0100 Subject: [PATCH 4/4] Update withWebComponent.tsx --- packages/main/src/internal/withWebComponent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/main/src/internal/withWebComponent.tsx b/packages/main/src/internal/withWebComponent.tsx index 07b3dbb3fba..6281b1ccbbb 100644 --- a/packages/main/src/internal/withWebComponent.tsx +++ b/packages/main/src/internal/withWebComponent.tsx @@ -5,7 +5,7 @@ import { useIsomorphicLayoutEffect, useSyncRef } from '@ui5/webcomponents-react- import type { ComponentType, ReactElement, ReactNode, Ref } from 'react'; import { cloneElement, forwardRef, Fragment, isValidElement, useEffect, useState, version } from 'react'; import type { CommonProps, Ui5DomRef } from '../types/index.js'; -import { camelToKebabCase, capitalizeFirstLetter, kebabToCamelCase, parseSemVer } from './utils.ts'; +import { camelToKebabCase, capitalizeFirstLetter, kebabToCamelCase, parseSemVer } from './utils.js'; const createEventPropName = (eventName: string) => `on${capitalizeFirstLetter(kebabToCamelCase(eventName))}`;