MST

星途 面试题库

面试题:React应用中如何优雅地管理全局快捷键并避免冲突

在一个复杂的React单页应用中,存在多个组件可能需要注册不同的快捷键。例如,组件A需要监听Ctrl + A实现全选,组件B需要监听Ctrl + C实现复制。你需要设计一个全局快捷键管理机制,确保各个快捷键之间不会产生冲突,并且当某个组件卸载时,其注册的快捷键能够正确取消监听。请阐述详细的设计思路,包括数据结构、主要函数逻辑等,并给出核心代码示例。
35.0万 热度难度
前端开发React

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 数据结构
    • 使用一个对象来存储所有注册的快捷键及其对应的回调函数和组件标识。例如:{ 'Ctrl+A': { callback: () => {}, componentId: 'componentA' }, 'Ctrl+C': { callback: () => {}, componentId: 'componentB' } }
    • 为每个组件分配一个唯一的标识(可以是组件的名称或生成的唯一ID),用于在组件卸载时取消对应的快捷键监听。
  2. 主要函数逻辑
    • 注册函数:接受快捷键组合(如Ctrl+A)、回调函数和组件标识作为参数,将其存储到上述数据结构中,并添加全局键盘事件监听器。在监听器中,判断按下的键组合是否与已注册的快捷键匹配,若匹配则执行对应的回调函数。
    • 取消注册函数:接受组件标识作为参数,遍历数据结构,删除所有与该组件标识相关的快捷键记录,并在必要时更新全局键盘事件监听器,确保不再触发已卸载组件的快捷键回调。

核心代码示例

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

// 存储快捷键的对象
const shortcutRegistry = {};

// 注册快捷键
function registerShortcut(shortcut, callback, componentId) {
    if (shortcutRegistry[shortcut]) {
        throw new Error(`Shortcut ${shortcut} is already registered.`);
    }
    shortcutRegistry[shortcut] = { callback, componentId };
    document.addEventListener('keydown', handleKeyDown);
}

// 取消注册快捷键
function unregisterShortcut(componentId) {
    Object.keys(shortcutRegistry).forEach(shortcut => {
        if (shortcutRegistry[shortcut].componentId === componentId) {
            delete shortcutRegistry[shortcut];
        }
    });
    if (Object.keys(shortcutRegistry).length === 0) {
        document.removeEventListener('keydown', handleKeyDown);
    }
}

// 处理键盘按下事件
function handleKeyDown(event) {
    const keyCombination = getKeyCombination(event);
    if (shortcutRegistry[keyCombination]) {
        shortcutRegistry[keyCombination].callback(event);
    }
}

// 获取键组合
function getKeyCombination(event) {
    const keys = [];
    if (event.ctrlKey) keys.push('Ctrl');
    if (event.shiftKey) keys.push('Shift');
    if (event.altKey) keys.push('Alt');
    keys.push(event.key);
    return keys.join('+');
}

// 示例组件A
function ComponentA() {
    const componentId = 'componentA';
    useEffect(() => {
        registerShortcut('Ctrl+A', () => {
            // 全选逻辑
            console.log('全选操作');
        }, componentId);
        return () => {
            unregisterShortcut(componentId);
        };
    }, []);
    return <div>Component A</div>;
}

// 示例组件B
function ComponentB() {
    const componentId = 'componentB';
    useEffect(() => {
        registerShortcut('Ctrl+C', () => {
            // 复制逻辑
            console.log('复制操作');
        }, componentId);
        return () => {
            unregisterShortcut(componentId);
        };
    }, []);
    return <div>Component B</div>;
}

function App() {
    return (
        <div>
            <ComponentA />
            <ComponentB />
        </div>
    );
}

export default App;