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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 09:06:03