You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Lexical的TypeaheadMenuPlugin中自动聚焦ShadCN Command组件

解决Lexical TypeaheadMenuPlugin中ShadCN Command组件自动聚焦问题

问题原因分析

  1. Ref传递失效:ShadCN的Command组件如果未通过forwardRef将ref转发到底层DOM元素,直接使用useRef会拿到组件实例而非可聚焦的DOM节点,调用focus()无效。
  2. 聚焦时机过早:原代码的空依赖useEffect在组件挂载时执行,但此时Lexical的菜单还未触发渲染(anchorElementRef.current可能仍为空),Portal中的Command组件尚未创建,导致commandRef.current为null。

解决方案

方案一:修正Ref传递与聚焦时机

确保Command组件的ref正确指向底层DOM,并延迟到DOM渲染完成后再执行聚焦:

import React, { useEffect, useRef } from "react";
import ReactDOM from "react-dom";
import { LexicalTypeaheadMenuPlugin } from "@lexical/react/LexicalTypeaheadMenuPlugin";
import { Command, CommandGroup, CommandItem, CommandList } from "@/components/ui/command";

const ComponentPickerMenuPlugin: React.FC = () => {
  // 指定ref类型为HTMLDivElement,匹配Command组件的根DOM节点
  const commandRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (commandRef.current) {
      // 使用requestAnimationFrame确保DOM完全渲染后再聚焦
      requestAnimationFrame(() => {
        commandRef.current?.focus();
      });
    }
  }, [commandRef.current]); // 监听ref的变化,确保菜单渲染后触发

  return (
    <LexicalTypeaheadMenuPlugin
      menuRenderFn={(anchorElementRef) =>
        anchorElementRef.current
          ? ReactDOM.createPortal(
              <Command ref={commandRef} tabIndex={0} className="rounded-lg">
                <CommandList>
                  <CommandGroup heading="Suggestions">
                    <CommandItem>Option 1</CommandItem>
                    <CommandItem>Option 2</CommandItem>
                  </CommandGroup>
                </CommandList>
              </Command>,
              anchorElementRef.current
            )
          : null
      }
    />
  );
};

export default ComponentPickerMenuPlugin;

方案二:聚焦第一个可选项(更符合菜单交互逻辑)

通常菜单的合理交互是直接聚焦到第一个选项,方便用户用上下键切换,无需聚焦容器本身:

import React, { useEffect, useRef } from "react";
import ReactDOM from "react-dom";
import { LexicalTypeaheadMenuPlugin } from "@lexical/react/LexicalTypeaheadMenuPlugin";
import { Command, CommandGroup, CommandItem, CommandList } from "@/components/ui/command";

const ComponentPickerMenuPlugin: React.FC = () => {
  const firstItemRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (firstItemRef.current) {
      requestAnimationFrame(() => {
        firstItemRef.current?.focus();
      });
    }
  }, [firstItemRef.current]);

  return (
    <LexicalTypeaheadMenuPlugin
      menuRenderFn={(anchorElementRef) =>
        anchorElementRef.current
          ? ReactDOM.createPortal(
              <Command className="rounded-lg">
                <CommandList>
                  <CommandGroup heading="Suggestions">
                    <CommandItem ref={firstItemRef}>Option 1</CommandItem>
                    <CommandItem>Option 2</CommandItem>
                  </CommandGroup>
                </CommandList>
              </Command>,
              anchorElementRef.current
            )
          : null
      }
    />
  );
};

export default ComponentPickerMenuPlugin;

方案三:自定义封装Command组件(解决Ref未转发问题)

如果ShadCN的Command组件未默认转发ref,可自定义封装组件内部处理聚焦:

import React, { useEffect, useRef, forwardRef } from "react";
import { Command as ShadCommand, CommandGroup, CommandItem, CommandList } from "@/components/ui/command";
import { LexicalTypeaheadMenuPlugin } from "@lexical/react/LexicalTypeaheadMenuPlugin";
import ReactDOM from "react-dom";

// 自定义带自动聚焦的Command组件,转发ref到底层DOM
const CommandWithAutoFocus = forwardRef<HTMLDivElement, React.ComponentProps<typeof ShadCommand>>(
  (props, ref) => {
    const innerRef = useRef<HTMLDivElement>(null);

    useEffect(() => {
      if (innerRef.current) {
        innerRef.current.focus();
      }
    }, []);

    return <ShadCommand ref={innerRef} tabIndex={0} {...props} />;
  }
);

const ComponentPickerMenuPlugin: React.FC = () => {
  return (
    <LexicalTypeaheadMenuPlugin
      menuRenderFn={(anchorElementRef) =>
        anchorElementRef.current
          ? ReactDOM.createPortal(
              <CommandWithAutoFocus className="rounded-lg">
                <CommandList>
                  <CommandGroup heading="Suggestions">
                    <CommandItem>Option 1</CommandItem>
                    <CommandItem>Option 2</CommandItem>
                  </CommandGroup>
                </CommandList>
              </CommandWithAutoFocus>,
              anchorElementRef.current
            )
          : null
      }
    />
  );
};

export default ComponentPickerMenuPlugin;

内容的提问来源于stack exchange,提问作者Umamaheswaran

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 17:20:59