useWindowSize 記錄與監視window的狀態
===

## *這不是插件!
這是我自己創的一個 customHook,因為很好用所以分享.
如有疑問歡迎分享想法讓這個hook更加精簡!
---
## 介紹:
這個 customHook 是用來監視:
- current 視窗的**寬度**和**高度**
- 是否已經Ready
- scrollY目前高度
- bottomNavbar在指定條件出現或消失(客制)
## 用途:
- 用視窗大小判斷特定JS去處理desktop web component 還是 mobile web 的component.
- 用視窗高低來計算需要整個網頁高度
- 用來記錄scrollY,在特定的條件下去進行切換UI達到華麗的UX表現.
- 用WindowReady 來判斷網頁render過程.
- (客制)bottomNavbar的表現判斷
## 程式:
```jsx
interface WindowSizeProps {
width: number;
height: number;
scrollY: number;
isWindowReady: boolean;
isAppear: boolean;
}
export const useWindowSize = () =>{
// 這裡可以做一些whitelist的事情
//例如某些page,bottomNavbar就不顯示。
const [windowSize, setWindowSize] = useState<WindowSizeProps>({
width: 0,
height: 0,
scrollY: 0,
isWindowReady: false,
isAppear: false,
});
const lastScrollLocation = useRef<number>(0);
const scrollTimeoutRef = useRef<NodeJS.Timeout | null>(null);
useEffect(()=>{
const handleScroll = () =>{
if(lastScrollLocation.current >= window.scrollY || window.scrollY <= 0 ){
// scrolling up keep display
setWindowSize((prevWindowSize)=>({
...prevWindowSize,
scrollY: window.scrollY,
isAppear: true,
}));
}else {
setWindowSize((prevWindowSize)=>({
...prevWindowSize,
scrollY:window.scrollY,
isAppear:false,
}));
}
lastScrollLocation.current = window.scrollY;
clearTimeout(scrollTimeoutRef.current!);
scrollTimeoutRef.current = setTimeout(()=>{
setWindowSize((prevWindowSize)=>({
...prevWindowSize,
isAppear:false,
}));
},200)
};
const handleSize = () =>{
if(window.visualViewport){
setWindowSize((prevWindowSize)=>({
...prevWindowSize,
width: window?.visualViewport?.width ?? 0,
height:window?.visualViewport?.height ?? 0,
scrollY: window.scrollY,
isWindowReady: true,
}));
}
};
if(typeof window !== "undefined"){
window.addEventListener("resize", handleSize);
window.addEventListener("scroll",handleScroll);
handleSize();
return () =>{
window.removeEventListener("resize", handleSize);
window.removeEventListener("scroll",handleScroll);
clearTimeout(scrollTimeoutRef.current!);
}
}
},[]);
return windowSize;
}
```
## 講解:
1. 完成定義 windowSizeProps 類型。
2. 初始化狀態,如果有什麼想監聽window的狀態,都可以往裡面加。
3. 設置引用和定時器,置於為什麼用useRef呢:
- **引用持久性**:useRef提供了一個容器,其.current屬性可用於儲存任何可變值。並且這個值在組件的所有渲染週期內保持不變。這對於跟踪不需要觸發渲染的數據來說是非常有用的。
- **避免不必要的渲染**:用useState來存儲定時器ID或非其他非狀態邏輯(如滾動位置)會在更新這些值時觸發組件的重新渲染。因為這個值的變化不需要去影響渲染,所以選用useRef在更新其current的時候才不會觸發重新渲染,對性能優化是有益的。
- **跨渲染週期的數據共享**: useRef 可以跨渲染週期保持數據,即使組件重新渲染,儲存在 useRef 中的數據也可以保持不變。
- **DOM引用**: useRef 通常用來引用組件內的DOM節點。當然不僅限於DOM引用,也適用於保持任何可變值。
4. handleScroll的第一個判斷是用來改變 isAppear 而誕生的,主要是在這裡紀錄和索取是最快的。如果 scroll up 的話,就讓套用 isAppear 的顯現出來,反則是消失。在這個專案裡我是搭配 transform:translateY來做變化。
5. 然後每次 lastScrollLocation 這個ref 來紀錄當下的scroll位置。
6. 然後就清楚定時器,這裡呢是要做如果 長期保持true,那就會自動藏起來。(optional,沒有一定要打開這個功能。)
7. 接下來呢,就定義handleScroll,很簡單就是把window的資料按照指定的名稱塞入其中就好了。
8. 功能寫好了,當然就是要運用了呀。打開 useEffect 然後判定如果window已經準備好了,那資料應該都有了吧?那就 run 吧!因為他們不是一直都需要執行的程式,所以記得 release 掉喔。
---
## 總結:
這是連我家技術長都認為是有用的Hook,因為很多時候我們無法透過CSS去控制做切換的動作,而且很多地方會用得到,我就想說來做一個CustomHook 來監聽window目前的狀態。當然用最多的是 width 和 isAppear XDD。 如果要做navbar內容切換,我也推薦scrollY 去監聽到了某個高度就切換這樣。例如:
```jsx
{scrollY > 1600 ? (
<div>....</div>
):null}
```
這樣就可以做切換了。希望這篇文章能幫到你!再見啦!