nextui-org / nextui

🚀 Beautiful, fast and modern React UI library.
https://nextui.org
MIT License
20.71k stars 1.27k forks source link

[BUG] - REDUCE MOTION WITH CALENDAR HYDRATATION #3395

Open viclafouch opened 2 weeks ago

viclafouch commented 2 weeks ago

NextUI Version

2.4.2

Describe the bug

The component Calendar has an hydratation issue with disableAnimation prop (from NextUIProvider) when using SSR and useReduceMotion.

Let's say we want to disable animation when user has reduce motion enabled.

On server, disableAnimation is undefined because we don't know the config of reduce motion from server (that's normal). On client, if disableAnimation is true, we have hydratation issue.

Steps to Reproduce the Bug or Issue

  1. Enable reduceMotion on your machine
  2. Add this in your app.tsx
 const isReducedMotion = useReducedMotion() // from framer-motion

// ....

// HYDRATATION ISSUE HERE
<NextUIProvider disableAnimation={isReducedMotion}>
   <Calendar calendarWidth={350} />
</NextUIProvider>

Expected behavior

No Hydration issue just because of reduceMotion is changing to true to false or whatever

Screenshots or Videos

Capture d’écran 2024-07-02 à 15 59 51

Operating System Version

MacOS

Browser

Chrome

linear[bot] commented 2 weeks ago

ENG-1095 [BUG] - REDUCE MOTION WITH CALENDAR HYDRATATION

ryo-manba commented 1 week ago

To avoid the hydration error, it's necessary to ensure that the initial render is consistent between the server and the client. useEffect hook ensures that the state is only updated on the client side, avoiding discrepancies between the server-rendering and client-rendering HTML.

The following code resolves the issue.

export default function Page() {
  const reducedMotion = useReducedMotion();
  const [isReducedMotion, setIsReducedMotion] = useState(false);

  useEffect(() => {
    setIsReducedMotion(reducedMotion ?? false);
  }, [reducedMotion]);

  return (
    <NextUIProvider disableAnimation={isReducedMotion}>
      <Calendar calendarWidth={350} />
    </NextUIProvider>
  );
}

Reference: https://github.com/vercel/next.js/discussions/17443#discussioncomment-87097

viclafouch commented 1 week ago

Hello @ryo-manba and thank you for your response.

Normally, this kind of state (isReducedMotion) shouldn't create any issue with hydratation. I suppose you render an element in calendar if the reducedMotion value is true or false. But why not using css (tailwind) normally and use display:none or whatever depending of the value ?

Since this motion reduce can be known in css (tailwind), if you render or hide an element using this property, I think you should recond using tailwind instead.

<div class="motion-reduce:hidden"> 
    . . . 
</div>

And using an Effect here just to fix this component and rerender again my all app, looks like a bit hard :(

WDYT ?

ryo-manba commented 3 days ago

Thank you for the suggestion. Your idea makes sense, and I'll give it a try. I'll also explore whether I can improve it by better utilizing the options in Framer Motion.