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

父组件onScroll事件无法调用子组件函数问题求助

问题描述

子组件内通过onScroll调用HeadlessUI Menu的close函数能正常关闭菜单,但把该函数传递到父组件后,父组件的onScroll调用就失效了。

错误原因分析

  1. 父组件未执行关闭函数:父组件的onScroll回调里只是访问了sidebarMenuElementRef.current,但没有调用这个函数,自然不会触发菜单关闭操作。
  2. 子组件Ref赋值逻辑错误:MenuButton的ref属性是用来获取DOM元素的,不能直接用来存储close函数,这种赋值方式无效,导致menuButtonElementRef.current无法正确拿到close函数。
  3. 多组件Ref冲突:父组件用单个useRef存储多个子组件的引用,最后只会保留最后一个子组件的ref,滚动时无法关闭所有打开的菜单。

修复方案

1. 修正父组件的Ref管理与滚动回调

把单个ref改成数组ref存储所有子组件的关闭函数,同时在滚动时遍历执行所有关闭操作:

import { useRef } from 'react'
import SidebarMenuElement from '@/Layouts/Authenticated/Partials/Sidebar/MenuElement'

// 示例导航数据,根据实际场景替换
const navigation = [
  { name: '菜单1', url: '/menu1', icon: () => <span>📁</span> },
  { name: '菜单2', url: '/menu2', icon: () => <span>📂</span> },
]

export default function Sidebar() {
  const sidebarMenuElementRefs = useRef([])

  const handleScroll = () => {
    // 遍历所有子组件的关闭函数并执行
    sidebarMenuElementRefs.current.forEach(closeFn => {
      typeof closeFn === 'function' && closeFn()
    })
  }

  return (
    <>
      <div>
        <div
          className="h-full w-[inherit] overflow-y-auto scroll-smooth"
          style={{ scrollbarGutter: 'stable both-edges' }}
          onScroll={handleScroll}
        >
          <nav>
            <div>
              <ul>
                {navigation.map((item, i) => (
                  <li key={item.name}>
                    <SidebarMenuElement
                      data={item}
                      ref={el => sidebarMenuElementRefs.current[i] = el}
                    />
                  </li>
                ))}
              </ul>
            </div>
          </nav>
        </div>
      </div>
    </>
  )
}

2. 修正子组件的Ref逻辑

直接在Menu的回调中把close函数存入ref,无需通过MenuButton的ref传递:

import { useRef, forwardRef, useImperativeHandle } from 'react'
import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'
import { Link } from '@inertiajs/react'

const SidebarMenuElement = forwardRef(function SidebarMenuElement(props, ref) {
  const { data } = props
  const closeMenuRef = useRef(null)

  // 向父组件暴露关闭函数
  useImperativeHandle(ref, () => () => closeMenuRef.current?.(), [])

  return (
    <Menu>
      {({ close }) => {
        // 实时更新close函数引用
        closeMenuRef.current = close
        return (
          <>
            <MenuButton className="flex items-center gap-2 p-2">
              <span>{data.icon()}</span>
              <span>{data.name}</span>
            </MenuButton>
            <MenuItems className="absolute bg-white shadow-md rounded-md p-2">
              <MenuItem>
                <Link href={data.url} className="block px-4 py-2 hover:bg-gray-100">
                  {data.name}
                </Link>
              </MenuItem>
            </MenuItems>
          </>
        )
      }}
    </Menu>
  )
})

export default SidebarMenuElement

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 10:44:59