Home > Back-end >  how can i use components based on device size?
how can i use components based on device size?

Time:12-06

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.

CodePudding user response:

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

CodePudding user response:

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.

  • Related