[英]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-fit
或auto-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.