0

How can we display different html elements or components based on the size of the device? In react, a package called react-responsive can be used.But in Next, in every component that I use this package, ssr does not run on that component. What do you usually do for this? After all, many times the things that are present in, for example, the Windows display are very different from the things that should be displayed on the mobile phone, and other html codes should be used.

Xenomorph
  • 19
  • 4
javad kh
  • 11
  • 4
  • `window.addEventListener('resize', handleScreenSizeChange)` – eroironico Dec 06 '22 at 09:13
  • If I use this way, when the user enters the site with any device, the information related to other devices must also be downloaded.it's not good. – javad kh Dec 06 '22 at 09:18
  • You can add a small js function which does the device check and then fetch the device-size related static files (js or css or anything else). – vighnesh153 Dec 06 '22 at 09:19
  • @vighnesh153 how ? : ))) next js has directory routing.the components are not static files that I fetch them.it's automatic.I'm not experienced. : ) – javad kh Dec 06 '22 at 09:39
  • The server doesn't know your browser, so this would have to be solved with CSS-Media-Queries. – niorad Dec 06 '22 at 09:59
  • @javadkh what do you mean with " the information related to other devices must also be downloaded". The window resize listener is just a function executed client-side when the window is being resized, just that – eroironico Dec 06 '22 at 10:03
  • @nick for example in phone and windows I want use different html tags or components.if in function I use css and media queries and maybe display none .Again, the data should be loaded even if it belongs to other devices. I want it not to be sent for rendering at all.is it possible? – javad kh Dec 06 '22 at 10:36
  • Of course, let me try to post an answer – eroironico Dec 06 '22 at 10:41
  • @javadkh I posted an answer, take a look at it – eroironico Dec 06 '22 at 11:06

2 Answers2

1

Since NextJs is server side rendered, Server has no way to know what screen size it is sending the generated html to.

Now 'useEffect' comes to the rescue, since useEffect will only run on client side.

We can use it to get the screen size type once the page is loaded on the browser.

You can use 'react-device-detect' in the following manner.

  import { isMobile } from 'react-device-detect';

  // declare a state
  const [stateMobile, setState] = useState(false);

  useEffect(() => {
    if (!isMobile && typeof window !== 'undefined') {
      setState(stateMobile);
    } else {
      setState(!stateMobile);
    }
  }, []);

Then in your return

 return (
    <>
      {isMobile ? (
        <div> Mobile html here </div> 
       ) : (
        <div> Desktop html here </div>
      )}
    </>
  );

Use this method in you app.js file once and pass it as props to layout. Rather than using useeffect in each component.

0

Ok, so if i have understood your question you're trying to render a page with all devices-related components but show (with css) only the current device's component.

My approach would be:

In you page component:

// imports

const PageComponent = () => {
    const resizeTimoutRef = useRef(null)

    const [screenSizeState, setScreenSizeState] = useState(null) // initialized at null because on the server you don't have the DOM

    // basic function to get the breakpoint as a string based on the width of the window
    const getScreenSize = windowWidth =>
        windowWidth < 768
        ? "sm"
        windowWidth < 1024
        ? "md"
        : windowWidth < 1280
        ? "lg"
        : "xl"

    useEffect(() => {
        // here i set the current screen breakpoint immediately on the page load
        setScreenSizeState(getScreenSize(window.innerWidth))

        // here i add a listener for the resize event of the window
        window.addEventListener('resize', () => {
            // if a resize timout exists i clear it to prevent calling setState too many times
            if(resizeTimeoutRef.current) clearTimeout(resizeTimeoutRef.current)

            // here i set the timeout ref to the function that will be executed 150ms after the last resize event, again, to prevent calling setState too many times
            resizeTimeoutRef.current = setTimeout(() => setScreenSizeState(getScreenSize(window.innerWidth)), 150)
        })
    }, []) // empty array means that this hook will be called only once on the first page load

    return (
        <>
            <div style={{ display: screenSizeState === 'sm' ? 'block' : 'none' }}>
                Always present in the DOM but displayed only on 'sm' breakpoint
            </div>
            <div style={{ display: ['md', 'lg'].includes(screenSizeState) ? 'block' : 'none' }}>
                Always present in the DOM but displayed only on 'md' and 'lg' breakpoints
            </div>
        </>
    )
}

export default PageComponent

Said that, of course you can adapt this code into a custom hook to make it reusable by just doing:

// hooks/useScreenSize.js
// imports

export const useScreenSize = () => {
    const resizeTimoutRef = useRef(null)

    const [screenSizeState, setScreenSizeState] = useState(null)

    const getScreenSize = windowWidth =>
        windowWidth < 768
        ? "sm"
        windowWidth < 1024
        ? "md"
        : windowWidth < 1280
        ? "lg"
        : "xl"

    useEffect(() => {
        setScreenSizeState(getScreenSize(window.innerWidth))

        window.addEventListener('resize', () => {
            if(resizeTimeoutRef.current) clearTimeout(resizeTimeoutRef.current)

            resizeTimeoutRef.current = setTimeout(() => setScreenSizeState(getScreenSize(window.innerWidth)), 150)
        })
    }, [])

    return screenSizeState
}

So that you can use it in your page component like this:

// pages/index.jsx
// imports

const PageComponent = () => {
    const breakpointState = useScreenSize()

    return (
        <>
            <div style={{ display: breakpointState === 'sm' ? 'block' : 'none' }}>
                Always present in the DOM but displayed only on 'sm' breakpoint
            </div>
            <div style={{ display: ['md', 'lg'].includes(breakpointState) ? 'block' : 'none' }}>
                Always present in the DOM but displayed only on 'md' and 'lg' breakpoints
            </div>
        </>
    ) 
}

export default PageComponent
eroironico
  • 1,159
  • 1
  • 15