Leo*_*ban 9 javascript reactjs next.js
在我的NextJS应用中,我似乎无法访问window
未处理的拒绝(ReferenceError):未定义窗口
componentWillMount() {
    console.log('window.innerHeight', window.innerHeight);
}
Rot*_*eti 59
如果您使用React Hooks,您可以将代码移动到Effect Hook 中:
import * as React from "react";
export const MyComp = () => {
  React.useEffect(() => {
    // window is accessible here.
    console.log("window.innerHeight", window.innerHeight);
  }, []);
  return (<div></div>)
}
里面的代码useEffect只在客户端(在浏览器中)执行,因此它可以访问window.
小智 44
无SSR
https://nextjs.org/docs/advanced-features/dynamic-import#with-no-ssr
import dynamic from 'next/dynamic'
const DynamicComponentWithNoSSR = dynamic(
  () => import('../components/hello3'),
  { ssr: false }
)
function Home() {
  return (
    <div>
      <Header />
      <DynamicComponentWithNoSSR />
      <p>HOME PAGE is here!</p>
    </div>
  )
}
export default Home
Ism*_*rov 29
发生错误的原因是窗口尚不可用,而组件仍在安装中。您可以在组件安装后访问窗口对象。
您可以创建一个非常有用的钩子来获得动态window.innerHeight或window.innerWidth
const useDeviceSize = () => {
  const [width, setWidth] = useState(0)
  const [height, setHeight] = useState(0)
  const handleWindowResize = () => {
    setWidth(window.innerWidth);
    setHeight(window.innerHeight);
  }
  useEffect(() => {
    // component is mounted and window is available
    handleWindowResize();
    window.addEventListener('resize', handleWindowResize);
    // unsubscribe from the event on component unmount
    return () => window.removeEventListener('resize', handleWindowResize);
  }, []);
  return [width, height]
}
export default useDeviceSize 
使用案例:
const [width, height] = useDeviceSize();
Ale*_*sky 19
将代码从componentWillMount()移至componentDidMount():
componentDidMount() {
  console.log('window.innerHeight', window.innerHeight);
}
在NextJS中,componentDidMount()仅执行客户端window和其他特定于浏览器的API 的客户端。从NextJS Wiki:
Next.js是通用的,这意味着它首先在服务器端执行代码,然后在客户端执行代码。window对象仅在客户端存在,因此,如果您绝对需要在某些React组件中访问它,则应将该代码放在componentDidMount中。此生命周期方法将仅在客户端上执行。您可能还需要检查是否没有满足您需求的替代通用库。
同样,React v17 componentWillMount()中将弃用该功能,因此在不久的将来有效地使用它是不安全的。
Dar*_*ert 16
其他解决方案是process.browser仅在客户端渲染期间使用来执行命令。
if (process.browser) {
  // client-side-only code
}小智 16
global?.window && window.innerHeight
使用运算符很重要?.,否则构建命令可能会崩溃。
Par*_*avi 12
有点晚了,但您也可以考虑在关闭该组件时使用动态导入。nextSSR
您可以在动态函数内扭曲组件的导入,然后使用返回的值作为实际组件。
import dynamic from 'next/dynamic'
const BoardDynamic = dynamic(() => import('../components/Board.tsx'), {
  ssr: false,
})
<>
   <BoardDynamic />
</>
小智 11
在类Component的构造函数中,您可以添加
if (typeof window === 'undefined') {
    global.window = {}
}
例子:
import React, { Component } from 'react'
class MyClassName extends Component {
    constructor(props){
        super(props)
        ...
        if (typeof window === 'undefined') {
            global.window = {}
        }
}
这将避免错误(在我的情况下,在我单击重新加载页面后会发生错误)。
小智 11
有史以来最好的解决方案
import dynamic from 'next/dynamic';
const Chart = dynamic(()=> import('react-apexcharts'), {
    ssr:false,
})小智 10
componentWillMount()生命周期钩子适用于服务器和客户端。如果服务器不知道页面服务window或document在页面服务期间,建议将代码移动到
解决方案1:
componentDidMount()
或者,解决方案 2
如果这是您只想执行的操作,那么您可以编写如下内容:
componentWillMount() {
    if (typeof window !== 'undefined') {
        console.log('window.innerHeight', window.innerHeight);
    }
}
我必须从 URL 访问哈希,所以我想出了这个
const hash = global.window && window.location.hash;
这是我所做的一个易于使用的解决方法。
const runOnClient = (func: () => any) => {
  if (typeof window !== "undefined") {
    if (window.document.readyState == "loading") {
      window.addEventListener("load", func);
    } else {
      func();
    }
  }
};
用法:
runOnClient(() => {
// access window as you like
})
// or async
runOnClient(async () => {
// remember to catch errors that might be raised in promises, and use the `await` keyword wherever needed
})
这比仅仅更好typeof window !== "undefined",因为如果您只是检查窗口是否未定义,那么如果您的页面被重定向到,它就不会工作,它只会在加载时工作一次。但即使页面被重定向到,此解决方法也有效,而不仅仅是在加载时一次。
刷新页面时我遇到了同样的问题(由于导入不能很好地与 SSR 配合使用)。
对我来说解决这个问题的方法是转到发生这种情况的页面并强制导入动态:
import dynamic from 'next/dynamic';
const SomeComponent = dynamic(()=>{return import('../Components/SomeComponent')}, {ssr: false});
//import SomeComponent from '../Components/SomeComponent'
注释掉原始导入并动态导入组件会强制在客户端呈现组件。
Nextjs 的文档中介绍了动态导入: https ://nextjs.org/docs/advanced-features/dynamic-import
我通过观看 YouTube 视频找到了这个解决方案: https://www.youtube.com/watch ?v=DA0ie1RPP6g
我将通用解决方案 ( if (typeof window === 'undefined') return;) 包装在自定义挂钩中,对此我非常满意。它有一个类似的反应useMemo钩子界面,我非常喜欢。
import { useEffect, useMemo, useState } from "react";
const InitialState = Symbol("initial");
/**
 *
 * @param clientFactory Factory function similiar to `useMemo`. However, this function is only ever called on the client and will transform any returned promises into their resolved values.
 * @param deps Factory function dependencies, just like in `useMemo`.
 * @param serverFactory Factory function that may be called server side. Unlike the `clientFactory` function a resulting `Promise` will not be resolved, and will continue to be returned while the `clientFactory` is pending.
 */
export function useClientSideMemo<T = any, K = T>(
  clientFactory: () => T | Promise<T>,
  deps: Parameters<typeof useMemo>["1"],
  serverFactory?: () => K
) {
  const [memoized, setMemoized] = useState<T | typeof InitialState>(
    InitialState
  );
  useEffect(() => {
    (async () => {
      setMemoized(await clientFactory());
    })();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, deps);
  return typeof window === "undefined" || memoized === InitialState
    ? serverFactory?.()
    : memoized;
}
使用示例:
我使用它动态导入与next.js 中的SSR不兼容的库,因为它自己的动态导入仅与组件兼容。
  const renderer = useClientSideMemo(
    async () =>
      (await import("@/components/table/renderers/HighlightTextRenderer"))
        .HighlightTextRendererAlias,
    [],
    () => "text"
  );
正如您所看到的,我什至实现了后备工厂回调,因此您也可以在最初在服务器上渲染时提供结果。在所有其他方面,这个钩子的行为应该类似于反应useMemo钩子。开放反馈。
| 归档时间: | 
 | 
| 查看次数: | 12868 次 | 
| 最近记录: |