自建轮子分享:react-khooks 一个基于React键盘事件的自定义hooks

react-khooks

这是一个基于React的键盘事件的自定义hooks,是基于公司的现有的业务需求封装的,支持一个按键绑定多个事件(事件队列管理),只触发最新绑定的事件,队列顺序根据业务去维护,毕竟正常来说一个按键在某一时刻只会触发单一功能事件。(其实可以通过事件权重去托管给emitter本身去管理)

ps:其中关于内嵌队列的设计原本想的更复杂,但实际业务没有没那复杂,就没有做更深层次的扩展。在此只是分享记录,因为没有参考任何轮子,所以可能还有很多想不到啦,如需可自取,链接在结尾

📦 Install

bash 复制代码
$ npm i react-khooks --save

🔨 Usage

jsx 复制代码
// 根据需要结构以下对应模块使用
import { useKeyEvent } from 'react-khooks'; //键盘hooks
import { emitter } from 'react-khooks'; //事件队列中心(单例)
参数 说明
keyName 键盘按键名key或keyCode,建议统一,必传(组合键使用+连接,如:ctrl+按键名,请注意和浏览器以及系统按键的冲突问题)
callback 回调函数,默认参数e为KeyboardEvent事件对象,必传
toolEventName 自定义事件名称,作为该键盘事件队列中的唯一标识,必传
type 键盘弹起或按下(keyup/keydown),默认keyup
delayTime 防抖/节流延迟时间,默认0为不使用节流/防抖 (如果使用keydown,建议设置)
delayType 1节流/2防抖,默认1
  • 一般来说,你只需要传入三个属性:keyName(键盘按键名),callback(回调函数),toolEventName(自定义事件名)
  • 支持复合键+按键和**复合键+复合键+按键的组合按键事件 (复合键是指ctrl/alt/shift)
  • 你可以基于callback的默认参数KeyboardEvent根据该事件对象进行更多逻辑控制或完成更多复合按键
  • useKeyEvent默认返回事件队列实例,这是一个单例,意味着你在任何地方返回的都是同一个emitter实例或者直接导入,以手动控制事件队列

基本用法:

jsx 复制代码
import React, { useState, useCallback } from 'react';
import { useKeyEvent } from 'react-khooks';

export default () => {
  const handleClick = () => {
    alert('按键z触发');
  };

  useKeyEvent({ keyName: 'z', callback: handleClick, toolEventName: 'alert' });

  return (
    <div>
      <div>按键z键弹出提示</div>
    </div>
  );
};

组合键(ctrl/alt/shift+按键):

  • 回调接收默认参数(KeyboardEvent 事件对象)
jsx 复制代码
import React, { useState, useCallback } from 'react';
import { useKeyEvent } from 'react-khooks';

export default () => {
  const [num, setNum] = useState(0);
  const handleClick = (e) => {
    setNum(num + 1);
  };
  useKeyEvent({ keyName: `alt+v`, callback: handleClick, toolEventName: '增加num' });
  useKeyEvent({ keyName: `ctrl+v`, callback: handleClick, toolEventName: '增加num' });
  useKeyEvent({ keyName: `shift+v`, callback: handleClick, toolEventName: '增加num' });

  return (
    <div>
      <div>按键ctrl/alt/shift+v增加num</div>
      <span>num: {num}</span>
    </div>
  );
};

动态切换绑定热键:

jsx 复制代码
import React, { useState, useCallback } from 'react';
import { useKeyEvent } from 'react-khooks';

export default () => {
  const [num, setNum] = useState(0);
  const [hotKey, setHotKey] = useState('m');
  const handleClick = () => {
    setNum(num + 1);
  };

  useKeyEvent({ keyName: hotKey, callback: handleClick, toolEventName: '修改num' });

  return (
    <div>
      <div>按键{hotKey}键修改num</div>
      <div>num: {num}</div>
      <div>
        <button onClick={() => setHotKey('n')}>切换为使用n键修改num</button>
      </div>
    </div>
  );
};

节流/防抖

jsx 复制代码
import React, { useState, useCallback } from 'react';
import { useKeyEvent } from 'react-khooks';

export default () => {
  const [num, setNum] = useState(0);
  const handleClick = useCallback(() => {
    setNum(num + 1);
  }, [num]);
  useKeyEvent({
    keyName: 'q',
    callback: handleClick,
    toolEventName: '长按q键修改num',
    delayTime: 500,
    type: 'keydown',
    delayType: 1,
  });
  useKeyEvent({
    keyName: 'w',
    callback: handleClick,
    toolEventName: '长按w键修改num',
    delayTime: 500,
    type: 'keydown',
    delayType: 2,
  });

  return (
    <div>
      <div>节流:长按q键修改num(每500ms触发一次)</div>
      <div>num: {num}</div>
      <div>防抖:长按w键修改num(直到抬起触发一次)</div>
    </div>
  );
};

