如何将窗口调整大小事件监听器的值设置为React状态?

3
这个问题很简单,但我可能忽略了一些小细节。窗口屏幕大小由PostLayout组件监听。当窗口宽度小于768px时,我期望isDesktopSize为false。我尝试了一切方法,比如在setIsDesktopSize中使用箭头函数、使用true或false文本作为状态值、使用回调函数等等……但这些方法都不起作用。
下面是PostLayout的共享代码:
import React, {useState,useEffect, useCallback} from 'react'
import LeftSideNavbar from './LeftSideNavbar'
import TopNavbar from './TopNavbar'

export default function PostLayout({children}) {
    const [isDesktopSize, setIsDesktopSize] = useState(true)

    let autoResize = () => {
        console.log("Desktop: " + isDesktopSize);
        console.log(window.innerWidth);
        if(window.innerWidth < 768 ){
            setIsDesktopSize(false)
        }else{
            setIsDesktopSize(true)
        }
    }

    useEffect(() => {
        window.addEventListener('resize', autoResize)
        autoResize();  
    }, [])

    return (
        <>
            <TopNavbar isDesktopSize={isDesktopSize}/>
            <main>
                <LeftSideNavbar/>
                {children}
            </main>
        </>  
    )
}

以下是控制台日志:

Desktop: true
627
3个回答

1

这可能可以被提取为一个自定义的钩子。你需要解决以下几个问题:

  1. 现在你默认将状态设置为true,但是当组件加载时,这可能是不正确的。这可能是为什么你在第一次执行effect时看到了错误的console log。计算初始状态以确保准确性可以避免一些瑕疵/双重渲染。
  2. 当组件卸载时,你没有断开调整大小监听器,这可能会导致在组件卸载后尝试设置状态时出错。

这是一个解决以上问题的自定义钩子示例:

function testIsDesktop() {
    if (typeof window === 'undefined') {
        return true;
    }
    return window.innerWidth >= 768;
}

function useIsDesktopSize() {
    // Initialize the desktop size to an accurate value on initial state set
    const [isDesktopSize, setIsDesktopSize] = useState(testIsDesktop);

    useEffect(() => {
        if (typeof window === 'undefined') {
            return;
        }

        function autoResize() {
            setIsDesktopSize(testIsDesktop());
        }

        window.addEventListener('resize', autoResize);

        // This is likely unnecessary, as the initial state should capture
        // the size, however if a resize occurs between initial state set by
        // React and before the event listener is attached, this
        // will just make sure it captures that.
        autoResize();

        // Return a function to disconnect the event listener
        return () => window.removeEventListener('resize', autoResize);
    }, [])

    return isDesktopSize;
}

然后使用它,您的其他组件将如下所示(假设您的自定义 hook 就在同一文件中 - 尽管将其提取到单独的文件并导入可能会很有用):

import React, { useState } from 'react'
import LeftSideNavbar from './LeftSideNavbar'
import TopNavbar from './TopNavbar'

export default function PostLayout({children}) {
    const isDesktopSize = useIsDesktopSize();

    return (
        <>
            <TopNavbar isDesktopSize={isDesktopSize}/>
            <main>
                <LeftSideNavbar/>
                {children}
            </main>
        </>  
    )
}

编辑:我稍微修改了一下,这样它理论上可以在服务器端呈现器上运行,假设是桌面尺寸。


感谢您的回复,Marc Baumbach。当我尝试您的建议时,我不断遇到“window未定义”的错误。我研究了这个错误。这个链接可能是解决方案,但对我没有用。实际上,GoonGamja的问题和我一样。最终,我无法通过这个编译错误。 - swim
@swim 这段代码是否也在服务器端渲染器上运行?这段代码假定它只在浏览器上运行。window 在浏览器中始终可用。 - Marc Baumbach
我修改了代码以检查window是否被定义。如果没有,它基本上不运行并假定它是桌面尺寸,尽管可能没有明确正确的解决方案。 - Marc Baumbach
桌面尺寸值现在已经正确设置 :) 这个问题的目的是使用 isDesktopSize 值添加响应属性。当 "isDesktopSize" 值为 false 时,我的 topnavbar 组件中的一些元素应该消失。我捕获到一个警告,如 "className did not match . Server: TopNavbar d-flex",客户端:"TopNavbar d-none"。因此,应用程序从服务器端做出决策。这些问题包括窗口、由于 SSR 应用程序而导致的不匹配的类属性,我可以迁移纯 React 项目或客户端项目。 - swim
我了解到非服务器端动态导入。这种导入方式是解决我不匹配的类属性问题的解决方案。由于不再从服务器端导入TopNavbar组件,因此类属性的初始值设定正确。 - swim

0

0

Try this, you are setting isDesktopSizze to 'mobile', which is === true

const [isDesktopSize, setIsDesktopSize] = useState(true)

let autoResize = () => {
        console.log("Desktop: " + isDesktopSize);
        console.log(window.innerWidth);
        if(window.innerWidth < 768 ){
            setIsDesktopSize(true)
        }else{
            setIsDesktopSize(false)
        }
    }


谢谢您的快速回复,但我已经更新了我的问题并更改了这些行。您可以删除答案,但还是非常感谢您的帮助。 - swim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接