美文网首页
[React Hooks] 样例学习---useEventLis

[React Hooks] 样例学习---useEventLis

作者: 小黄人get徐先生 | 来源:发表于2019-08-11 19:26 被阅读0次

使用事件监听

如果你发现自己使用useEffect添加了大量的事件监听,你也许应该考虑移动这些逻辑到一个自定义hook。下面这个样例,我们创建了一个useEventListenerhook来处理检查addEventListener方法是否支持,如果支持的话就添加事件监听,并且在清除的时候移除监听。

import React, { useState, useRef, useEffect, useCallback } from 'react';

// Usage
function App() {
  // State 用来存储鼠标的坐标
  const [coords, setCoords] = useState({ x: 0, y:0 });

  // 使用 useCallback 的事件处理程序,这样引用就不会更改
  const handler = useCallback(
    ({ clientX, clientY }) => {
      // 更新坐标
      setCoords({ x: clientX, y: clientY });
    };
  );

  // 使用我们自己的 hook 添加事件监听
  useEventListener('mousemove', handler);

  return (
    <h1>
      The mouse position is ({coords.x}, {coords.y})
    </h1>
  );
}

// 下面使我们编写的自定义 hook
function useEventListener(eventName, handler, element = window) {
  // 创建一个 ref 来存储处理程序
  const saveHandler = useRef();

  // 如果 handler 变化了,就更新 ref.current 的值。
  // 这个让我们下面的 effect 永远获取到最新的 handler
  useEffect(() => {
    saveHandler.current = handler;
  }, [handler]);

  useEffect(
    () => {
      // 确保元素支持 addEventListener
      const isSupported = element && element.addEventListener;
      if (!isSupported) return;

      // 创建事件监听调用存储在 ref 的处理方法
      const eventListener = event => saveHandler.current(event);

      // 添加事件监听
      element.addEventListener(eventName, eventListener);

      // 清除的时候移除事件监听
      return () => {
        element.removeEventListener(eventName, eventListener);
      };
    },
    [eventName, element] // 如果 eventName 或 element 变化,就再次运行
  ); 
};

export default App;

这里使用 useCallback 的原因是防止引用发生变更,如果使用普通的函数声明方式,每次该函数组件再次执行时就会重新声明函数,导致函数引用发生变化,自定义hook里监听 handler 的 useEffect 方法就会重复执行(因为声明函数的引用发生变化)。

const handler = useCallback(
  ({ clientX, clientY }) => {
  // Update coordinates
  setCoords({ x: clientX, y: clientY });
  }
);

useEffect(() => {
  savedHandler.current = handler;
  }, [handler]);

useRef 类似于 class 的实例变量,这里的 savaHandler 主要用来存储最新的 handler

const savedHandler = useRef();

useEffect(() => {
  savedHandler.current = handler;
}, [handler]);

初次加载代码的执行流程:

  1. const [coords, setCoords] = useState({ x: 0, y: 0 });
  2. const handler = useCallback(...
  3. useEventListener('mousemove', handler); // 进入到自定义 hook
  4. const savedHandler = useRef(); // 到这里App 和 useEventListener 的同步代码都加载完了(App里面没有 useEffect),下面使用异步的方式按顺序加载 useEventListener 的两个 useEffect
  5. 同步代码加载完后,异步代码进入加载序列,在异步加载之前,完成 return 操作。此时 coords 为 {x:0,y:0}
return (
  <h1>
    The mouse position is ({coords.x}, {coords.y})
  </h1>
); 
  1. 执行第一个 useEffect。目前 useEffect 闭包函数里面的 handler 变量为 undefined。传入的 handler 为上面声明的方法。两者不相等,所以这里执行内部代码,执行完毕后,saveHandler.curent 就存储了最新的 handler 方法。
 useEffect(() => {
        savedHandler.current = handler;
    }, [handler]);
  1. 下面执行第二个 useEffect。eventName 和 element 都发生了变化,由 undefined 到对应的赋值,所以执行内部的逻辑代码,为 element 上的 eventName 事件绑定对应的 handler。
  2. 以上步骤完成初始化流程。

mousemove 事件监听阶段:

  1. 完成初始化步骤后,成功为 window 上的 mousemove 事件绑定对应的 handler。现在每当鼠标移动,就会触发对应的 hanlder。
  2. 该方法会调用 setCoords 设置 coords 的值。当值发生变化后,App 会重新执行整个逻辑流程。
const handler = useCallback(
  ({ clientX, clientY }) => {
    // Update coordinates
    setCoords({ x: clientX, y: clientY });
  }
);
  1. const [coords, setCoords] = useState({ x: 0, y: 0 }); // 再次执行的时候有些 hook 方法忽略执行。(这里是我猜测的,因为再次执行整的流程的时候,对应的值不可能对重新初始化)。const handler = useCallback(... 同。
  2. useEventListener('mousemove', handler); // 进入自定义 hook
  3. const savedHandler = useRef(); // 不执行
  4. 两个 useEffect 异步执行。所以这里优先执行 return 语句,此时 coords 的值已经发生改变,return 后页面会发生改变。
return (
    <h1>
        The mouse position is ({coords.x}, {coords.y})
    </h1>
);
  1. 执行两个 useEffect,他们的 deps 的值都没有发生改变,所以两个 useEffect 都不执行。
  2. 监听流程代码的执行顺序如上。

相关文章

  • [React Hooks] 样例学习---useEventLis

    使用事件监听 如果你发现自己使用useEffect添加了大量的事件监听,你也许应该考虑移动这些逻辑到一个自定义ho...

  • [React Hooks] 样例学习---useWhyDidYo

    前置知识 React.memo React.memo 是一个高阶组件。类似于 React.PureComponen...

  • react-hooks

    前置 学习面试视频 总结react hooks react-hooks react-hooks为函数组件提供了一些...

  • 十个案例学会 React Hooks

    在学会使用React Hooks之前,可以先看一下相关原理学习React Hooks 前言 在 React 的世界...

  • React Hooks

    React Hooks Hooks其实就是有状态的函数式组件。 React Hooks让React的成本降低了很多...

  • React Hooks

    前言 React Conf 2018 上 React 提出了关于 React Hooks 的提案,Hooks 作为...

  • 5分钟简单了解React-Hooks

    首先附上官网正文?:React Hooks Hooks are a new addition in React 1...

  • react-hooks

    react-hooks react-hooks 是react16.8以后,react新增的钩子API,目的是增加代...

  • React-hooks API介绍

    react-hooks HOOKS hooks概念在React Conf 2018被提出来,并将在未来的版本中被...

  • React Hooks 入门

    React Hooks 是 React v16.8 版本引入了全新的 API。 React Hooks 基本概念 ...

网友评论

      本文标题:[React Hooks] 样例学习---useEventLis

      本文链接:https://www.haomeiwen.com/subject/jzpsjctx.html