最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Render different components based on screen size in Next.js - Stack Overflow

programmeradmin2浏览0评论

I'm trying to render render a ponent differently based on if the screen width < 768p.

If it is less than 768p, then load the hamburger menu. If not, load the entire menu.

Here's the code I use. The issue is, when the page first loads, I cannot observe the changes. But when I make the browser screen small and then increase it to it's original size; the effects take place.

I think it's because React is being rendered server-side. But still don't understand why it works by making the screen smaller and then bigger again.

import "twin.macro"
import { Global, css } from "@emotion/core"

import { useState, useEffect } from "react"

const Navbar = () => {
  const useWindowDimensions = () => {
    const hasWindow = typeof window !== "undefined"

    function getWindowDimensions() {
      const width = hasWindow ? window.innerWidth : null
      const height = hasWindow ? window.innerHeight : null
      return {
        width,
        height,
      }
    }

    const [windowDimensions, setWindowDimensions] = useState(
      getWindowDimensions()
    )

    useEffect(() => {
      if (hasWindow) {
        function handleResize() {
          setWindowDimensions(getWindowDimensions())
        }

        window.addEventListener("resize", handleResize)
        return () => window.removeEventListener("resize", handleResize)
      }
    }, [hasWindow])

    return windowDimensions
  }

  const { height, width } = useWindowDimensions()
  const breakpoint = 768

  return (
    <div>
      {width <= breakpoint ? (
        <div>
          <HamburgerMenu />
        </div>
      ) : (
        <div>
           <FullMenu />
        </div>
  )
}

export default Navbar

Is there a way I can force the Next.js app to apply the effects on the first render?

I'm trying to render render a ponent differently based on if the screen width < 768p.

If it is less than 768p, then load the hamburger menu. If not, load the entire menu.

Here's the code I use. The issue is, when the page first loads, I cannot observe the changes. But when I make the browser screen small and then increase it to it's original size; the effects take place.

I think it's because React is being rendered server-side. But still don't understand why it works by making the screen smaller and then bigger again.

import "twin.macro"
import { Global, css } from "@emotion/core"

import { useState, useEffect } from "react"

const Navbar = () => {
  const useWindowDimensions = () => {
    const hasWindow = typeof window !== "undefined"

    function getWindowDimensions() {
      const width = hasWindow ? window.innerWidth : null
      const height = hasWindow ? window.innerHeight : null
      return {
        width,
        height,
      }
    }

    const [windowDimensions, setWindowDimensions] = useState(
      getWindowDimensions()
    )

    useEffect(() => {
      if (hasWindow) {
        function handleResize() {
          setWindowDimensions(getWindowDimensions())
        }

        window.addEventListener("resize", handleResize)
        return () => window.removeEventListener("resize", handleResize)
      }
    }, [hasWindow])

    return windowDimensions
  }

  const { height, width } = useWindowDimensions()
  const breakpoint = 768

  return (
    <div>
      {width <= breakpoint ? (
        <div>
          <HamburgerMenu />
        </div>
      ) : (
        <div>
           <FullMenu />
        </div>
  )
}

export default Navbar

Is there a way I can force the Next.js app to apply the effects on the first render?

Share Improve this question edited Jul 2, 2020 at 13:51 theairbend3r asked Jul 2, 2020 at 9:22 theairbend3rtheairbend3r 7993 gold badges10 silver badges30 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 6

Next.js is universal, which means it executes code first server-side, then client-side. The window object is only present client-side

so in the first time it cannot access to window, so value of windowDimensions is null

when you resize you change the state make your app re-render , so it bee client-side-rendering => you can access to window , then your code work

Completing above answer, yes, with useEffect or useLayoutEffect, here is my own hook for this purpose:

import { useState, useEffect, useLayoutEffect, useCallback } from 'react';
import throttle from 'lodash/throttle';

type ScreenSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';

interface ScreenDimension {
  screenSize: ScreenSize | undefined;
  width: number | undefined;
  height: number | undefined;
}

function getScreenSize(width: number): ScreenSize {
  if (width >= 1536) {
    return '2xl';
  } else if (width >= 1280) {
    return 'xl';
  } else if (width >= 1024) {
    return 'lg';
  } else if (width >= 768) {
    return 'md';
  } else if (width >= 640) {
    return 'sm';
  } else {
    return 'xs';
  }
}

export function useScreenSize(): ScreenDimension {
  const [dimension, setDimension] = useState<ScreenDimension>({
    screenSize: undefined, // Default to 'xs'
    width: undefined,
    height: undefined,
  });

  // Define the resize handler
  const handleResize = useCallback(() => {
    if (typeof window !== 'undefined') {
      const newSize = getScreenSize(window.innerWidth);
      setDimension({
        screenSize: newSize,
        width: window.innerWidth,
        height: window.innerHeight,
      });
    }
  }, []);

  // Create a throttled version of the resize handler
  const throttledHandleResize = useCallback(throttle(handleResize, 300), [
    handleResize,
  ]);

  useLayoutEffect(() => {
    if (typeof window !== 'undefined') {
      // Run the handler immediately to set the initial screen size
      handleResize();

      // Set up the throttled resize listener
      window.addEventListener('resize', throttledHandleResize);

      // Clean up
      return () => {
        throttledHandleResize.cancel(); // Cancel the throttle function on cleanup
        window.removeEventListener('resize', throttledHandleResize);
      };
    }
  }, [throttledHandleResize]);

  return dimension;
}

发布评论

评论列表(0)

  1. 暂无评论