簡體   English   中英

根據屏幕寬度在 UI 中使用 React 更改元素行為

[英]Using React change element behavior in UI based on screen width

我會寫一些關於我的項目的信息。

框架: NextJs,低於版本 13 UI 框架: ChakraUI(使用預定義組件,主要是 Flex)

想法如下:我已經測試並確定了哪些屏幕尺寸應該需要 UI 上的元素行為更改,並決定編寫代碼來促進該功能。

做這樣的事情,我將避免為每個頁面/組件編寫 @media 邏輯,因為我已經知道屏幕尺寸會發生變化。

這很含糊,所以我將添加一些上下文:

    if (typeof window === "undefined") return 6;
    const windowWidth: number = window.innerWidth;

    if (windowWidth <= 600) return 2;
    else if (windowWidth <= 900) return 3;
    else if (windowWidth <= 1200) return 4;
    else if (windowWidth <= 1400) return 5;

    return 6;

如您所見,基本上我已經決定對於某些斷點寬度,將返回一個數字。 使用它我可以影響,例如,在屏幕上一行顯示多少項目,或者元素的寬度應該是多少等。

顯示一行中應包含多少項的用法示例:

width={ ${Math.floor(100 / displayItemsCount)}% }

或者這里是一個影響項目寬度的例子:

寬度={displayItemsCount <= 2? '90%':displayItemsCount <= 4? '70%': '50%'}

注意:為避免混淆, displayItemsCount是上述代碼塊返回的值。

免責聲明:由於像我想象的那樣做事情導致我做了很多測試、研究並給我帶來了問題,所以我也願意接受以下類型的建議: “這種想法是錯誤的,更好的解決方案是。 ..”。

我知道這里有十億個關於這個問題的答案,但我認為它們並沒有涵蓋我在這里需要的一切。 一些代碼也來自我的 SO 研究。

我將在這里寫一些基礎工作,以便清楚當前使用的是什么。

首先,我想要一個 debounce present,這樣代碼就不會在每次像素變化時都被調用。 這主要用於用戶在使用檢查元素工具調整屏幕大小時。

function debounce(fn: Function, ms: number) {
    let timeoutId: ReturnType<typeof setTimeout>
    return function (this: any, ...args: any[]) {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(() => fn.apply(this, args), ms);
    }
};

function determineDisplayItemsCount() {
    if (typeof window === "undefined") return 6;
    const windowWidth: number = window.innerWidth;

    if (windowWidth <= 600) return 2;
    else if (windowWidth <= 900) return 3;
    else if (windowWidth <= 1200) return 4;
    else if (windowWidth <= 1400) return 5;

    return 6;
};

Debounce會在調整屏幕大小后經過一段時間后調用代碼,而determineDisplayItemsCount會根據當前屏幕大小返回一個值。

忽略determineDisplayItemsCount的命名,這是一些舊用例的殘余,因此將對其進行更改。

我想要的第二件事是,這段代碼出現在一個鈎子中,這樣就很容易使用。

function useDisplayItemsCount() {

    const [displayItemsCount, setDisplayItemsCount] = useState<number>(determineDisplayItemsCount());

    useEffect(() => {
        const debounceHandleResize = debounce(
            function handleResize() {
                let count: number = determineDisplayItemsCount();
                if (count != displayItemsCount)
                    setDisplayItemsCount(count);
            }, 250);
        window.addEventListener('resize', debounceHandleResize)

        return () => {
            window.removeEventListener('resize', debounceHandleResize)
        }
    }, []);
    
    return displayItemsCount;
}

如您所見,在useEffect中,只要屏幕改變寬度,就會調用handleResize ,但會在額外的 250 毫秒過去之后。 我還添加了如果新計數與當前displayItemsCount相同,則 state 不會改變。

結果是我現在可以 go 進入任何頁面/組件和 go 類似: const displayItemsCount = useDisplayItemsCount(); . 然后將該值用於我需要的任何東西。

問題

  • 首要問題

調用 useDisplayItemsCount時, displayItemsCount的初始值將根據determineDisplayItemsCount function 設置。這會導致水合作用錯誤,因為我必須使用window並且組件尚未安裝。 我的想法是,如果我在determineDisplayItemsCount中有以下行: if (typeof window === "undefined") return 6; ,我會避免水合作用錯誤,但不,它仍然存在。

  • 第二期

所以我決定見鬼,讓我們讓它的初始值為 6: const [displayItemsCount, setDisplayItemsCount] = useState<number>(6); ,但這會導致當頁面從手機(即較小的屏幕)加載時, useDisplayItemsCount在開始時返回 6。 鈎子中的useEffect僅在用戶在頁面上滾動一點或與某些東西交互后才會被調用。

我想我的問題是,如何使這段代碼正常工作,而不會導致水合作用錯誤?

如前所述,一個很好的答案也是一種替代方法。

注意:可能值得一提的是,使用 Grid 並不能真正解決我的問題,因為我想避免在所有情況下都寫 @media 。

使用 CSS 網格,您實際上不需要使用單個媒體查詢來創建完全響應的網格列。 使用minmax function,通過auto-fitauto-fill ,您可以為網格項目設置最小和最大尺寸。 列數將根據可用空間自動調整以適應或填充。

運行下面的代碼片段,然后使用全屏鏈接看看我的意思。

 /* With CSS Grid, you can create a fully responsive grid in two declarations on the parent. */.container { display: grid; grid-gap: .75rem; grid-template-columns: repeat(auto-fit, minmax(175px, 1fr)); }.item { aspect-ratio: 1; background-color: #ccc; border: 1px solid; display: grid; font: bold 2rem sans-serif; place-content: center; width: 100%; }
 <div class="container"> <div class="item">1</div> <div class="item">2</div> <div class="item">3</div> <div class="item">4</div> <div class="item">5</div> <div class="item">6</div> <div class="item">7</div> <div class="item">8</div> <div class="item">9</div> </div>

感謝@JohnLi 和@jme11 提供的信息。 您的答案組合是我的解決方案。

我現在使用 Chakra 斷點來處理組件的樣式,同時使用網格來顯示位置。

這真的很有幫助。 如果可以的話,我會接受這兩個答案!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM