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

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

useStartTyping



document注册keydown事件,检查事件触发时activeElement是不是input和textarea以及是不是有contenteditable属性,以及检查keyCode的值是不是在有效范围,排除ctl alt等等。


const isFocusedElementEditable = () => {
  const { activeElement, body } = document;
  if (!activeElement) {
    return false;
  }
  // If not element has focus, we assume it is not editable, too.
  if (activeElement === body) {
    return false;
  }
  // Assume <input> and <textarea> elements are editable.
  switch (activeElement.tagName) {
    case 'INPUT':
    case 'TEXTAREA':
      return true;
  }
  // Check if any other focused element id editable.
  return activeElement.hasAttribute('contenteditable');
};
const isTypedCharGood = ({ keyCode, metaKey, ctrlKey, altKey }: KeyboardEvent) => {
  if (metaKey || ctrlKey || altKey) {
    return false;
  }
  // 0...9
  if (keyCode >= 48 && keyCode <= 57) {
    return true;
  }
  // a...z
  if (keyCode >= 65 && keyCode <= 90) {
    return true;
  }
  // All other keys.
  return false;
};
const useStartTyping = (onStartTyping: (event: KeyboardEvent) => void) => {
  useLayoutEffect(() => {
    const keydown = event => {
      !isFocusedElementEditable() && isTypedCharGood(event) && onStartTyping(event);
    };
    document.addEventListener('keydown', keydown);
    return () => {
      document.removeEventListener('keydown', keydown);
    };
  }, []);
};
复制代码


useMeasure



  1. ResizeObserver 采用了 resize-observer-polyfill, 高阶组件react-virtualized-auto-sizer 也能做到类似功能监听功能
  2. 利用函数ref属性
  3. useState使用函数参数初始化ResizeObserver实例


const useMeasure = <T>(): [(instance: T) => void, ContentRect] => {
  const [rect, set] = useState({
    width: 0,
    height: 0,
    top: 0,
    left: 0,
    bottom: 0,
    right: 0,
  });
  const [observer] = useState(
    () =>
      new ResizeObserver(entries => {
        const entry = entries[0];
        if (entry) {
          set(entry.contentRect);
        }
      })
  );
  const ref = useCallback(
    node => {
      observer.disconnect();
      if (node) {
        observer.observe(node);
      }
    },
    [observer]
  );
  return [ref, rect];
};
export default useMeasure;
复制代码


useClickAway



HTML Nodecontains方法, 判断节点是否为该节点的后代节点。


const useClickAway = (
  ref: RefObject<HTMLElement | null>,
  onClickAway: (event: KeyboardEvent) => void,
  events: string[] = defaultEvents
) => {
  const savedCallback = useRef(onClickAway);
  useEffect(() => {
    savedCallback.current = onClickAway;
  }, [onClickAway]);
  useEffect(() => {
    const handler = event => {
      const { current: el } = ref;
      el && !el.contains(event.target) && savedCallback.current(event);
    };
    for (const eventName of events) {
      on(document, eventName, handler);
    }
    return () => {
      for (const eventName of events) {
        off(document, eventName, handler);
      }
    };
  }, [events, ref]);
};
复制代码


useCss



借用了 CSS-in-JS library nano-css

nano-css本身使用的是 CSSStyleSheet.insertRule


const useCss = (css: object): string => {
  const className = useMemo(() => 'react-use-css-' + (counter++).toString(36), []);
  const sheet = useMemo(() => new nano.VSheet(), []);
  useLayoutEffect(() => {
    const tree = {};
    cssToTree(tree, css, '.' + className, '');
    sheet.diff(tree);
    return () => {
      sheet.diff({});
    };
  });
  return className;
};
复制代码


useDrop



监听document的dragover,dragenter,dragleave,dragexit,drop,paste等事件。


useDropArea



传入事件监听函数,返回bound各种方法以及是否hover属性。 boud方法需要挂载到节点,那么突破口就是bound。


通过bound注入事件,然后在事件里面拦截,再调用传入的监听函数。


看看调用


import {useDropArea} from 'react-use';
const Demo = () => {
  const [bond, state] = useDropArea({
    onFiles: files => console.log('files', files),
    onUri: uri => console.log('uri', uri),
    onText: text => console.log('text', text),
  });
  return (
    <div {...bond}>
      Drop something here.
    </div>
  );
};
复制代码


useFullscreen



引入了库 screenfull 。 基本就是request全屏然后,监听change事件。

当然对video进行了额外的处理。


const onWebkitEndFullscreen = () => {
      video!.current!.removeEventListener('webkitendfullscreen', onWebkitEndFullscreen);
      onClose();
    };
    const onChange = () => {
      if (screenfull) {
        const isScreenfullFullscreen = screenfull.isFullscreen;
        setIsFullscreen(isScreenfullFullscreen);
        if (!isScreenfullFullscreen) {
          onClose();
        }
      }
    };
    if (screenfull && screenfull.enabled) {
      try {
        screenfull.request(ref.current);
        setIsFullscreen(true);
      } catch (error) {
        onClose(error);
        setIsFullscreen(false);
      }
      screenfull.on('change', onChange);
    } else if (video && video.current && video.current.webkitEnterFullscreen) {
      video.current.webkitEnterFullscreen();
      video.current.addEventListener('webkitendfullscreen', onWebkitEndFullscreen);
      setIsFullscreen(true);
    } else {
      onClose();
      setIsFullscreen(false);
    }
复制代码


useSpeech



这个功能我以前不知道的说。

浏览器支持率居然将近 90%, Can i use |speechSynthesis

其实就用利用了window.speechSynthesis。 原生代码也就是


var utterance = new SpeechSynthesisUtterance('我们都是中国人'); 
window.speechSynthesis.speak(utterance)
复制代码


useWait



额,直接用的react-wait


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