快键键 hook 实现

93 阅读1分钟

需求: 审核的时候需要加键盘快捷键

import { useEffect, useRef } from "react";

type KeyMap = { [key: string]: (evt: KeyboardEvent) => void };

const useKeyboard = (keyDownMap: KeyMap = {}, keyUpMap: KeyMap = {}) => {
	const keyStackRef = useRef<Array<{ down: KeyMap; up: KeyMap }>>([]);
	const keyDownMapRef = useRef(keyDownMap); // 使用 useRef 存储最新的 keyDownMap
	const keyUpMapRef = useRef(keyUpMap); // 使用 useRef 存储最新的 keyUpMap

	// 每次 keyDownMap 或 keyUpMap 变化时更新 ref
	useEffect(() => {
		keyDownMapRef.current = keyDownMap;
		keyUpMapRef.current = keyUpMap;
	}, [keyDownMap, keyUpMap]);

	// 生成唯一的键值字符串,依据传入的修饰键和按键
	const getKeyForEvent = (evt: KeyboardEvent) => {
		const keys = [];

		// 如果对应的修饰键被按下,则将其添加到 keys 数组中
		if (evt.ctrlKey) keys.push("ctrl");
		if (evt.metaKey) keys.push("meta"); // MacOS 的 Command 键
		if (evt.altKey) keys.push("alt");
		if (evt.shiftKey) keys.push("shift");

		// 最后将实际按下的键添加到 keys 数组中
		keys.push(evt.key?.toLowerCase() || evt.key);
		// console.log("keys", keys, keys.join("+"));
		// 返回一个以 '+' 连接的字符串作为唯一键值
		return keys.join("+");
	};

	// 处理键盘事件的逻辑
	const onKeyEvent = (keyMap: KeyMap) => {
		return (evt: KeyboardEvent) => {
			const key = getKeyForEvent(evt); // 获取组合键的唯一标识符
			if (!keyMap[key]) return; // 如果没有对应的处理函数,直接返回
			evt.preventDefault(); // 阻止默认行为
			keyMap[key](evt); // 执行对应的处理函数
		};
	};

	// 处理 keyDown 事件
	const onKeyDown = (evt: KeyboardEvent) => {
		const currentKeyMap =
			keyStackRef.current[keyStackRef.current.length - 1]?.down ||
			keyDownMapRef.current; // 使用最新的 keyDownMap
		onKeyEvent(currentKeyMap)(evt);
	};

	// 处理 keyUp 事件
	const onKeyUp = (evt: KeyboardEvent) => {
		const currentKeyMap =
			keyStackRef.current[keyStackRef.current.length - 1]?.up ||
			keyUpMapRef.current; // 使用最新的 keyUpMap
		onKeyEvent(currentKeyMap)(evt);
	};

	useEffect(() => {
		window.addEventListener("keydown", onKeyDown); // 添加键盘按下事件监听
		window.addEventListener("keyup", onKeyUp); // 添加键盘松开事件监听
		return () => {
			window.removeEventListener("keydown", onKeyDown); // 组件卸载时移除监听
			window.removeEventListener("keyup", onKeyUp);
		};
	}, []);

	// 将新的键映射推入栈中
	const pushKeyMap = (downMap: KeyMap = {}, upMap: KeyMap = {}) => {
		keyStackRef.current.push({ down: downMap, up: upMap });
	};

	// 从栈中弹出最新的键映射
	const popKeyMap = () => {
		keyStackRef.current.pop();
	};

	return {
		pushKeyMap,
		popKeyMap,
	};
};

export default useKeyboard;

使用

	useKeyboard({
		"shift+p": () => {
			if (props.isOpen && !props?.isDeliverLevel) {
				handleAudit("approved", props?.isProduct16);
			}
		},
		"shift+r": () => {
			if (props.isOpen && !props?.isDeliverLevel) {
				handleAudit("rejected", props?.isProduct16);
			}
		},
	});