前言
2019 年写的分析,慎重观看。不过其中的思路还是值得学习的。
useBattery
- 作用:获取电池信息
- 原理 navigator.getBattery, 并监听chargingchange, levelchange,chargingtimechange,dischargingtimechange事件。
useGeolocation
- 获取地址位置
- Geolocation, navigator.geolocation.getCurrentPosition获取地址位置, navigator.geolocation.watchPosition监听变化。
useLocation
- 作用:获取浏览器location信息
- 原理: 监听window的popstate,pushstate,replacestate事件。
useEffect(() => { const onPopstate = () => setState(buildState('popstate')); const onPushstate = () => setState(buildState('pushstate')); const onReplacestate = () => setState(buildState('replacestate')); on(window, 'popstate', onPopstate); on(window, 'pushstate', onPushstate); on(window, 'replacestate', onReplacestate); return () => { off(window, 'popstate', onPopstate); off(window, 'pushstate', onPushstate); off(window, 'replacestate', onReplacestate); }; }, []); 复制代码
useMedia
使用window.matchMedia获得MediaQueryList 对象,然后添加监听事件。
import { useEffect, useState } from 'react'; import { isClient } from './util'; const useMedia = (query: string, defaultState: boolean = false) => { const [state, setState] = useState(isClient ? () => window.matchMedia(query).matches : defaultState); useEffect(() => { let mounted = true; const mql = window.matchMedia(query); const onChange = () => { if (!mounted) { return; } setState(!!mql.matches); }; mql.addListener(onChange); setState(mql.matches); return () => { mounted = false; mql.removeListener(onChange); }; }, [query]); return state; }; export default useMedia; 复制代码
useNetwork
- 监听window的online和offline事件。
- 如果navigator支持connection,那么再监听change事件。
on(window, 'online', onOnline); on(window, 'offline', onOffline); if (connection) { on(connection, 'change', onConnectionChange); localSetState({ ...state, online: navigator.onLine, since: undefined, ...getConnectionState(), }); } return () => { off(window, 'online', onOnline); off(window, 'offline', onOffline); if (connection) { off(connection, 'change', onConnectionChange); } }; 复制代码
usePageLeave
document监听mouseout事件,判断event.relatedTarget || event.toElement 是不是documentElement。DOM通过event对象的relatedTarget属性提供了相关元素的信息。这个属性只对于mouseover和mouseout事件才包含值;对于其他事件,这个属性的值是null。 IE不支持realtedTarget属性,但提供了保存着同样信息的不同属性。在mouseover事件触发时,IE的fromElement属性中保存了相关元素;在mouseout事件触发时,IE的toElement属性中保存着相关元素。
const usePageLeave = (onPageLeave, args = []) => { useEffect(() => { if (!onPageLeave) { return; } const handler = event => { event = event ? event : (window.event as any); const from = event.relatedTarget || event.toElement; if (!from || (from as any).nodeName === 'HTML') { onPageLeave(); } }; document.addEventListener('mouseout', handler); return () => { document.removeEventListener('mouseout', handler); }; }, args); }; 复制代码
useIdle
- 监听 window的 ['mousemove', 'mousedown', 'resize', 'keydown', 'touchstart', 'wheel'] 这些事件。 如果发生即为活跃。
- 监听document的onVisibility事件,如果是当前窗体激活,设置为活跃。
- 启动计时器,如果活跃之后一段时间没有相关操作,即为idle。
const defaultEvents = ['mousemove', 'mousedown', 'resize', 'keydown', 'touchstart', 'wheel']; const oneMinute = 60e3; const useIdle = (ms: number = oneMinute, initialState: boolean = false, events: string[] = defaultEvents): boolean => { const [state, setState] = useState<boolean>(initialState); useEffect(() => { let mounted = true; let timeout: any; let localState: boolean = state; const set = (newState: boolean) => { if (mounted) { localState = newState; setState(newState); } }; const onEvent = throttle(50, () => { if (localState) { set(false); } clearTimeout(timeout); timeout = setTimeout(() => set(true), ms); }); const onVisibility = () => { if (!document.hidden) { onEvent(); } }; for (let i = 0; i < events.length; i++) { on(window, events[i], onEvent); } on(document, 'visibilitychange', onVisibility); timeout = setTimeout(() => set(true), ms); return () => { mounted = false; for (let i = 0; i < events.length; i++) { off(window, events[i], onEvent); } off(document, 'visibilitychange', onVisibility); }; }, [ms, events]); return state; }; 复制代码
useScrolling
计时器来实现,滚动事件后150ms设置为不滚动,如期间再滚,清除计时器,重新开启计时器。
const handleScrollEnd = () => { setScrolling(false); }; const handleScroll = () => { setScrolling(true); clearTimeout(scrollingTimeout); scrollingTimeout = setTimeout(() => handleScrollEnd(), 150); }; ref.current.addEventListener('scroll', handleScroll, false); return () => { if (ref.current) { ref.current.removeEventListener('scroll', handleScroll, false); } }; } 复制代码
useSize
采取的内嵌iframe的方式,更多监听方式参考:xiangwenhu.github.io/TakeItEasy/…
const ref = useRef<HTMLIFrameElement | null>(null); let window: Window | null = null; const setSize = () => { const iframe = ref.current; const size = iframe ? { width: iframe.offsetWidth, height: iframe.offsetHeight, } : { width, height }; setState(size); }; const onWindow = (windowToListenOn: Window) => { windowToListenOn.addEventListener('resize', setSize); DRAF(setSize); }; 复制代码