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

点击Input关闭模态框时useState两次调用的问题求助

问题分析与解决方案

问题根源

当你再次点击Input试图关闭模态框时,会出现两次setIsShow调用的冲突:

  1. 自定义hook给document绑定了捕获阶段的click事件(第三个参数true),点击Input时,document的click事件会优先触发。此时Input不在模态框的ref范围内,hook的事件处理逻辑会先调用setIsShow(false)。
  2. 随后事件传递到Input本身,触发它的onClick回调,调用setIsShow(true)。
  3. 两次状态更新抵消后,isShow最终还是true,导致模态框无法关闭。

另外,你的hook存在一个小问题:useEffect没有设置依赖数组,每次组件渲染都会重复绑定/解绑document的click事件,可能引发不必要的性能损耗。


修复方案

方案一:让Input点击事件切换状态(最简单)

直接修改Input的onClick回调,从强制设置true改为切换当前状态,这样无论hook的事件先触发什么,最终都会得到正确的开闭效果:

<input
  onClick={() => setIsShow(prev => !prev)}
  type="button"
  value={
    `${adults === 1 ? `${adults} взрослый` : `${adults} взрослых`} · ${child === 0 ? 'Без детей' : child === 1 ? `${child} ребенок` : child < 5 ? `${child} ребенка` : `${child} детей`} · ${rooms === 1 ? `${rooms} номер` : rooms < 5 ? `${rooms} номера` : `${rooms} номеров`}`
  }
/>

方案二:优化自定义hook,跳过触发元素的关闭逻辑

在hook中新增对触发元素(Input)的判断,点击它时不执行关闭逻辑,同时修正useEffect的依赖问题:

import { useEffect, useRef, useState } from "react";

export default function useOutsideElement(initialVisible: boolean) {
  const [isShow, setIsShow] = useState<boolean>(initialVisible)
  const ref = useRef<HTMLDivElement>(null)
  // 存储触发元素的ref
  const triggerRef = useRef<HTMLInputElement | null>(null)

  const clickHandler = (e: MouseEvent) => {
    // 点击的是触发元素,直接跳过关闭逻辑
    if (triggerRef.current === e.target) return
    if (ref.current && !ref.current.contains(e.target as Node)) {
      setIsShow(false)
    }
  }

  useEffect(() => {
    document.addEventListener('click', clickHandler, true)
    return () => {
      document.removeEventListener('click', clickHandler, true)
    }
  }, []) // 空依赖数组,仅绑定一次事件

  return { ref, isShow, setIsShow, triggerRef }
}

然后在组件中把Input的ref传给hook:

const PersonSearchBlock: NextPage = () => {
  const { ref, isShow, setIsShow, triggerRef } = useOutsideElement(false)
  // ... 其余状态不变

  return (
    <div className='shelters_housing_space_container_search_bar_person'>
      <div className='shelters_housing_space_container_search_bar_person_contant'>
        {/* ... 图片部分不变 */}
        <input
          ref={triggerRef}
          onClick={() => setIsShow(true)}
          type="button"
          // ... 其余代码不变
        />
      </div>
      {/* ... 模态框部分不变 */}
    </div>
  )
}

额外注意事项

要确保PersonModal组件正确接收并绑定refFunck到自身的根DOM元素上,否则可能出现点击模态框内部也会关闭的问题。可以用forwardRef实现:

import { forwardRef } from 'react'

const PersonModal = forwardRef((props, ref) => {
  return (
    <div ref={ref} className='your-modal-class'>
      {/* 模态框内容 */}
    </div>
  )
})

内容的提问来源于stack exchange,提问作者Стас Лабанов

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 10:35:20