react-use 部分源码分析(上)

简介: 2019 年写的分析,慎重观看。不过其中的思路还是值得学习的。

前言



2019 年写的分析,慎重观看。不过其中的思路还是值得学习的。


useBattery




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



  1. 监听window的online和offline事件。
  2. 如果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



  1. 监听 window的 ['mousemove', 'mousedown', 'resize', 'keydown', 'touchstart', 'wheel'] 这些事件。 如果发生即为活跃。
  2. 监听document的onVisibility事件,如果是当前窗体激活,设置为活跃。
  3. 启动计时器,如果活跃之后一段时间没有相关操作,即为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);
  };
复制代码



相关文章
|
存储 前端开发 JavaScript
新手也可以读懂的 React18 源码分析
打造全网最简单,新手也可以看懂的 React 18 源码分析系列。共同学习 React 设计思想,提升编码能力,轻松应对前端面试
292 0
新手也可以读懂的 React18 源码分析
|
前端开发
React的渲染"0"问题及源码分析
React的渲染"0"问题及源码分析
React的渲染"0"问题及源码分析
|
前端开发 JavaScript 算法
React躬行记(16)——React源码分析
React可大致分为三部分:Core、Reconciler和Renderer,在阅读源码之前,首先需要搭建测试环境,为了方便起见,本文直接采用了网友搭建好的环境,React版本是16.8.6,与最新版本很接近。
React躬行记(16)——React源码分析
react-use 部分源码分析(下)
2019 年写的分析,慎重观看。不过其中的思路还是值得学习的。
580 0
|
缓存 前端开发 API
react hooks源码分析:useState
阅读react hooks源码了解工作原理,开阔思维,提升架构能力。
1167 0
|
前端开发 JavaScript
React源码分析3 — React生命周期详解
React源码系列文章,请多支持: [React源码分析1 — 组件和对象的创建(createClass,createElement)](https://www.atatech.org/articles/72905) [React源码分析2 — React组件插入DOM流程](http://www.
4587 0
|
Web App开发 前端开发 JavaScript
源码分析-react3-创建dom
React.createElement class Welcome extends React.Component { constructor(){ super() this.state={ test:1} } render() { return Hello, {this.
861 0
源码分析-react2-根节点渲染
//FiberNode{ alternate : '通过该属性和后面的切片进行比较', child : '改切片的子切片', firstEffect : '当前要加入的切片', stateNode : '当前切片的基本信息' }   // Fiber对象 ...
783 0