关于回调函数 callback 的处理强烈建议使用 useCallback,避免组件重新渲染时频繁订阅取消

  • 使用 useCalback 内 setState 获取当前状态
jsx 复制代码
import React, { useState, useCallback } from 'react';
import { useKeyEvent } from 'react-khooks';

export default () => {
  const [num, setNum] = useState(0);

  const handleClick = useCallback(() => {
    setNum((num) => num + 1);
  }, []);

  useKeyEvent({ keyName: 'a', callback: handleClick, toolEventName: 'add' });

  return (
    <div>
      设置快捷键 a 修改 useState 定义的 num 数据
      <div>num: {num}</div>
    </div>
  );
};
  • useCallback 依赖获取当前状态
jsx 复制代码
import React, { useState, useCallback } from 'react';
import { useKeyEvent } from 'react-khooks';

export default () => {
  const [num, setNum] = useState(0);

  const handleClick = useCallback(() => {
    setNum(num - 1);
  }, [num]);

  useKeyEvent({ keyName: 's', callback: handleClick, toolEventName: 'reduce' });

  return (
    <div>
      设置快捷键 s 修改 useState 定义的 num 数据
      <div>num: {num}</div>
    </div>
  );
};
  • 不推荐(会导致组件重新渲染时频繁取消/订阅,性能差,虽然内部做了处理,避免这个问题,但是还是不推荐)
  • 目前做了优化,组件重新渲染时不会频繁取消/订阅(虽然好像可以在回调中拿到最新的state,但还是推荐使用前门两种方式获取state)
jsx 复制代码
import React, { useState, useCallback } from 'react';
import { useKeyEvent } from 'react-khooks';

export default () => {
  const [num, setNum] = useState(0);
  const handleClick = (param) => {
    setNum(num + param);
  };

  useKeyEvent({ keyName: 'ctrl+x', callback: () => handleClick(1), toolEventName: 'add_2' });
  useKeyEvent({ keyName: 'shift+x', callback: () => handleClick(-1), toolEventName: 'reduce_2' });

  return (
    <div>
      <div>按键ctrl+x增加num</div>
      <div>按键shift+x减小num</div>
      <button onClick={() => handleClick(1)}> 加 1 </button>
      <span>num: {num}</span>
      <button onClick={() => handleClick(-1)}> 减 1 </button>
    </div>
  );
};

freezeAll/unfreezeAll:冻结/解冻所有键盘事件队列

jsx 复制代码
import React, { useState, useCallback } from 'react';
import { useKeyEvent } from 'react-khooks';

export default () => {
  const [num, setNum] = useState(0);

  const handleClick = () => {
    setNum((num) => num + 1);
  };

  const { emitter } = useKeyEvent({ keyName: 'f', callback: handleClick, toolEventName: 'add' });
  // 你也可以直接 import { emitter } from 'react-khooks',因为这里的emitter始终是同一个实例

  return (
    <div>
      <p>按下键盘f键修改num</p>
      <span style={{ border: '1px solid #ccc' }} onClick={() => emitter.freezeAll()}>
        冻 结
      </span>
      <div>num: {num}</div>
      <span style={{ border: '1px solid #ccc' }} onClick={() => emitter.unfreezeAll()}>
        解 冻
      </span>
    </div>
  );
};

因为已经满足当前业务需求,所以没有在做更多功能的实现,如需自取,附上地址:

npm地址

git仓库

相关推荐
LuckyLay3 小时前
React百日学习计划-Grok3
前端·学习·react.js
呵呵哒( ̄▽ ̄)"5 小时前
React - 编写选择礼物组件
前端·javascript·react.js
Coding的叶子5 小时前
React Flow 简介:构建交互式流程图的最佳工具
前端·react.js·流程图·fgai·react agent
Peter 谭13 小时前
React Hooks 实现原理深度解析:从基础到源码级理解
前端·javascript·react.js·前端框架·ecmascript
LuckyLay15 小时前
React百日学习计划——Deepseek版
前端·学习·react.js
程序猿阿伟16 小时前
《React Native与Flutter:社交应用中用户行为分析与埋点统计的深度剖析》
flutter·react native·react.js
学渣y18 小时前
React状态管理-对state进行保留和重置
javascript·react.js·ecmascript
进取星辰20 小时前
25、Tailwind:魔法速记术——React 19 样式新思路
前端·react.js·前端框架
Bl_a_ck1 天前
【React】Craco 简介
开发语言·前端·react.js·typescript·前端框架
寧笙(Lycode)1 天前
React系列——HOC高阶组件的封装与使用
前端·react.js·前端框架