レスポンシブ時の View の変化は基本的に CSS のメディアクエリで行いたいが、 window サイズが大きくなったときにモバイル用のモーダルメニューを閉じるとか、意外とレスポンシブ時に JS でイベントを実行したい時がある
window.resize
を使った今までやりかた
const breakpoint = 768 as const; export const App = () => { const timerRef = useRef<number>(0); const [isSp, setIsSp] = useState<boolean>(true); useEffect(() => { const timer = timerRef.current; const resizeEvent = () => { window.clearTimeout(timer); window.setTimeout(() => { if (window.innerWidth >= breakpoint) { setIsSp(false); } else { setIsSp(true); } }, 100); }; // レンダリング時に window サイズをチェックするために呼び出す resizeEvent(); // add addEventListener window.addEventListener("resize", resizeEvent); return () => { window.clearTimeout(timer); window.removeEventListener("resize", resizeEvent); }; }, []); return (<div>{isSp? "true" : "false"}</div>) }
今までは window.resize
イベントを監視して特定の window サイズになった際にイベントを発火させていたがイベントを間引く debounde の処理などいろいろと面倒な部分があった
また、debounce させていても window がリサイズされる事にイベントが呼び出されるので処理が多くなってしまう
MediaQueryListAPI matchMedia
を使った方法
const breakpoint = 768 as const; export const App = () => { const [isSp, setIsSp] = useState<boolean>(true); useEffect(() => { const mediaQueryList = window.matchMedia(`(min-width: ${breakpoint}px)`); const handleOrientationChange = ( evt: MediaQueryList | MediaQueryListEvent ) => { if (evt.matches) { setIsSp(false); } else { setIsSp(true); } }; // レンダリング時に window サイズをチェックするために呼び出す handleOrientationChange(mediaQueryList); // add addEventListener mediaQueryList.addEventListener("change", handleOrientationChange); return () => { mediaQueryList.removeEventListener("change", handleOrientationChange); }; }, []); return (<div>{isSp? "true" : "false"}</div>) }
MediaQueryListAPI を使った方法だと設定したブレイクポイントを跨いだタイミングでだけ change イベントが発火される
リスナー関数の引数は初回呼び出し時は MediaQueryList
で change イベントから受け取る場合は MediaQueryListEvent
になるようなので evt: MediaQueryList | MediaQueryListEvent
とした。もっと良い指定方法があるかもしれない。
Sample
次のサンプルでは breakpoint
を跨ぐと各エリアのデザインが変更され、発火されたイベントの回数を表示している。
matchMedia
を使った方は必要なときだけイベントが発火しているのが分かると思う
[参考]
- MediaQueryList - Web APIs | MDN
- MediaQueryList | typescript - v3.7.7
- JSのレスポンシブ対応をresizeからmatchMediaに移行した
- Debounce と Throttle(JavaScript)