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

Flowbite Modal打开后,react-hook-form的setFocus自动聚焦失效,求替代方案

解决Flowbite Modal内react-hook-form输入框自动聚焦问题

问题本质

直接用useRef同步调用focus()无效,核心原因是Flowbite Modal的显示包含异步动画过程——输入框要等Modal动画完成后才真正挂载到DOM中,同步执行聚焦时元素还未就绪。

替代方案

方案1:利用Flowbite Modal的onShow回调

Flowbite React的Modal组件提供了onShow回调,会在Modal完全显示(含动画结束)后触发,此时输入框已就绪,可安全调用聚焦逻辑。结合react-hook-form的register获取输入框ref:

import { useState } from 'react';
import { Modal } from 'flowbite-react';
import { useForm } from 'react-hook-form';

function ModalWithAutoFocus() {
  const [showModal, setShowModal] = useState(false);
  const { register, formState: { errors } } = useForm();
  // 从register中解构出输入框ref
  const inputRef = register('username', { required: '用户名不能为空' }).ref;

  const handleModalShow = () => {
    inputRef.current?.focus();
  };

  return (
    <>
      <button onClick={() => setShowModal(true)}>打开模态框</button>
      <Modal show={showModal} onClose={() => setShowModal(false)} onShow={handleModalShow}>
        <Modal.Header>用户登录</Modal.Header>
        <Modal.Body>
          <div className="mb-4">
            <label htmlFor="username" className="block mb-2 text-sm font-medium">用户名</label>
            <input
              id="username"
              type="text"
              className={`w-full px-3 py-2 border ${errors.username ? 'border-red-500' : 'border-gray-300'} rounded`}
              {...register('username', { required: '用户名不能为空' })}
            />
            {errors.username && <p className="mt-1 text-xs text-red-500">{errors.username.message}</p>}
          </div>
        </Modal.Body>
        <Modal.Footer>
          <button onClick={() => setShowModal(false)} className="mr-2 px-4 py-2 bg-gray-200 rounded">取消</button>
          <button type="submit" className="px-4 py-2 bg-blue-600 text-white rounded">提交</button>
        </Modal.Footer>
      </Modal>
    </>
  );
}

方案2:使用react-hook-form内置的setFocus方法

react-hook-form提供了setFocusAPI,可直接指定表单字段名聚焦,配合Modal的onShow回调使用更简洁:

import { useState } from 'react';
import { Modal } from 'flowbite-react';
import { useForm } from 'react-hook-form';

function ModalWithAutoFocus() {
  const [showModal, setShowModal] = useState(false);
  const { register, setFocus, formState: { errors } } = useForm();

  const handleModalShow = () => {
    // 直接通过字段名触发聚焦
    setFocus('username');
  };

  return (
    <>
      <button onClick={() => setShowModal(true)}>打开模态框</button>
      <Modal show={showModal} onClose={() => setShowModal(false)} onShow={handleModalShow}>
        <Modal.Header>用户登录</Modal.Header>
        <Modal.Body>
          <div className="mb-4">
            <label htmlFor="username" className="block mb-2 text-sm font-medium">用户名</label>
            <input
              id="username"
              type="text"
              className={`w-full px-3 py-2 border ${errors.username ? 'border-red-500' : 'border-gray-300'} rounded`}
              {...register('username', { required: '用户名不能为空' })}
            />
            {errors.username && <p className="mt-1 text-xs text-red-500">{errors.username.message}</p>}
          </div>
        </Modal.Body>
        <Modal.Footer>
          <button onClick={() => setShowModal(false)} className="mr-2 px-4 py-2 bg-gray-200 rounded">取消</button>
          <button type="submit" className="px-4 py-2 bg-blue-600 text-white rounded">提交</button>
        </Modal.Footer>
      </Modal>
    </>
  );
}

方案3:监听Modal显示状态的useEffect

通过useEffect监听Modal的显示状态变化,当状态变为true时,检查输入框ref是否存在再执行聚焦:

import { useState, useEffect } from 'react';
import { Modal } from 'flowbite-react';
import { useForm } from 'react-hook-form';

function ModalWithAutoFocus() {
  const [showModal, setShowModal] = useState(false);
  const { register, formState: { errors } } = useForm();
  const inputRef = register('username', { required: '用户名不能为空' }).ref;

  useEffect(() => {
    // 仅当Modal显示且输入框ref存在时执行聚焦
    if (showModal && inputRef.current) {
      inputRef.current.focus();
    }
  }, [showModal, inputRef]);

  return (
    <>
      <button onClick={() => setShowModal(true)}>打开模态框</button>
      <Modal show={showModal} onClose={() => setShowModal(false)}>
        <Modal.Header>用户登录</Modal.Header>
        <Modal.Body>
          <div className="mb-4">
            <label htmlFor="username" className="block mb-2 text-sm font-medium">用户名</label>
            <input
              id="username"
              type="text"
              className={`w-full px-3 py-2 border ${errors.username ? 'border-red-500' : 'border-gray-300'} rounded`}
              {...register('username', { required: '用户名不能为空' })}
            />
            {errors.username && <p className="mt-1 text-xs text-red-500">{errors.username.message}</p>}
          </div>
        </Modal.Body>
        <Modal.Footer>
          <button onClick={() => setShowModal(false)} className="mr-2 px-4 py-2 bg-gray-200 rounded">取消</button>
          <button type="submit" className="px-4 py-2 bg-blue-600 text-white rounded">提交</button>
        </Modal.Footer>
      </Modal>
    </>
  );
}

方案优势

这些方案均基于组件官方回调或React生命周期逻辑,无需依赖时间延迟,避免了不同设备性能差异导致的聚焦失效问题,代码更具可读性和可维护性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 02:50:25