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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

useMediaQuery does not play well with dynamic components in Next.js #35891

Open
2 tasks done
yjoer opened this issue Jan 20, 2023 · 5 comments
Open
2 tasks done

useMediaQuery does not play well with dynamic components in Next.js #35891

yjoer opened this issue Jan 20, 2023 · 5 comments
Assignees
Labels
bug 🐛 Something doesn't work hook: useMediaQuery nextjs ready to take Help wanted. Guidance available. There is a high chance the change will be accepted

Comments

@yjoer
Copy link

yjoer commented Jan 20, 2023

Duplicates

  • I have searched the existing issues

Latest version

  • I have tested the latest version

Steps to reproduce 🕹

Link to live example:
https://codesandbox.io/p/sandbox/usemediaquery-and-suspense-ipi2y0

Steps:

  1. Navigate to https://ipi2y0-3000.preview.csb.app/example for the reproduction of the error.
  2. We can also produce the same error by updating states in useLayoutEffect https://ipi2y0-3000.preview.csb.app/possible-cause.
  3. If updating states in useLayoutEffect is the root cause in this case, a possible fix is to wrap these functions in startTransition as in https://ipi2y0-3000.preview.csb.app/possible-fix

Current behavior 😯

Next.js emits the following error when useMediaQuery is used in a component with dynamically imported components.

Error: This Suspense boundary received an update before it finished hydrating. This caused the boundary to switch to client rendering. The usual way to fix this is to wrap the original update in startTransition.

Expected behavior 🤔

There should not be any errors when using the hook with dynamically imported components.

Context 🔦

I am using useMediaQuery in a component with lazy-loaded components.

Your environment 🌎

npx @mui/envinfo
 Browser: Google Chrome 109

 System:
    OS: Linux 5.15 Debian GNU/Linux 11 (bullseye) 11 (bullseye)
  Binaries:
    Node: 16.17.0 - /usr/local/bin/node
    Yarn: 1.22.19 - /usr/local/bin/yarn
    npm: 8.15.0 - /usr/local/bin/npm
  Browsers:
    Chrome: Not Found
    Firefox: Not Found
  npmPackages:
    @emotion/react: ^11.10.5 => 11.10.5 
    @emotion/styled: ^11.10.5 => 11.10.5 
    @mui/base:  5.0.0-alpha.114 
    @mui/core-downloads-tracker:  5.11.5 
    @mui/material: ^5.11.5 => 5.11.5 
    @mui/private-theming:  5.11.2 
    @mui/styled-engine:  5.11.0 
    @mui/system:  5.11.5 
    @mui/types:  7.2.3 
    @mui/utils:  5.11.2 
    @types/react: 18.0.26 => 18.0.26 
    react: 18.2.0 => 18.2.0 
    react-dom: 18.2.0 => 18.2.0 
    typescript: 4.9.4 => 4.9.4 
@yjoer yjoer added the status: waiting for maintainer These issues haven't been looked at yet by a maintainer label Jan 20, 2023
@zannager zannager added the package: system Specific to @mui/system label Jan 23, 2023
@mnajdova
Copy link
Member

@yjoer would you like to submit PR with the proposed fix? We could try using startTransition and add some tests to see if everything would work as expected.

@mnajdova mnajdova added bug 🐛 Something doesn't work hook: useMediaQuery ready to take Help wanted. Guidance available. There is a high chance the change will be accepted and removed package: system Specific to @mui/system status: waiting for maintainer These issues haven't been looked at yet by a maintainer labels Jan 23, 2023
@yjoer
Copy link
Author

yjoer commented Jan 23, 2023

After digging for a while, I have confirmed that this has nothing to do with useLayoutEffect. As I am using React 18 and useSyncExternalStore is available, the implementation used is useMediaQueryNew. Initially, I thought to ignore the server snapshot and have the client re-render the component after mounting, provided there is a mismatch. But it seems like the way how useSyncExternalStore works does not allow this. React would re-render the outer tree before the inner tree within the suspense boundary hydrates. Along these lines, I suppose the straightforward solution is to either:

  1. Ensure the snapshots between the client and server match.
  2. Disable SSR if we wanted to use useMediaQuery in a component with suspended components.
  3. Use useMediaQuery in a component without suspended components. This will always work even when there is a mismatch.

@himself65
Copy link

I faced the same problem

@himself65
Copy link

workaround

import { useMediaQuery } from '@react-hookz/web';

export function useSidebarFloating() {
  const theme = useTheme();
  return (
    useMediaQuery(theme.breakpoints.down('md').replace(/^@media( ?)/m, '')) ??
    false
  );
}

@himself65
Copy link

workaround

import { useMediaQuery } from '@react-hookz/web';

export function useSidebarFloating() {

const theme = useTheme();

return (

useMediaQuery(theme.breakpoints.down('md').replace(/^@media( ?)/m, '')) ??
false

);

}

This is great but it doesn't work for theme.breakpoints.up()

I believe mui returns some extra string in breakpoints. You should check it and use regex to remove extra content

@zannager zannager added the nextjs label Jan 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🐛 Something doesn't work hook: useMediaQuery nextjs ready to take Help wanted. Guidance available. There is a high chance the change will be accepted
Projects
None yet
Development

No branches or pull requests

4 participants