Nesting BleepsAnimator, Animator, and Grid components causes mangled HTML
VerdeVistaVoyager opened this issue · comments
Describe the bug
Nesting BleepsAnimator, Animator, and Grid components creates HTML nesting issues.
To reproduce
Steps to reproduce the behavior:
Combine BleepsAnimator, Animator, and Grid components.
Run the app.
Open the app in your browser and view browser dev tools.
See the error messages.
Expected behavior
See no error messages (nesting these Awres components should not create mangled HTML).
Screenshots
// ERROR 1 of 2
Warning: Expected server HTML to contain a matching <h1> in <span>.
at h1
at Animator (webpack-internal:///(app-client)/./node_modules/@arwes/react-animator/build/esm/Animator/Animator.js:24:13)
at span
at eval (webpack-internal:///(app-client)/./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
at p
at eval (webpack-internal:///(app-client)/./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
at Text (webpack-internal:///(app-client)/./node_modules/@arwes/react-text/build/esm/Text/Text.js:22:17)
at div
at eval (webpack-internal:///(app-client)/./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
at Grid (webpack-internal:///(app-client)/./node_modules/@mui/material/Grid/Grid.js:392:87)
at div
at eval (webpack-internal:///(app-client)/./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
at Grid (webpack-internal:///(app-client)/./node_modules/@mui/material/Grid/Grid.js:392:87)
at Animator (webpack-internal:///(app-client)/./node_modules/@arwes/react-animator/build/esm/Animator/Animator.js:24:13)
at Animator (webpack-internal:///(app-client)/./node_modules/@arwes/react-animator/build/esm/Animator/Animator.js:24:13)
at BleepsProvider (webpack-internal:///(app-client)/./node_modules/@arwes/react-bleeps/build/esm/BleepsProvider/BleepsProvider.js:12:13)
at AnimatorGeneralProvider (webpack-internal:///(app-client)/./node_modules/@arwes/react-animator/build/esm/AnimatorGeneralProvider/AnimatorGeneralProvider.js:10:13)
at Sandbox (webpack-internal:///(app-client)/./app/page.tsx:542:110)
at InnerLayoutRouter (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/layout-router.js:227:11)
at RedirectErrorBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/redirect-boundary.js:73:9)
at RedirectBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/redirect-boundary.js:81:11)
at NotFoundBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/not-found-boundary.js:59:11)
at LoadingBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/layout-router.js:324:11)
at ErrorBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/error-boundary.js:104:11)
at InnerScrollAndFocusHandler (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/layout-router.js:139:9)
at ScrollAndFocusHandler (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/layout-router.js:213:11)
at RenderFromTemplateContext (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/render-from-template-context.js:15:44)
at OuterLayoutRouter (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/layout-router.js:334:11)
at body
at html
at RedirectErrorBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/redirect-boundary.js:73:9)
at RedirectBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/redirect-boundary.js:81:11)
at ReactDevOverlay (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/react-dev-overlay/internal/ReactDevOverlay.js:70:9)
at NotFoundErrorBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/not-found-boundary.js:51:9)
at NotFoundBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/not-found-boundary.js:59:11)
at HotReload (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/react-dev-overlay/hot-reloader-client.js:318:11)
at Router (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/app-router.js:150:11)
at ErrorBoundaryHandler (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/error-boundary.js:77:9)
at ErrorBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/error-boundary.js:104:11)
at AppRouter (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/app-router.js:395:13)
at ServerRoot (webpack-internal:///(app-client)/./node_modules/next/dist/client/app-index.js:166:11)
at RSCComponent
at Root (webpack-internal:///(app-client)/./node_modules/next/dist/client/app-index.js:182:11)
// ERROR 2 of 2
Warning: validateDOMNesting(...): <h1> cannot appear as a descendant of <p>.
at h1
at Animator (webpack-internal:///(app-client)/./node_modules/@arwes/react-animator/build/esm/Animator/Animator.js:24:13)
at span
at eval (webpack-internal:///(app-client)/./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
at p
at eval (webpack-internal:///(app-client)/./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
at Text (webpack-internal:///(app-client)/./node_modules/@arwes/react-text/build/esm/Text/Text.js:22:17)
at div
at eval (webpack-internal:///(app-client)/./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
at Grid (webpack-internal:///(app-client)/./node_modules/@mui/material/Grid/Grid.js:392:87)
at div
at eval (webpack-internal:///(app-client)/./node_modules/@emotion/react/dist/emotion-element-c39617d8.browser.esm.js:60:66)
at Grid (webpack-internal:///(app-client)/./node_modules/@mui/material/Grid/Grid.js:392:87)
at Animator (webpack-internal:///(app-client)/./node_modules/@arwes/react-animator/build/esm/Animator/Animator.js:24:13)
at Animator (webpack-internal:///(app-client)/./node_modules/@arwes/react-animator/build/esm/Animator/Animator.js:24:13)
at BleepsProvider (webpack-internal:///(app-client)/./node_modules/@arwes/react-bleeps/build/esm/BleepsProvider/BleepsProvider.js:12:13)
at AnimatorGeneralProvider (webpack-internal:///(app-client)/./node_modules/@arwes/react-animator/build/esm/AnimatorGeneralProvider/AnimatorGeneralProvider.js:10:13)
at Sandbox (webpack-internal:///(app-client)/./app/page.tsx:542:110)
at InnerLayoutRouter (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/layout-router.js:227:11)
at RedirectErrorBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/redirect-boundary.js:73:9)
at RedirectBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/redirect-boundary.js:81:11)
at NotFoundBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/not-found-boundary.js:59:11)
at LoadingBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/layout-router.js:324:11)
at ErrorBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/error-boundary.js:104:11)
at InnerScrollAndFocusHandler (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/layout-router.js:139:9)
at ScrollAndFocusHandler (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/layout-router.js:213:11)
at RenderFromTemplateContext (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/render-from-template-context.js:15:44)
at OuterLayoutRouter (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/layout-router.js:334:11)
at body
at html
at RedirectErrorBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/redirect-boundary.js:73:9)
at RedirectBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/redirect-boundary.js:81:11)
at ReactDevOverlay (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/react-dev-overlay/internal/ReactDevOverlay.js:70:9)
at NotFoundErrorBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/not-found-boundary.js:51:9)
at NotFoundBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/not-found-boundary.js:59:11)
at HotReload (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/react-dev-overlay/hot-reloader-client.js:318:11)
at Router (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/app-router.js:150:11)
at ErrorBoundaryHandler (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/error-boundary.js:77:9)
at ErrorBoundary (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/error-boundary.js:104:11)
at AppRouter (webpack-internal:///(app-client)/./node_modules/next/dist/client/components/app-router.js:395:13)
at ServerRoot (webpack-internal:///(app-client)/./node_modules/next/dist/client/app-index.js:166:11)
at RSCComponent
at Root (webpack-internal:///(app-client)/./node_modules/next/dist/client/app-index.js:182:11)
Development environment
// package.json
"dependencies": {
"@arwes/react": "^1.0.0-alpha.23",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.14.7",
"@mui/material": "^5.14.0",
"@types/node": "20.4.2",
"@types/react": "18.2.15",
"@types/react-dom": "18.2.7",
"autoprefixer": "10.4.14",
"eslint": "8.45.0",
"eslint-config-next": "13.4.10",
"next": "13.4.10",
"postcss": "8.4.26",
"react": "18.2.0",
"react-dom": "18.2.0",
"tailwindcss": "3.3.3",
"typescript": "5.1.6"
}
// Node version
v18.16.0
// OS
Ubuntu 20.04
## Testing environment
// Browser
Chrome
Hello @VerdeVistaVoyager!
I see you are using Material UI but I think the problem is only related to the Arwes components. Can you please create a sandbox example in https://arwes.dev/play? Select any existing sandbox and click the option "Customize" you so you can share the URL with the example code.
Thanks for reporting!
@romelperez Yup! Since the playground is client-only environment, there is no hydration issue to troubleshoot within the Awres playground. This is going to be specific to @Arwes
integration with Next.js
.
Code snippet below includes the following changes:
- Material UI removed
- Material UI components, such as
<Grid>
, replaced with placeholder<div>
's
Behavior:
- no hydration error nor nested HTML errors in Arwes playground
- hydration errors and nested HTML errors when using
Next.js
and'use client'
import React, { useEffect, type ReactElement, useState, ReactNode } from 'react';
import { createRoot } from 'react-dom/client';
import { type CSSObject, Global } from '@emotion/react';
import {
createAppTheme,
createAppStylesBaseline,
type AnimatorGeneralProviderSettings,
AnimatorGeneralProvider,
Animator,
Animated,
aaVisibility,
aa,
type BleepsProviderSettings,
BleepsProvider,
useBleeps,
BleepsOnAnimator,
FrameSVGCorners,
GridLines,
Dots,
MovingLines,
Text,
FrameSVGNefrex,
AnimatorInterface,
AnimatorNode,
useAnimator,
AnimatorProps,
} from '@arwes/react';
interface ExperimentItemProps extends AnimatorProps {
children?: ReactNode
}
const ExperimentItem = ({ children, ...animator }: ExperimentItemProps): ReactElement => {
return (
<Animator {...animator}>
<Animated
style={{ margin: 5, width: 40, height: 15, backgroundColor: '#777' }}
animated={{
transitions: {
entering: {
x: [0, 50],
backgroundColor: ['#0ff', '#ff0'],
options: { easing: 'linear' }
},
exiting: {
x: [50, 0],
backgroundColor: ['#ff0', '#0ff'],
options: { easing: 'linear' }
}
}
}}
hideOnExited={false}
/>
<div style={{ marginLeft: 20 }}>
{children}
</div>
</Animator>
);
};
const Experiment = () => {
return (
<AnimatorGeneralProvider duration={{ enter: 0.4, stagger: 0.1 }}>
<ExperimentItem manager='sequence' combine>
<ExperimentItem manager='parallel' combine>
{Array(5).fill(0).map((_, i) => <ExperimentItem key={i} />)}
</ExperimentItem>
<ExperimentItem manager='stagger' combine>
{Array(5).fill(0).map((_, i) => <ExperimentItem key={i} />)}
</ExperimentItem>
<ExperimentItem manager='sequence' combine>
{Array(5).fill(0).map((_, i) => <ExperimentItem key={i} />)}
</ExperimentItem>
</ExperimentItem>
</AnimatorGeneralProvider>
)
}
const theme = createAppTheme();
const stylesBaseline = createAppStylesBaseline(theme);
const Background = (): ReactElement => {
return (
<div
style={{
position: 'fixed',
inset: 0,
backgroundColor: theme.colors.primary.bg(1),
backgroundImage: 'linear-gradient(rgba(0, 0, 0, 0.9), rgba(0, 0, 0, 0.9)), url("bg1.jpeg")',
backgroundSize: 'cover',
}}
>
<GridLines lineColor={theme.colors.primary.deco(0)} />
<Dots color={theme.colors.primary.deco(1)} />
<MovingLines lineColor={theme.colors.primary.deco(2)} />
</div>
);
};
const TagLines = () => {
const childrenList = [
'TEAM PLAYER',
'SOFTWARE ENGINEER',
'JAVASCRIPT DEVELOPER',
'NODE.JS DEVELOPER',
'FRONT END DEVELOPER',
'API DEVELOPER',
'CLOUD ENGINEER',
'PEOPLE FIRST DEVELOPER',
'LIFE LONG LEARNER'
];
const [childrenIndex, setChildrenIndex] = useState(0);
useEffect(() => {
const timeout = setTimeout(() => {
const isLastIndex = childrenIndex === childrenList.length - 1;
const nextIndex = isLastIndex ? 0 : childrenIndex + 1;
setChildrenIndex(nextIndex);
}, 2000);
return () => clearTimeout(timeout);
}, [childrenIndex, childrenList.length]);
return (
<h4>
<Animator duration={{ enter: 1, exit: 1 }}>
<Text fixed>
{childrenList[childrenIndex]}
</Text>
</Animator>
</h4>
);
}
const Sandbox = () => {
// ignoring the hydration errors
// TODO: check if awres library has recommendations for <AnimatorGeneralProvider ...> usage
const [floatingButtonVisible, setFloatingButtonVisible] = useState(false);
return (
<>
<Global styles={stylesBaseline as Record<string, CSSObject>} />
<AnimatorGeneralProvider duration={{
enter: 0.2,
exit: 0.2,
stagger: 0.04
}}>
<BleepsProvider
master={{ volume: 0.9 }}
bleeps={{
intro: {
sources: [{ src: 'https://next.arwes.dev/assets/sounds/intro.mp3', type: 'audio/mpeg' }]
},
click: {
sources: [{ src: 'https://next.arwes.dev/assets/sounds/click.mp3', type: 'audio/mpeg' }]
}
}}>
<Animator active={true} combine manager='stagger'>
<Animator>
<Background />
</Animator>
<Animator>
<div>
<div>
<Text
style={{ color: '#ddd', fontFamily: 'monospace' }}
manager='decipher'
easing='outSine'
fixed
>
<Animator active={true} duration={{ enter: 1.5, exit: 1.5 }}>
<h1
style={{
width: '320px',
textAlign: 'center',
fontSize: '48px'
}}>
<span></span>
</h1>
</Animator>
</Text>
</div>
<div>
<TagLines />
</div>
<div>
<div
style={{ marginTop: '50px' }}>
<button
id="about-btn"
// href="#about"
// variant="outlined"
onClick={() => {
console.log('clicked')
setFloatingButtonVisible(!floatingButtonVisible);
}}
>About</button>
<button
id="experiment-btn"
// href="#experiment"
// variant="outlined"
style={{
margin: '5px'
}}
onClick={() => {
setFloatingButtonVisible(!floatingButtonVisible);
}}
>Experiment</button>
</div>
</div>
</div>
</Animator>
<Animator>
<div>
<div>
<h1 style={{ width: '220px', fontSize: '16px', marginBottom: '10%' }} id="about">
<Text style={{ color: 'yellow' }}>
Hello </Text>
</h1>
</div>
<div>
<h1 style={{ width: '220px', fontSize: '16px', marginBottom: '10%' }}>
<Text style={{ color: 'yellow' }}>
From year to year I had op
</Text>
</h1>
</div>
<div>
<h1 style={{ width: '220px', fontSize: '16px', marginBottom: '10%' }}>
<Text style={{ color: 'yellow' }}>
I am
</Text>
</h1>
</div>
<div>
<h1 style={{ width: '220px', fontSize: '16px', marginBottom: '10%' }}>
<Text style={{ color: 'yellow' }}>
Noodles available upon request.
</Text>
</h1>
</div>
</div>
</Animator>
</Animator>
</BleepsProvider>
</AnimatorGeneralProvider >
<div>
<div>
<div id="experiment">
<Experiment />
</div>
</div>
</div>
<div
onClick={() => {
setFloatingButtonVisible(!floatingButtonVisible);
}}
style={{
display: floatingButtonVisible ? 'block' : 'none'
}}>
<div>
<div>
<div />
</div>
</div>
</div >
</>
);
};
createRoot(document.querySelector('#root') as HTMLElement).render(<Sandbox />);
Opportunity for me to better understand hydration behavior with Nextjs and Reactjs.
Can't promise dedicated time to look into this, but the outcome would look like a barebones BleepsAnimator and Animator example on a Next.js page such that it (hydrates correctly OR has no hydration) AND (has no nested HTML errors).
If I ever come back to this I'll leave a comment