Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

馃悰 Next.js: Responsive props are not working properly #970

Open
alexasselin008 opened this issue May 12, 2022 · 2 comments
Open

馃悰 Next.js: Responsive props are not working properly #970

alexasselin008 opened this issue May 12, 2022 · 2 comments
Labels

Comments

@alexasselin008
Copy link
Member

alexasselin008 commented May 12, 2022

Describe the bug

When using Orbit in a Next.js App, using a responsive prop throws a warning

Steps to reproduce

import { Flex } from "@sharegate/orbit-ui";

export function MyView() {
    return (
        <Flex minHeight="100vh" marginTop={13} backgroundColor={{ base:"blue", sm:"red" }}>

        </Flex>
    );
}

Open console, and observe the following error :

next-dev.js?3515:25 Warning: Prop `style` did not match. Server: "min-height:100vh;margin-top:var(--o-ui-sp-13);background-color:blue;display:flex;flex-direction:row" Client: "min-height:100vh;margin-top:var(--o-ui-sp-13);background-color:red;display:flex;flex-direction:row"

Visual result:
The property background color is blue, but it should be red.

Expected results

No warning and a red background

@patricklafrance
Copy link
Member

What does other DS do to fix this? Does react-spectrum have a solution? I guess any DS supporting SSR and responsive props have the same issue.

@tjosepo
Copy link
Member

tjosepo commented Jul 7, 2023

I think the solution is to use useSyncExternalStore.

It's meant to handle external states (such as browser APIs) and server rendering. It lets you set a specific state for hydration.

I believe this might work.

Then again, do we want to set a state for hydration if we don't know ahead of time what it'll look like on the user's device?

export function BreakpointProvider({
    children,
    unsupportedMatchMediaBreakpoint = "lg"
}: BreakpointProvider) {

    // Listen to the "resize" event
    const subscribe = useCallback((subscriber) => {
       document.addEventListener("resize", subscriber);
       return () => document.removeEventListener("resize", subscriber);
    }, []);
    
    // Get the breakpoint on resize
    const getSnapshot = useCallback(() => {
         const matched: Breakpoint[] = [];

        for (const [key, value] of ReversedBreakpoints) {
           if (window.matchMedia(value).matches) {
                matched.push(key);
           }
        }

        return JSON.stringify(matched);
    }, []);

    // Get the default breakpoint, which will be used by the server and during hydration
    const getServerSnapshot = useCallback(() => {
        return JSON.stringify([unsupportedMatchMediaBreakpoint])
    }, [unsupportedMatchMediaBreakpoint]);

    // We return a JSON value because React does a shallow comparison with the result of `getSnapshot`
    // to know if it needs to rerender the component after `onresize` event is fired.
    const matchedBreakpointsJson = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
    const matchedBreakpoints = JSON.parse(matchedBreakpointsJson);

    return (
        <BreakpointContext.Provider value={{ matchedBreakpoints }}>
            {children}
        </BreakpointContext.Provider>
    );
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants