如何使用反应钩子检测 Next.js SSR 中的窗口大小?

Goo*_*mja 12 reactjs server-side-rendering next.js react-dates

我正在使用 Next.js 和react-dates构建一个应用程序。

我有两个组件DateRangePicker组件和DayPickerRangeController组件。

当窗口的宽度大于1180px时,我想渲染DateRangePicker,如果尺寸小于这个,我想渲染DayPickerRangeController

这是代码:

      windowSize > 1180 ?
           <DateRangePicker
             startDatePlaceholderText="Start"
             startDate={startDate}
             startDateId="startDate"
             onDatesChange={handleOnDateChange}
             endDate={endDate}
             endDateId="endDate"
             focusedInput={focus}
             transitionDuration={0}
             onFocusChange={(focusedInput) => {
               if (!focusedInput) {
                 setFocus("startDate")
               } else {
                 setFocus(focusedInput)
                }
               }}
                /> :
             <DayPickerRangeController
               isOutsideRange={day => isInclusivelyBeforeDay(day, moment().add(-1, 'days'))}
               startDate={startDate}
               onDatesChange={handleOnDateChange}
               endDate={endDate}
               focusedInput={focus}
               onFocusChange={(focusedInput) => {
               if (!focusedInput) {
                 setFocus("startDate")
                 } else {
                  setFocus(focusedInput)
                 }
               }}
              /> 
          }
Run Code Online (Sandbox Code Playgroud)

我通常使用反应钩与窗口对象检测窗口屏幕宽度像

但是我发现ssr的时候这种方式是不可用的,因为ssr渲染没有window对象。

无论ssr如何,是否有另一种方法可以安全地获得窗口大小?

Dar*_*ert 34

您可以通过添加以下代码来避免在 ssr 中调用检测函数:

// make sure your function is being called in client side only
if (typeof window !== 'undefined') {
  // detect window screen width function
}
Run Code Online (Sandbox Code Playgroud)

来自您链接的完整示例:

import { useState, useEffect } from 'react';

// Usage
function App() {
  const size = useWindowSize();

  return (
    <div>
      {size.width}px / {size.height}px
    </div>
  );
}

// Hook
function useWindowSize() {
  // Initialize state with undefined width/height so server and client renders match
  // Learn more here: https://joshwcomeau.com/react/the-perils-of-rehydration/
  const [windowSize, setWindowSize] = useState({
    width: undefined,
    height: undefined,
  });

  useEffect(() => {
    // only execute all the code below in client side
    if (typeof window !== 'undefined') {
      // Handler to call on window resize
      function handleResize() {
        // Set window width/height to state
        setWindowSize({
          width: window.innerWidth,
          height: window.innerHeight,
        });
      }
    
      // Add event listener
      window.addEventListener("resize", handleResize);
     
      // Call handler right away so state gets updated with initial window size
      handleResize();
    
      // Remove event listener on cleanup
      return () => window.removeEventListener("resize", handleResize);
    }
  }, []); // Empty array ensures that effect is only run on mount
  return windowSize;
}
Run Code Online (Sandbox Code Playgroud)

  • 如果我没记错的话, useEffect 仅在客户端上运行,因此不需要 if (typeof window !== 'undefined') 条件 (2认同)

Has*_*san 10

虽然Darryl RN提供了绝对正确的答案。我想说一句:您真的不需要检查window内部对象是否存在,useEffect因为useEffect它只在客户端运行而从不在服务器端运行,并且该window对象始终在客户端可用。

  • 这是不正确的,因为如果未定义的情况不受保护,nextjs 将对代码进行 lint 处理,并且无法编译,并出现有关变量不存在的错误 (2认同)

fru*_*oaf 7

useEffect(()=> {
   window.addEventListener('resize', ()=> {
       console.log(window.innerHeight, window.innerWidth)
   })
}, [])
Run Code Online (Sandbox Code Playgroud)