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

React中数据发生变化时如何触发运行临时特效?

React实现DOM更新后触发临时特效的方案

方案1:状态驱动实现(优先推荐)

React本身是状态驱动UI更新的,我们不需要额外监听DOM变化,直接对比新旧接口数据就能精准定位到需要加特效的元素,是最符合React设计理念的实现方式:

实现步骤

  • 接口请求返回新数据时,和旧数据做diff,记录所有价格发生变化的股票id
  • 用状态变量存储这部分变动的id集合
  • 渲染时给对应id的价格span添加特效类
  • 开启定时器,2秒后清空变动id集合自动移除特效类

代码示例

import { useState, useEffect } from 'react'

// 特效对应的CSS,可自行调整动画效果
const priceFlashCss = `
.price-flash {
  animation: flash 0.5s ease-in-out 2;
}
@keyframes flash {
  0% { background-color: transparent; }
  50% { background-color: #ffeb3b; }
  100% { background-color: transparent; }
}
`

function StockPriceList() {
  const [stockList, setStockList] = useState([])
  const [changedIds, setChangedIds] = useState(new Set())

  // 模拟请求接口
  const fetchStockData = async () => {
    const res = await fetch('/your/api/url')
    const newStockList = await res.json()
    
    // 对比新旧数据,找出价格变动的id
    const newChangedIds = new Set()
    newStockList.forEach(newItem => {
      const oldItem = stockList.find(item => item.id === newItem.id)
      if (oldItem && oldItem.price !== newItem.price) {
        newChangedIds.add(newItem.id)
      }
    })

    setStockList(newStockList)
    setChangedIds(newChangedIds)

    // 2秒后移除特效
    setTimeout(() => {
      setChangedIds(new Set())
    }, 2000)
  }

  // 组件挂载后首次请求,之后每隔一段时间刷新
  useEffect(() => {
    fetchStockData()
    const timer = setInterval(fetchStockData, 5000)
    return () => clearInterval(timer)
  }, [stockList])

  return (
    <div>
      <style>{priceFlashCss}</style>
      {stockList.map(item => (
        <div key={item.id}>
          {item.ticker} Price : <span className={changedIds.has(item.id) ? 'price-flash' : ''}>{item.price}</span>
        </div>
      ))}
    </div>
  )
}

方案2:MutationObserver实现(监听DOM变化的方案)

如果你确实需要脱离业务数据、直接监听DOM的变更(比如渲染逻辑封装在第三方组件内无法拿到原始数据对比的场景),可以直接使用原生MutationObserver实现:

代码示例

import { useRef, useEffect, useState } from 'react'

const priceFlashCss = `
.price-flash {
  animation: flash 0.5s ease-in-out 2;
}
@keyframes flash {
  0% { background-color: transparent; }
  50% { background-color: #ffeb3b; }
  100% { background-color: transparent; }
}
`

function StockPriceList() {
  const [stockList, setStockList] = useState([])
  const listRef = useRef(null)

  // 模拟请求接口的逻辑和之前一致
  const fetchStockData = async () => {
    const res = await fetch('/your/api/url')
    const newStockList = await res.json()
    setStockList(newStockList)
  }

  useEffect(() => {
    fetchStockData()
    const timer = setInterval(fetchStockData, 5000)
    return () => clearInterval(timer)
  }, [])

  // 初始化MutationObserver监听DOM变化
  useEffect(() => {
    if (!listRef.current) return

    const observer = new MutationObserver((mutations) => {
      mutations.forEach(mutation => {
        // 只监听价格span的文本变化
        if (mutation.type === 'characterData' && mutation.target.parentElement.tagName === 'SPAN') {
          const spanEl = mutation.target.parentElement
          spanEl.classList.add('price-flash')
          setTimeout(() => {
            spanEl.classList.remove('price-flash')
          }, 2000)
        }
      })
    })

    observer.observe(listRef.current, {
      subtree: true, // 监听所有后代元素
      characterData: true, // 监听文本内容变化
      childList: true
    })

    // 组件卸载时断开监听避免内存泄漏
    return () => observer.disconnect()
  }, [])

  return (
    <div ref={listRef}>
      <style>{priceFlashCss}</style>
      {stockList.map(item => (
        <div key={item.id}>
          {item.ticker} Price : <span>{item.price}</span>
        </div>
      ))}
    </div>
  )
}

注意事项

该方案需要额外维护DOM监听逻辑,性能略低于状态驱动的方案,仅推荐在无法拿到原始数据做对比的场景使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 23:27:03