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

使用framer-motion的motion.span组件出现TypeError问题求助

解决framer-motion中motion.span引发的TypeError: Cannot set properties of null (setting 'textContent')错误

问题描述

使用framer-motion的motion.span组件时,出现TypeError: Cannot set properties of null (setting 'textContent')错误。该错误不会阻止Web应用运行,但会在一段时间后突然弹出在屏幕上,错误由animationProjectsCount函数中的onUpdate回调引发。

错误原因

  1. ref临时为空:whileInView触发动画时,可能因组件重渲染、视图状态变化等原因,projectCount.current还未绑定到DOM元素或已临时变为null,此时直接赋值textContent就会报错。
  2. 动画未清理:组件卸载后,之前启动的动画可能仍在后台运行,此时projectCount.current已经是null,执行赋值操作触发错误。
  3. 重复触发动画:元素多次进入视图时,动画会重复启动,增加了ref为空的概率。

解决方案

1. 给ref添加空值判断

在onUpdate回调中先检查projectCount.current是否存在,再执行赋值操作,避免空值错误:

const animationProjectsCount = () => {
    animate(0, 30, {
        duration: 1,
        onUpdate: (v) => {
            if (projectCount.current) { // 先确认ref指向有效DOM元素
                projectCount.current.textContent = v.toFixed();
            }
        }
    })
}

2. 组件卸载时清理动画

使用useRef保存动画控制对象,在组件卸载时停止动画,防止组件销毁后动画仍在运行:

import React, { useRef, useEffect } from 'react';
import { animate, motion } from "framer-motion";
import Timeline from './Timeline';

const Home = () => {
    const projectCount = useRef(null);
    const animationControls = useRef(null); // 保存动画控制对象

    const animationProjectsCount = () => {
        animationControls.current = animate(0, 30, {
            duration: 1,
            onUpdate: (v) => {
                if (projectCount.current) {
                    projectCount.current.textContent = v.toFixed();
                }
            }
        })
    }

    useEffect(() => {
        // 组件卸载时停止动画
        return () => {
            if (animationControls.current) {
                animationControls.current.stop();
            }
        }
    }, [])

    return (
        <div id='about'>
            <div className="abtSections">
                <div className="abtLeft">
                    <h1 className='abtHeader'>About</h1>
                    <p>As I strive to achieve my goal of becoming a full-stack developer, I have acquired valuable experience through the completion of various projects at a beginner level, and my accumalated years of programming and project work. Moreover, I possess a notable self aptitude for self-directed learning and am able to apply myself for acquiring new skills at a constrained time frame.</p>
                </div>
                <div className="abtRight">
                    <Timeline />
                </div>
                <div style={{ clear: "both" }}></div>
            </div>
            <div className='projectsDone'>
                <p className='number'>
                    +<motion.span ref={projectCount} whileInView={animationProjectsCount}></motion.span>
                </p>
                <span className='done'>Projects Done</span>
            </div>
        </div>
    )
}

export default Home

3. 限制动画只触发一次

给whileInView添加viewport={{ once: true }}配置,确保动画仅在元素第一次进入视图时触发,减少重复执行带来的风险:

<motion.span ref={projectCount} whileInView={animationProjectsCount} viewport={{ once: true }}></motion.span>

综合优化建议

将以上三种方案结合使用:空值判断避免即时错误,动画清理防止组件卸载后报错,单次触发减少不必要的动画执行,彻底解决该问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 21:53:21