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

Next.js应用中state/props混淆导致UI不更新问题求助

问题分析与修复方案

你的问题核心是两点:

  1. _app.js 里的曲目数据只在初始加载时获取一次,之后没有更新机制,导致传给Player的trackName一直是初始值;
  2. Player组件里虽然通过轮询获取了新曲目并更新了自身state,但渲染时仍使用props的trackName,所以state的变化无法反映到UI上。

下面给出两种修复方案,按需选择:

方案一:Player组件自管理状态(推荐,内聚性更强)

让Player组件自己维护曲目状态,初始化时用_app传入的初始值,之后轮询API更新自身state,渲染时使用state中的值:

import React from 'react'
import ReactPlayer from 'react-player'

export default class Player extends React.Component {
  constructor(props) {
    super(props)
    // 初始化state,用props的初始曲目名
    this.state = {
      trackName: props.trackName
    }
    this.intervalId = null
  }

  componentDidMount() {
    // 每秒轮询API
    this.intervalId = setInterval(() => {
      fetch("https://kchungradio.airtime.pro/api/live-info-v2")
        .then(response => {
          if (!response.ok) {
            return Promise.reject(response)
          }
          return response.json()
        })
        .then(data => {
          const newTrackName = data.tracks.current.name
          // 仅当曲目变化时更新state,避免不必要重渲染
          if (newTrackName !== this.state.trackName) {
            this.setState({ trackName: newTrackName })
          }
        })
        .catch(error => {
          if (typeof error.json === "function") {
            error.json().then(jsonError => {
              console.log(jsonError)
            }).catch(genericError => {
              console.log("API通用错误")
              console.log(error.statusText)
            })
          } else {
            console.log("Fetch错误")
            console.log(error)
          }
        })
    }, 1000)
  }

  componentWillUnmount() {
    // 组件卸载时清除定时器,防止内存泄漏
    if (this.intervalId) {
      clearInterval(this.intervalId)
    }
  }

  render() {
    const { url } = this.props
    const { trackName } = this.state
    // React中使用className替代class
    return (
      <div className="player">
        <ReactPlayer url={url} />
        <div>{trackName}</div>
      </div>
    )
  }
}

_app.js 无需修改,保持原样即可,它负责提供初始的曲目数据。

方案二:在_app.js中全局管理状态

如果你的应用有多个组件需要使用曲目信息,可以把状态提升到_app.js,Player组件仅做展示:

修改后的_app.js

import React, { useState, useEffect } from 'react'
import Player from '../components/Player'

function MyApp({ Component, pageProps, initialTracks }) {
  const url = "https://kchungradio.out.airtime.pro/kchungradio_a"
  const [tracks, setTracks] = useState(initialTracks)

  useEffect(() => {
    // 每秒轮询更新曲目数据
    const intervalId = setInterval(async () => {
      try {
        const res = await fetch("https://kchungradio.airtime.pro/api/live-info-v2")
        if (!res.ok) {
          throw new Error('API响应失败')
        }
        const data = await res.json()
        setTracks(data.tracks)
      } catch (error) {
        console.error('获取曲目信息失败:', error)
      }
    }, 1000)

    // 卸载时清除定时器
    return () => clearInterval(intervalId)
  }, [])

  return (
    <>
      <Player
        url={url}
        trackName={tracks.current.name}
      />
      <Component {...pageProps} />
    </>
  )
}

MyApp.getInitialProps = async (appContext) => {
  const res = await fetch("https://kchungradio.airtime.pro/api/live-info-v2")
  const data = await res.json()
  return { initialTracks: data.tracks }
}

export default MyApp

简化后的Player组件

import React from 'react'
import ReactPlayer from 'react-player'

export default function Player({ url, trackName }) {
  return (
    <div className="player">
      <ReactPlayer url={url} />
      <div>{trackName}</div>
    </div>
  )
}

关键注意点

  • React中类组件的HTML类名必须用className,不能用原生HTML的class,否则会触发警告;
  • 无论哪种方案,都要记得在组件卸载时清除定时器,避免内存泄漏;
  • 方案一中添加了曲目变化判断,只有当新曲目和当前state不同时才更新,减少不必要的组件重渲染。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 10:57:42