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

React项目:播放列表增删曲目后总时长不更新问题求助

问题:React播放列表总时长动态计算功能无响应排查

背景

我是React新手,正在Codecademy Pro「全栈工程师」路径中完成第一个完整项目:基于Spotify API开发React网站,实现获取曲目并创建播放列表保存至Spotify的功能。

我希望在App→Playlist组件中添加播放列表总时长动态计算功能:每次添加或移除曲目时,总时长自动更新。目前已完成以下工作:

  • 在Spotify.js中获取了duration_ms数据
  • 在Playlist.js中实现了单首歌曲时长(分钟格式)的展示
  • 在App.js中通过useState定义了totalTime和setTotalTime,默认值为'0:0'
  • 将相关props从App.js传递到Playlist.js,再传递给新建的Time函数组件

问题

在Time.js中,我渲染了展示totalTime的HTML元素,并使用useCallback定义了一个更新totalTime状态的函数,该函数应在播放列表tracks状态变化时触发。但目前功能无响应!DevTools显示tracks已传入Time组件,且函数单独传入硬编码参数测试有效,怀疑问题出在props命名上。


相关代码

App.js

import React, { useState, useCallback, useEffect } from "react";
import "./App.css";

import Playlist from "../Playlist/Playlist";
import SearchBar from "../SearchBar/SearchBar";
import SearchResults from "../SearchResults/SearchResults";
import Spotify from "../../util/Spotify";

const App = () => {
  const [searchResults, setSearchResults] = useState([]);
  const [playlistName, setPlaylistName] = useState("New Playlist");
  const [playlistTracks, setPlaylistTracks] = useState([]);
  const [totalTime, setTotalTime] = useState(0+':'+0);

  const search = useCallback((term) => {
    Spotify.search(term).then(setSearchResults);
  }, []);

  const addTrack = useCallback(
    (track) => {
      if (playlistTracks.some((savedTrack) => savedTrack.id === track.id))
        return;

      setPlaylistTracks((prevTracks) => [...prevTracks, track]);
    },
    [playlistTracks]
  );

  const removeTrack = useCallback((track) => {
    setPlaylistTracks((prevTracks) =>
      prevTracks.filter((currentTrack) => currentTrack.id !== track.id)
    );
  }, []);

  const updatePlaylistName = useCallback((name) => {
    setPlaylistName(name);
  }, []);

  const savePlaylist = useCallback(() => {
    const trackUris = playlistTracks.map((track) => track.uri);
    Spotify.savePlaylist(playlistName, trackUris).then(() => {
      setPlaylistName("New Playlist");
      setPlaylistTracks([]);
    });
  }, [playlistName, playlistTracks]);

  return (
    <div>
      <h1>
        Ja<span className="highlight">mmm</span>ing
      </h1>
      <div className="App">
        <SearchBar onSearch={search} />
        <div className="App-playlist">
          <SearchResults searchResults={searchResults} onAdd={addTrack} />
          <Playlist
            playlistName={playlistName}
            playlistTracks={playlistTracks}
            onNameChange={updatePlaylistName}
            onRemove={removeTrack}
            onSave={savePlaylist}
            totalTime={totalTime}
            handleTime = {setTotalTime}
          />
        </div>
      </div>
    </div>
  );
};

export default App;

Playlist.js

import React, { useCallback, useEffect } from "react";

import "./Playlist.css";

import TrackList from "../TrackList/TrackList";
import Time from "../Time/Time";

const Playlist = (props) => {
  const handleNameChange = useCallback(
    (event) => {
      props.onNameChange(event.target.value);
    },
    [props.onNameChange]
  );

  return (
    <div className="Playlist">
      <input onChange={handleNameChange} defaultValue="New Playlist" />
      <TrackList
        tracks={props.playlistTracks}
        isRemoval={true}
        onRemove={props.onRemove}
      />
<br/>
       <Time onChange={props.setTotalTime} totalTime={props.totalTime} tracks= {props.playlistTracks} />
      <button className="Playlist-save" onClick={props.onSave}>
        SAVE TO SPOTIFY
      </button>
    </div>
  );
};

export default Playlist;

Time.js

import React, {useCallback} from "react";

const Time =(props) => {
      const handleTotalTime = useCallback(()=> {
          let totalTime = 0;
            props.tracks.forEach((track)=> {
              totalTime += track.duration_ms;
            })
          const minutes = Math.floor(totalTime/60000);
          const seconds = ((totalTime % 60000) /1000).toFixed(0);
           let newTime = minutes + ':' + (seconds < 10 ? '0': '') + seconds;
        props.setTotalTime(newTime);
        }, [props.tracks]); 

    return (
        <span>Your Playlist Total Time is currently: <input value={props.totalTime} onChange={handleTotalTime} /></span>
    );
}

export default Time;

问题修复方案

1. 修正Props传递的命名错误

  • App.js中传给Playlist的更新函数是handleTime={setTotalTime},但Playlist里传给Time时错误使用了props.setTotalTime,实际应该是props.handleTime
  • Time组件接收的props名也需要对应调整,避免不匹配

2. 修正触发逻辑

原来把计算函数绑定在input的onChange事件上,只会在用户手动修改输入框时触发,正确的做法是用useEffect监听tracks数组的变化,自动执行计算。

修复后的代码

修正Playlist.js中的Time组件props
// 原代码
<Time onChange={props.setTotalTime} totalTime={props.totalTime} tracks= {props.playlistTracks} />
// 修改后
<Time setTotalTime={props.handleTime} totalTime={props.totalTime} tracks={props.playlistTracks} />
重写Time.js的计算逻辑
import React, { useEffect } from "react";

const Time = (props) => {
  useEffect(() => {
    let totalTimeMs = 0;
    props.tracks.forEach((track) => {
      totalTimeMs += track.duration_ms;
    });
    const minutes = Math.floor(totalTimeMs / 60000);
    const seconds = ((totalTimeMs % 60000) / 1000).toFixed(0);
    const newTime = `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;
    props.setTotalTime(newTime);
  }, [props.tracks, props.setTotalTime]);

  return (
    <span>Your Playlist Total Time is currently: {props.totalTime}</span>
  );
};

export default Time;

额外优化

  • 总时长是计算得出的只读值,不需要用input展示,直接用文本渲染即可
  • App.js中totalTime的初始值可以改为'0:00',格式更统一

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 01:15:33