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

React中useRef为何无法立即更新组件的clientHeight?

解决useRef无法立即获取FilterCheckboxGroup组件正确高度的问题

问题根源

FilterText组件结构简单,DOM同步渲染完成,useEffect执行时能拿到准确的clientHeight;但FilterCheckboxGroup内部大概率存在异步渲染逻辑(比如从接口拉取选项数据、动态生成DOM节点),当useEffect触发时,它的DOM还未完全加载完毕,因此首次拿到的是未渲染完整的高度,刷新页面后组件缓存了数据,DOM同步渲染,高度才正确。

解决方案

方案1:用useLayoutEffect替代useEffect+监听DOM变化

useLayoutEffect会在DOM更新完成后立即执行(早于useEffect),再结合MutationObserver监听组件内部DOM变化,确保内容加载完成后重新计算高度:

import { useLayoutEffect, useState, useRef } from 'react';

const [menuHeight, setMenuHeight] = useState();
const subMenuFilterList = useRef(null);

// 自适应菜单高度
useLayoutEffect(() => {
    const updateMenuHeight = () => {
        if (subMenuFilterList.current && openSubMenu) {
            setMenuHeight(subMenuFilterList.current.clientHeight);
        } else {
            setMenuHeight(null);
        }
    };

    // 初始计算高度
    updateMenuHeight();

    // 仅针对FilterCheckboxGroup添加DOM变化监听
    let domObserver = null;
    if (subMenuFilterList.current && filtersList?.[filterId]?.systemName === 'in') {
        domObserver = new MutationObserver(updateMenuHeight);
        domObserver.observe(subMenuFilterList.current, {
            childList: true, // 监听子节点增减
            subtree: true,   // 监听所有后代节点
            attributes: false
        });
    }

    // 组件卸载/依赖变化时清理监听
    return () => {
        domObserver?.disconnect();
    };
}, [openSubMenu, filterId, filtersList]); // 加入filtersList作为依赖,确保数据更新时重新计算

方案2:子组件添加内容就绪回调(推荐,更精准)

如果FilterCheckboxGroup是你自己封装的组件,可以在内部内容加载完成后主动通知父组件,父组件收到通知后再计算高度:

子组件FilterCheckboxGroup修改:

const FilterCheckboxGroup = ({ onContentReady, ...props }) => {
    const [options, setOptions] = useState([]);

    useEffect(() => {
        // 模拟异步获取选项数据
        fetch('/api/get-checkbox-options')
            .then(res => res.json())
            .then(data => {
                setOptions(data);
                // 数据加载完成,触发回调通知父组件
                onContentReady?.();
            });
    }, []);

    return (
        <div>
            {options.map(opt => (
                <label key={opt.id}>
                    <input type="checkbox" value={opt.value} />
                    {opt.label}
                </label>
            ))}
        </div>
    );
};

父组件调用时添加回调:

const handleFilterContentReady = () => {
    if (subMenuFilterList.current && openSubMenu) {
        setMenuHeight(subMenuFilterList.current.clientHeight);
    }
};

// 在React.createElement中新增onContentReady属性
React.createElement(
    filtersList?.[filterId]?.node ?? FilterCheckboxGroup,
    {
        column: column,
        onChange,
        value,
        ...props,
        key: filterId,
        filtersList: filtersList,
        setOpenSubMenu: setOpenSubMenu,
        setFilterValue: setFilterValue,
        table,
        ref: subMenuFilterList,
        onContentReady: handleFilterContentReady // 新增回调
    }
)

关键说明

  • 避免仅依赖openSubMenu和filterId触发高度计算,必须考虑子组件内容的异步加载时机。
  • MutationObserver适合无法修改子组件的场景,而子组件回调的方式更高效、精准。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 08:52:15