简体   繁体   中英

How to set window resize event listener value to React State?

This issue is very simple but I probably overlook very little point. Window screen size is listening by PostLayout component. When window width is less than 768px, I expect that isDesktopSize is false. I tried everything like using arrow function in setIsDesktopSize , using text inside of true or false for state value, using callback method etc... but it's not working.

PostLayout shared below:

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>
        </>  
    )
}

console log is shared below:

Desktop: true
627

This could probably be extracted into a custom hook. There's a few things you'd want to address:

  1. Right now you default the state to true , but when the component loads, that may not be correct. This is probably why you see an incorrect console log on the first execution of the effect. Calculating the initial state to be accurate could save you some jank/double rendering.
  2. You aren't disconnecting the resize listener when the component unmounts, which could result in an error attempting to set state on the component after it has unmounted.

Here's an example of a custom hook that addresses those:

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

Then to use this, your other component would look like this (assuming your custom hook is just in this same file -- though it may be useful to extract it to a separate file and import it):

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>
        </>  
    )
}

EDIT: I modified this slightly so it should theoretically work with a server-side renderer, which will assume a desktop size.

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) } }

I didn't find such a package on npm and I thought it would be nice to create one: https://www.npmjs.com/package/use-device-detect . I think it will help someone :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM