React函数组件检测div溢出报错:Cannot read property 'style' of null
问题分析与解决方案
你遇到的Cannot read property 'style' of null错误,核心原因是组件初始渲染时,DOM元素还未挂载到页面,此时ref.current是null,你直接在render阶段调用checkOverflow(),自然会访问到null的style属性导致报错。
下面针对两种方案分别给出修复后的代码,并解释关键改进点:
方案1:使用useRef(函数组件推荐替代createRef)
函数组件中更推荐用useRef(它会保持引用稳定,不像createRef每次渲染都创建新对象),结合useEffect在DOM挂载后再执行溢出检测:
import React, { useState, useRef, useEffect } from "react"; import "./styles.css"; export default function App(props) { const [showMore, setShowMore] = useState(false); const [isOverflowing, setIsOverflowing] = useState(false); const contentRef = useRef(null); const toggleShowMore = () => { setShowMore(!showMore); }; const checkOverflow = () => { const el = contentRef.current; if (!el) return false; // 先判断元素是否存在,做容错处理 // 临时修改overflow确保检测准确(如果你的容器默认不是hidden的话) const originalOverflow = el.style.overflow; if (!originalOverflow || originalOverflow === "visible") { el.style.overflow = "hidden"; } const hasOverflow = el.clientWidth < el.scrollWidth || el.clientHeight < el.scrollHeight; el.style.overflow = originalOverflow; // 恢复原样式 return hasOverflow; }; useEffect(() => { // DOM挂载后执行检测,同时监听文本变化、展开/收起状态、窗口resize const updateOverflowStatus = () => { setIsOverflowing(checkOverflow()); }; updateOverflowStatus(); window.addEventListener("resize", updateOverflowStatus); // 组件卸载时移除监听 return () => { window.removeEventListener("resize", updateOverflowStatus); }; }, [props.text, showMore]); return ( <> <div ref={contentRef} className={showMore ? "container-nowrap" : "container"} > {props.text} </div> {isOverflowing && ( <span className="link" onClick={toggleShowMore}> {showMore ? "show less" : "show more"} </span> )} </> ); }
方案2:使用forwardRef
子组件同样需要通过useEffect延迟检测,确保ref已绑定到DOM元素:
子组件
import React, { useState, useEffect } from "react"; export const App = React.forwardRef((props, ref) => { const [showMore, setShowMore] = useState(false); const [isOverflowing, setIsOverflowing] = useState(false); const toggleShowMore = () => { setShowMore(!showMore); }; const checkOverflow = () => { const el = ref.current; if (!el) return false; const originalOverflow = el.style.overflow; if (!originalOverflow || originalOverflow === "visible") { el.style.overflow = "hidden"; } const hasOverflow = el.clientWidth < el.scrollWidth || el.clientHeight < el.scrollHeight; el.style.overflow = originalOverflow; return hasOverflow; }; useEffect(() => { const updateOverflow = () => { setIsOverflowing(checkOverflow()); }; updateOverflow(); window.addEventListener("resize", updateOverflow); return () => { window.removeEventListener("resize", updateOverflow); }; }, [props.text, showMore]); return ( <> <div ref={ref} className={showMore ? "container-nowrap" : "container"} > {props.text} </div> {isOverflowing && ( <span className="link" onClick={toggleShowMore}> {showMore ? "show less" : "show more"} </span> )} </> ); });
父组件(无需修改)
import React from "react"; import ReactDOM from "react-dom"; import { App } from "./App"; const rootElement = document.getElementById("root"); const contentRef = React.createRef(); ReactDOM.render( <React.StrictMode> <App ref={contentRef} text="Start editing to see some magic happen! Click show more to expand and show less to collapse the text" /> </React.StrictMode>, rootElement );
关键改进说明
- 延迟检测时机:用
useEffect确保DOM挂载后再执行溢出检测,避免初始渲染时ref.current为null的问题。 - 状态托管溢出结果:把溢出状态存在
useState中,而不是在render阶段实时计算,避免重复执行检测逻辑,也保证渲染的稳定性。 - 添加resize监听:窗口大小变化可能导致溢出状态改变,监听resize事件可以实时更新状态。
- 容错处理:在
checkOverflow中先判断元素是否存在,避免意外报错。
内容的提问来源于stack exchange,提问作者troglodyte07
相关产品推荐
相关产品推荐

