React中如何通过计算容器div宽度设置文本宽度并解决渲染报错
错误原因
- 你直接在组件函数顶层调用
setTextSplit(),执行时React尚未完成DOM渲染,且初始状态下allPostsData为null,带有hero-description、.before类名的DOM元素完全未生成,document.querySelector('.hero-description')返回null,读取offsetWidth属性自然触发空类型错误。 - 即使临时绕过报错,你的博客数据是异步从sanity客户端拉取的,数据返回更新状态触发组件重渲染后,新的DOM节点生成,此前执行的
setTextSplit()也不会重新计算样式,效果无法生效。
修复方案
调整函数调用时机,将setTextSplit()放入依赖allPostsData的useEffect钩子中,确保DOM完全渲染、数据加载完成后再执行,同时增加元素存在性判断避免边界报错,修改后的完整代码如下:
import React, { useEffect, useState} from "react"; import { Link } from "react-router-dom"; import sanityClient from "../client"; export default function Home() { const [allPostsData, setAllPosts] = useState(null); function setTextSplit() { const descEl = document.querySelector('.hero-description'); // 先判断元素存在再执行后续逻辑 if (!descEl) return; const sideWidth = descEl.offsetWidth; const before = document.querySelectorAll(".before"); for(let i = 0; i < before.length; i++) { before[i].style.width = before[i].offsetWidth - sideWidth + 16 + 'px'; } } useEffect(() => { sanityClient .fetch( `*[_type == "post"]{ title, postNumber, excerpt, slug, publishedOn, imageAlt, mainImage{ asset->{ _id, url } } }` ) .then((data) => setAllPosts(data)) .catch(console.error); }, []); // 新增useEffect:allPostsData更新、DOM渲染完成后执行分色计算 useEffect(() => { setTextSplit(); // 适配窗口缩放场景可选增加以下监听 window.addEventListener('resize', setTextSplit); return () => window.removeEventListener('resize', setTextSplit); }, [allPostsData]); return ( <main> {allPostsData && allPostsData.map((post, index) => ( <section key={index} className='hero-post'> <Link to={"/" + post.slug.current} key={post.slug.current} className="hero-image-link" > <img className='hero-image' src={post.mainImage.asset.url} alt={post.imageAlt} /> </Link> <div className='hero-description'> <p className='hero-post-date'>{post.publishedOn}</p> <p>{post.excerpt}</p> <Link to={"/" + post.slug.current} key={post.slug.current} className='hero-post-link'> <p>Keep Reading >></p> </Link> <div className="text-container"> <h1 className='hero-title'>{post.title}<span className='hero-post-number'>/{post.postNumber}</span></h1> <h1 className='hero-title before'>{post.title}<span className='hero-post-number'>/{post.postNumber}</span></h1> </div> </div> </section> ))} </main> ); }
内容的提问来源于stack exchange,提问作者Drucial
相关产品推荐
相关产品推荐

