引入Apollo Client useQuery后tabs.current为null的问题求助
问题根源
你遇到的核心问题是useQuery的loading状态导致组件提前返回,跳过了tabs相关DOM的渲染:
当homepageLoading为true时,组件直接返回"Loading header",此时你的tabs容器(也就是tabs.current对应的DOM元素)根本没有被渲染到页面上,自然tabs.current会是null。即使后续数据加载完成组件重新渲染,之前依赖tab的useEffect执行时,也可能因为DOM还未完成挂载导致null问题。
解决方案
1. 保留tabs容器的渲染(即使在loading状态)
不要在loading时直接返回纯文本,而是渲染包含tabs结构的占位内容,保证tabs对应的DOM始终存在,这样useRef能稳定获取到引用:
// 替换原来的loading/error返回逻辑 if (homepageLoading) { // 渲染带tabs容器的骨架屏,或者空的结构 return ( <div className="your-tabs-container-class" ref={tabs}> {/* 骨架屏内容,比如空的tab面板 */} <div className="tab-panel-skeleton"></div> </div> ); } if (homepageError) { return ( <div className="error-container"> Oops there has been an error: {homepageError.message} </div> ); }
2. 给heightFix函数加容错处理
避免因为tabs.current为null或者子元素不存在导致报错,在操作DOM前增加判断:
const heightFix = () => { // 先判断tabs.current是否存在 if (!tabs.current) return; const targetPanel = tabs.current.children[tab - 1]; if (targetPanel) { tabs.current.style.height = `${targetPanel.offsetHeight}px`; } };
3. 修正useEffect的依赖项(移除eslint禁用)
原来的useEffect禁用了依赖检查,可能导致闭包问题(比如拿不到最新的tab或者tabs.current)。把heightFix加入依赖,同时可以用useCallback包裹确保回调更新:
// 用useCallback包裹heightFix,确保依赖变化时更新 const heightFix = useCallback(() => { if (!tabs.current) return; const targetPanel = tabs.current.children[tab - 1]; if (targetPanel) { tabs.current.style.height = `${targetPanel.offsetHeight}px`; } }, [tab]); // 依赖tab,因为tab变化时需要重新计算高度 useEffect(() => { heightFix(); }, [heightFix]); // 依赖heightFix,确保回调更新时执行
修改后的完整示例片段
function HomeHowItWorks() { const [tab, setTab] = useState(1); const [blockName, setBlockName] = useState("home"); const tabs = useRef(null); useEffect(() => { console.log(tabs.current); }, [tabs]); const heightFix = useCallback(() => { if (!tabs.current) return; const targetPanel = tabs.current.children[tab - 1]; if (targetPanel) { tabs.current.style.height = `${targetPanel.offsetHeight}px`; } }, [tab]); useEffect(() => { heightFix(); }, [heightFix]); const { data: homepageData, error: homepageError, loading: homepageLoading, } = useQuery(getPageHomepage, { variables: { blockname: blockName }, }); console.log(homepageData); if (homepageLoading) { return ( <div ref={tabs} className="your-tabs-container"> <div className="tab-panel-skeleton"></div> </div> ); } if (homepageError) { return <div>Oops there has been an error: {homepageError.message}</div>; } return ( {/* 你的完整tabs渲染逻辑,确保tabs ref绑定到正确的容器上 */} <div ref={tabs} className="your-tabs-container"> {/* 切换tab的按钮 */} <button onClick={() => setTab(1)}>Tab 1</button> <button onClick={() => setTab(2)}>Tab 2</button> {/* 面板内容,用homepageData填充 */} <div className="tab-panel"> {homepageData?.pageContent} </div> {/* 其他面板... */} </div> ); }
内容的提问来源于stack exchange,提问作者sokratesli
相关产品推荐
相关产品推荐

