乐闻世界logo
搜索文章和话题

NextJS 如何在 SSR 环境通过react hook获取窗口的大小?

7 个月前提问
3 个月前修改
浏览次数147

3个答案

1
2
3

在 Next.js 的服务器端渲染(SSR)环境中,由于没有浏览器窗口对象,无法直接通过 React Hook 获取窗口的大小。但是,我们可以通过使用 React 的生命周期方法来在客户端(浏览器环境)中获取窗口大小,并且可以通过设置状态来使其可用于组件。

下面是使用 React Hook useStateuseEffect 来获取窗口大小的方法:

jsx
import React, { useState, useEffect } from 'react'; const useWindowSize = () => { // 初始化状态为 undefined,因为在服务器端渲染时无法确定窗口大小 const [windowSize, setWindowSize] = useState({ width: undefined, height: undefined, }); useEffect(() => { // 只有在客户端环境下才能运行这段代码 if (typeof window !== 'undefined') { // 处理窗口大小变化的函数 const handleResize = () => { setWindowSize({ width: window.innerWidth, height: window.innerHeight, }); }; // 监听窗口大小变化 window.addEventListener('resize', handleResize); // 立即调用一次,以获取初始窗口大小 handleResize(); // 清理函数,在组件卸载时移除事件监听 return () => window.removeEventListener('resize', handleResize); } }, []); // 空数组确保效果只在组件挂载和卸载时运行 return windowSize; }; // 你的组件 const MyComponent = () => { const { width, height } = useWindowSize(); return ( <div> {width && height ? ( <p>窗口大小:宽度 {width} px,高度 {height} px</p> ) : ( <p>正在加载窗口大小...</p> )} </div> ); }; export default MyComponent;

在上面的代码中,我们定义了一个自定义 Hook useWindowSize,它返回当前的窗口大小。在服务器端渲染时,windowSize 的状态会被初始化为 undefined,因为没有窗口对象。在组件挂载到 DOM 后(客户端环境),useEffect 会被调用,这时我们可以安全地访问 window 对象来设置窗口大小。当窗口大小变化时,handleResize 函数会更新 windowSize 状态。

最后,在我们的组件 MyComponent 中,我们使用 useWindowSize 来获取并展示窗口大小。如果窗口大小还未确定(即在服务器端渲染期间),它可以显示一个加载提示或者某种占位内容。

2024年6月29日 12:07 回复

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

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

您的链接中的完整示例:

shell
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 // 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; }

注意:更新为 Sergey Dubovik 评论,我们不需要验证窗口,因为 useEffect 在客户端运行

2024年6月29日 12:07 回复

由于某种原因,我遇到了错误,但这对我有用。仅适用于宽度:

shell
const useWidth = () => { const [width, setWidth] = useState(0) const handleResize = () => setWidth(window.innerWidth) useEffect(() => { handleResize() window.addEventListener('resize', handleResize) return () => window.removeEventListener('resize', handleResize) // eslint-disable-next-line react-hooks/exhaustive-deps }, []) return width }
2024年6月29日 12:07 回复

你的答案