从零开始用 React 构建音乐播放器应用程序🔥🎶

2025-05-24

从零开始用 React 构建音乐播放器应用程序🔥🎶

在本博客教程中,我们将从头开始使用 react.js 设置和构建音乐播放器应用程序。

如果您想了解有关 React 的更多信息,这里有一篇专门为您准备的文章。

从零开始做出反应

我们将从零开始构建 UI 及其功能。不过,在开始之前,应用程序的最终版本应该是这样的。

演示

您还可以查看此应用程序的最终实时版本。

音乐播放器应用程序-react.netlify.app

那么,事不宜迟,让我们开始这个派对吧。

安装 React 应用程序

让我们开始我们的第一个 React 应用程序。如果你的系统上还没有安装 Node.js,那么首先需要安装它。访问 Node.js 官方网站并安装正确且合适的版本。我们需要 Node.js 是因为我们可以利用 Node 包管理器或 NPM 功能。

mkdir

现在,创建一个空白文件夹,并在您选择的代码编辑器中打开它。在本教程中,我将使用 VScode。下一步,让我们打开集成终端并输入 npx create-react-app music-payer-react-app 。此命令将在当前目录中创建应用程序,该应用程序将命名为 music-payer-react-app。

脚本插图

创建 React 应用

安装通常只需几分钟。通常,下载软件包时,我们会使用 npm 将它们下载到项目中,但这里我们使用 npx,即软件包运行器,它会帮我们下载并配置所有内容,以便我们能够使用一个很棒的模板开始开发。现在,是时候启动我们的开发服务器了,只需输入 npm start,它就会自动在浏览器中打开 react-app。

反应应用程序

样板模板的初始显示效果如下。现在,我们来研究一下 create-react-app 提供的文件和文件夹结构。其中有一个名为 node module 的文件夹,其中包含我们所有的 node 依赖项。然后是 public 文件夹,其中唯一重要的就是 index.html 文件。因此,这看起来像是一个标准的 HTML 文件,包含 head、body 和 meta 标签。您会注意到 body 标签内有一个 id 为 root 的 div,后面跟着 fallback noscript 标签,该标签只有在用户浏览器禁用 JavaScript 时才会显示。

<!--index.html-->

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <title>React practice</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>

  </body>
</html>

Enter fullscreen mode Exit fullscreen mode

所以你可能想知道内容从哪里来。记住,我们所有的源代码都包含在 source 或 src 文件夹中,React 会将其注入到根 div 元素中。让我们看一下 src 文件夹,其中包含一些样式表、JavaScript 文件和 SVG 文件。

src 目录

现在,转到我们的 App.js 文件

// App.js

import logo from './logo.svg';
import './App.css';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}
export default App;

Enter fullscreen mode Exit fullscreen mode

在本例中,我们只是使用标准 JavaScript 从 react 导入 react,从 logo 导入 logo。接下来,我们有一个名为 APP 的普通 JavaScript 函数,这个函数在 React 中被称为函数组件,它返回一个看起来像 HTML 但实际上是 jsx 的 react 元素,如您所见,有一个 div 标签,其 className 为 APP,我们不能单独说 class,因为 class 是 JavaScript 中的保留字,所以在 jsx 中我们必须使用 className。接下来,我们有标题和图像,请注意,在图像源上有我们的徽标,它实际上是我们在顶部导入的一个 JavaScript 变量,因此为了在 JSX 中使用 JavaScript,我们必须用花括号将其括起来,然后我们有一个段落、一个锚标记,这就是这个组件的全部内容。

注意:由于导出功能,我们可以提取组件并将其放置在网页上。export 出现在 app.js 文件的底部,表示我们正在导出 App 函数。

那么,现在让我们看一下 index.js 文件。

// index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);
reportWebVitals();

Enter fullscreen mode Exit fullscreen mode

所以,在这种情况下,我们再次从 react 导入 react,这次我们还导入了 react-dom,然后导入 CSS 样式表文件,最后,我们从 App.js 导入 App(这是我们刚刚讨论过的文件),还有 service worker,它用于让你的应用程序完全离线工作。然后我们调用 ReactDom.render,它接受两个参数。第一个参数是 jsx 对象,在 jsx 中我们可以包含用户定义的组件,因此 react strict mode 是一个 react 定义的组件,而 App 是一个用户定义的组件,第二个参数是 document.getElementById('root'),它指向 index.html 文件中的根 div,也是我们访问网页内容的方式。

注意:ReactDom 将我们的内容呈现到位于 index.html 文件的根 div 中。

创建音乐播放器应用程序。

让我们从头开始在 React 中创建一个简单的音乐播放器应用程序,但首先,让我们创建最终应用程序的原型或思维导图。

所以,我们的最终应用程序看起来会像这样。

原型

在开始构建项目之前,我们必须先清理一下 create-react-app 提供的一些文件。清理完成后,你的 src 文件应该如下所示。

文件夹结构

现在,在公共文件夹中,创建另一个名为 songs 和 songs_images 的文件夹,并在该 songs 文件夹中添加所有您想要的歌曲,并在 songs_images 中添加该歌曲的封面图片。

歌曲文件夹

歌曲图像文件夹

所有歌曲和歌曲图片均可从此处下载

React-music-player-app(Github)

现在,转到你的 App.js 文件并创建一个 useState() 钩子,因为这个钩子将使我们能够将状态集成到我们的函数组件中。与类组件中的状态不同,useState() 不适用于对象值。如有必要,我们可以直接使用原语,并为多个变量创建多个 React 钩子。

const [state,setState] = useState(initialState);

useState() 钩子

React 中的 Hook 必须始终在函数顶部声明。这也有助于在组件的所有渲染之间保存状态。现在,将 songs 的初始化代码修改如下图所示:

usestate 演示

将以下代码复制并粘贴到您的 App.js 文件中。


// App.js

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

const App=()=> {

const [songs,setSongs] = useState([
    {
        "title": "$orries",
        "artist": "Peachy!",
        "album": " Shiloh",
        "track": "$orries",
        "year": "1",
        "img_src": "./songs_images/$orries_Cover (front)_e.jpg",
        "src": "./songs/$orries.mp3"
    },
    {
        "title": "[oops]",
        "artist": "potsu",
        "album": "[oops]",
        "track": "1",
        "year": "",
        "img_src": "./songs_images/[oops]_Cover (front)_e.jpg",
        "src": "./songs/[oops].mp3"
    },
    {
        "title": "5:32pm",
        "artist": "The Deli",
        "album": "Vibes 2",
        "track": "12",
        "year": "",
        "img_src": "./songs_images/5 32pm_Cover (front)_e.jpg",
        "src": "./songs/5 32pm.mp3"
    },
    {
        "title": "88 Keys",
        "artist": "Oatmello",
        "album": "Snapshots",
        "track": "3",
        "year": "",
        "img_src": "./songs_images/88 Keys_Cover (front)_e.jpg",
        "src": "./songs/88 Keys.mp3"
    },
    {
        "title": "Affection",
        "artist": "Jinsang",
        "album": "Life",
        "track": "15",
        "year": "",
        "img_src": "./songs_images/Affection_Cover (front)_e.jpg ",
        "src": "./songs/Affection.mp3"
    },
    {
        "title": "Again",
        "artist": "Wun Two",
        "album": "Penthouse",
        "track": "4",
        "year": "",
        "img_src": "./songs_images/Again_Cover (front)_e.jpg",
        "src": "./songs/Again.mp3"
    },
    {
        "title": "Alone and Lonely",
        "artist": "prxz",
        "album": " Shiloh Dynasty",
        "track": "Love Wounds",
        "year": "2",
        "img_src": "./songs_images/Alone and Lonely_Cover (front)_e.jpg",
        "src": "./songs/Alone and Lonely.mp3"
    },
    {
        "title": "Baby You're Worth It",
        "artist": "Kina",
        "album": "Baby You're Worth It",
        "track": "1",
        "year": "",
        "img_src": "./songs_images/Baby You're Worth It_Cover (front)_e.jpg",
        "src": "./songs/Baby You're Worth It.mp3"
    },
    {
        "title": "Backpack City",
        "artist": "Flovry",
        "album": " tender spring",
        "track": "Ages Ago",
        "year": "4",
        "img_src": "./songs_images/ ",
        "src": "./songs/Backpack City.mp3"
    },
    {
        "title": "Beauty",
        "artist": "eyeroze",
        "album": "Heartless",
        "track": "4",
        "year": "",
        "img_src": "./songs_images/Beauty_Cover (front)_e.jpg",
        "src": "./songs/Beauty.mp3"
    },
    {
        "title": "Better Than He Can",
        "artist": "Jennifer Flores",
        "album": " Shiloh Dynasty",
        "track": " LofiCentral",
        "year": "All My Love",
        "img_src": "./songs_images/Better Than He Can_Cover (front)_e.jpg",
        "src": "./songs/Better Than He Can.mp3"
    },
    {
        "title": "Break My Heart Again",
        "artist": "90degrees",
        "album": "Break My Heart Again",
        "track": "1",
        "year": "",
        "img_src": "./songs_images/Break My Heart Again_Cover (front)_e.jpg",
        "src": "./songs/Break My Heart Again.mp3"
    },
    {
        "title": "Brightness",
        "artist": "eyeroze",
        "album": "Heartless",
        "track": "15",
        "year": "",
        "img_src": "./songs_images/Brightness_Cover (front)_e.jpg",
        "src": "./songs/Brightness.mp3"
    },
    {
        "title": "Call me",
        "artist": "90sFlav",
        "album": "Collection",
        "track": "1",
        "year": "",
        "img_src": "./songs_images/Call me_Cover (front)_e.jpg",
        "src": "./songs/Call me.mp3"
    },
    {
        "title": "Can We Kiss Forever?",
        "artist": "Kina",
        "album": " Adriana Proenza",
        "track": "Can We Kiss Forever?",
        "year": "1",
        "img_src": "./songs_images/Can We Kiss Forever _Cover (front)_e.jpg",
        "src": "./songs/Can We Kiss Forever .mp3"
    },

]);

 return (
    <div className="App">
      MusicPlayer
    </div>
  );
}

export default App;

Enter fullscreen mode Exit fullscreen mode

现在,在 src 文件夹中,创建另一个名为 components 的文件夹,并在该文件夹中创建三个组件:Player、PlayerControls 和 PlayerDetails。

成分

添加组件后,就该将 fontawesome 库安装到项目中了。只需将以下代码复制并粘贴到 package.json 和依赖项中,然后在集成终端中输入 npm install 即可。

    "@fortawesome/fontawesome-svg-core": "^1.2.32",
    "@fortawesome/free-solid-svg-icons": "^5.15.1",
    "@fortawesome/react-fontawesome": "^0.1.12",

Enter fullscreen mode Exit fullscreen mode

包.json

另外,让我们在公共文件夹内的 index.html 文件中导入缩小版本的 fontawesome css CDN 链接。

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"/>

Enter fullscreen mode Exit fullscreen mode

您的最终 index.html 文件应该看起来与此有点类似。

// index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />

    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
    />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />

    <title>Lofi Muisc Player</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>

Enter fullscreen mode Exit fullscreen mode

让我们转到 PlayerControl 组件并添加以下代码。此组件将显示音乐播放器的控件。


// PlayerControls.js

import React from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
  faPlay,
  faPause,
  faForward,
  faBackward,
} from "@fortawesome/free-solid-svg-icons";

function PlayerControls(props) {
  return (
    <div className="music-player--controls">
      <button className="skip-btn" onClick={() => props.SkipSong(false)}>
        <FontAwesomeIcon icon={faBackward} />
      </button>
      <button
        className="play-btn"
        onClick={() => props.setIsPlaying(!props.isPlaying)}
      >
        <FontAwesomeIcon icon={props.isPlaying ? faPause : faPlay} />
      </button>
      <button className="skip-btn" onClick={() => props.SkipSong()}>
        <FontAwesomeIcon icon={faForward} />
      </button>
    </div>
  );
}

export default PlayerControls;

Enter fullscreen mode Exit fullscreen mode

现在,让我们来看看 PlayerDetails 组件。这个组件会列出歌曲的所有详细信息


// PlayerDetails.js

import React from "react";

function PlayerDetails(props) {
  return (
    <div className="music-player--details">
      <div className="details-img">
        <img
          className="details-img--image"
          src={props.song.img_src}
          alt={props.song.title}
        />
      </div>
      <div class="range"></div>
      <div className="artist-info">
        <h3 className="details-title">{props.song.title}</h3>
        <h4 className="details-artist">{props.song.artist}</h4>
        <div class="line"></div>
      </div>
    </div>
  );
}

export default PlayerDetails;

Enter fullscreen mode Exit fullscreen mode

最后,是时候更新我们的 Player 组件了。这将是我们应用程序运行的主要组件。第一步是导入 useState()、useRef()、useEffect() 以及我们之前创建的组件,并将其导入到我们的播放器组件中。


// Player.js

import React,{useState,useRef,useEffect} from 'react';
import PlayerDetails from "./PlayerDetails";
import PlayerControls from "./PlayerControls";

Enter fullscreen mode Exit fullscreen mode

我们之前讨论过 useState() 钩子。

反应使用状态

让我们深入研究 useEffect() 钩子。通过使用此钩子,你告诉 React 你的组件需要在渲染后执行某些操作。React 会记住你传递的函数(我们将其称为“效果”),并在执行 DOM 更新后调用它。为此,我们设置了文档标题,但我们也可以执行数据获取或调用一些其他命令式 API。将 useEffect() 放置在组件内部使我们可以直接从效果中访问 c​​ount 状态变量(或任何 props)。我们不需要特殊的 API 来读取它 - 它已经在函数作用域中。钩子拥抱 JavaScript 闭包,并避免在 JavaScript 已经提供解决方案的情况下引入特定于 React 的 API。useEffect() 钩子有点类似于我们所知道的类组件的生命周期方法。它在组件的每次渲染之后运行,包括初始渲染。因此,它可以被认为是 componentDidMount、componentDidUpdate 和 componentWillUnmount 的组合。如果我们想要控制 effect 何时运行(仅在初始渲染时,或仅在特定状态变量发生变化时),我们可以将依赖项传递给 effect 来实现。此钩子还提供了一个清理选项,允许在组件销毁前清理资源。effect 的基本语法是: useEffect(didUpdate) 。

useRef

我们深入研究了 useState() 和 useEffect() hooks。那么,你可能会好奇“什么是 useRef() hooks?”

此钩子仅返回一个可变的 ref 对象,其 .current 属性(initialValue)为传入的参数。返回的对象将在组件的整个生命周期内保留。

const refContainer = useRef(initialValue);

Enter fullscreen mode Exit fullscreen mode

让我们回到代码中。在 Player 组件内部,利用 useState() 和 useRef() 这两个钩子。


// Player.js

const audioElement = useRef(null);
const [isPlaying, setIsPlaying] = useState(false);

Enter fullscreen mode Exit fullscreen mode

之后,利用 useEffect() 钩子实现暂停和播放功能


useEffect(() => {
        if (isPlaying) {
            audioElement.current.play();
        } else {
            audioElement.current.pause();
        }
    });

Enter fullscreen mode Exit fullscreen mode

现在,创建一个实际跳过或转发歌曲的功能。


const SkipSong = (forwards = true) => {
        if (forwards) {
            props.setCurrentSongIndex(() => {
                let temp = props.currentSongIndex;
                temp++;

                if (temp > props.songs.length - 1) {
                    temp = 0;
                }

                return temp;
            });
        } else {
            props.setCurrentSongIndex(() => {
                let temp = props.currentSongIndex;
                temp--;

                if (temp < 0) {
                    temp = props.songs.length - 1;
                }

                return temp;
            });
        }
    }

Enter fullscreen mode Exit fullscreen mode

最后,在return语句中添加以下代码。

<p>
<div className="text-anim">
   <strong>Upcoming Song:</strong>
</div>
<div className="nextsong-details">
   <img
   src={props.songs[props.nextSongIndex].img_src}
   alt={props.songs[props.nextSongIndex].title}
   style={{ width: "4em", height: "auto" }}
   />
   <p>
      <b>{props.songs[props.nextSongIndex].title} </b>&nbsp; by &nbsp;
      <b>{props.songs[props.nextSongIndex].artist}</b>
      {/* &nbsp; from album
      &nbsp; */}
      {/* <b>{props.songs[props.nextSongIndex].album}</b> */}
   </p>
</div>
</p>
<div className="music-player">
   <audio
      src={props.songs[props.currentSongIndex].src}
      ref={audioElement}
      ></audio>
   <PlayerDetails song={props.songs[props.currentSongIndex]} />
   <PlayerControls
      isPlaying={isPlaying}
      setIsPlaying={setIsPlaying}
      SkipSong={SkipSong}
      />
   <div class="player__footer">
      <ul class="list list--footer">
         <li>
            <a href="#" class="list__link">
            <i class="fa fa-heart-o"></i>
            </a>
         </li>
         <li>
            <a href="#" class="list__link">
            <i class="fa fa-random"></i>
            </a>
         </li>
         <li>
            <a href="#" class="list__link">
            <i class="fa fa-undo"></i>
            </a>
         </li>
         <li>
            <a href="#" class="list__link">
            <i class="fa fa-ellipsis-h"></i>
            </a>
         </li>
      </ul>
   </div>
   {/* 
   <h4>Lofi Music Player React </h4>
   */}
</div>

Enter fullscreen mode Exit fullscreen mode

返回

您的最终播放器组件应该看起来像这样。

//Player.js

import React, { useState, useRef, useEffect } from "react";
import PlayerDetails from "./PlayerDetails";
import PlayerControls from "./PlayerControls";

function Player(props) {
  const audioElement = useRef(null);
  const [isPlaying, setIsPlaying] = useState(false);

  useEffect(() => {
    if (isPlaying) {
      audioElement.current.play();
    } else {
      audioElement.current.pause();
    }
  });

  const SkipSong = (forwards = true) => {
    if (forwards) {
      props.setCurrentSongIndex(() => {
        let temp = props.currentSongIndex;
        temp++;

        if (temp > props.songs.length - 1) {
          temp = 0;
        }

        return temp;
      });
    } else {
      props.setCurrentSongIndex(() => {
        let temp = props.currentSongIndex;
        temp--;

        if (temp < 0) {
          temp = props.songs.length - 1;
        }

        return temp;
      });
    }
  };

  return (
    <>
      <p>
        <div className="text-anim">
          <strong>Upcoming Song:</strong>
        </div>

        <div className="nextsong-details">
          <img
            src={props.songs[props.nextSongIndex].img_src}
            alt={props.songs[props.nextSongIndex].title}
            style={{ width: "4em", height: "auto" }}
          />
          <p>
            <b>{props.songs[props.nextSongIndex].title} </b>&nbsp; by &nbsp;
            <b>{props.songs[props.nextSongIndex].artist}</b>
            {/* &nbsp; from album
            &nbsp; */}
            {/* <b>{props.songs[props.nextSongIndex].album}</b> */}
          </p>
        </div>
      </p>
      <div className="music-player">
        <audio
          src={props.songs[props.currentSongIndex].src}
          ref={audioElement}
        ></audio>
        <PlayerDetails song={props.songs[props.currentSongIndex]} />

        <PlayerControls
          isPlaying={isPlaying}
          setIsPlaying={setIsPlaying}
          SkipSong={SkipSong}
        />

        <div class="player__footer">
          <ul class="list list--footer">
            <li>
              <a href="#" class="list__link">
                <i class="fa fa-heart-o"></i>
              </a>
            </li>

            <li>
              <a href="#" class="list__link">
                <i class="fa fa-random"></i>
              </a>
            </li>

            <li>
              <a href="#" class="list__link">
                <i class="fa fa-undo"></i>
              </a>
            </li>

            <li>
              <a href="#" class="list__link">
                <i class="fa fa-ellipsis-h"></i>
              </a>
            </li>
          </ul>
        </div>

        {/* <h4>Lofi Music Player React </h4> */}
      </div>
    </>
  );
}
export default Player;

Enter fullscreen mode Exit fullscreen mode

最后,是时候更新我们的 App.js 文件了。在 App.js 中添加双状态。

const [currentSongIndex,setCurrentSongIndex] = useState(0);
const [nextSongIndex,setNextSongIndex] = useState(currentSongIndex + 1);

Enter fullscreen mode Exit fullscreen mode

创建一个功能,当当前歌曲结束时自动播放下一首歌曲。


useEffect(()=>{
  setNextSongIndex(()=>{
  if (currentSongIndex + 1 >songs.length - 1 ){
    return 0;
  } else{
    return currentSongIndex + 1;
  }
});
},[currentSongIndex])

Enter fullscreen mode Exit fullscreen mode

最后,导入您的 Player 组件并使用以下道具返回它。

<Player currentSongIndex={currentSongIndex} setCurrentSongIndex={setCurrentSongIndex} nextSongIndex={nextSongIndex} songs={songs} />

Enter fullscreen mode Exit fullscreen mode

最后,App 组件已锁定并准备就绪。App 组件内部的最终代码应如下所示。


// App.js

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

const App=()=> {

const [songs,setSongs] = useState([
    {
        "title": "$orries",
        "artist": "Peachy!",
        "album": " Shiloh",
        "track": "$orries",
        "year": "1",
        "img_src": "./songs_images/$orries_Cover (front)_e.jpg",
        "src": "./songs/$orries.mp3"
    },
    {
        "title": "[oops]",
        "artist": "potsu",
        "album": "[oops]",
        "track": "1",
        "year": "",
        "img_src": "./songs_images/[oops]_Cover (front)_e.jpg",
        "src": "./songs/[oops].mp3"
    },
    {
        "title": "5:32pm",
        "artist": "The Deli",
        "album": "Vibes 2",
        "track": "12",
        "year": "",
        "img_src": "./songs_images/5 32pm_Cover (front)_e.jpg",
        "src": "./songs/5 32pm.mp3"
    },
    {
        "title": "88 Keys",
        "artist": "Oatmello",
        "album": "Snapshots",
        "track": "3",
        "year": "",
        "img_src": "./songs_images/88 Keys_Cover (front)_e.jpg",
        "src": "./songs/88 Keys.mp3"
    },
    {
        "title": "Affection",
        "artist": "Jinsang",
        "album": "Life",
        "track": "15",
        "year": "",
        "img_src": "./songs_images/Affection_Cover (front)_e.jpg ",
        "src": "./songs/Affection.mp3"
    },
    {
        "title": "Again",
        "artist": "Wun Two",
        "album": "Penthouse",
        "track": "4",
        "year": "",
        "img_src": "./songs_images/Again_Cover (front)_e.jpg",
        "src": "./songs/Again.mp3"
    },
    {
        "title": "Alone and Lonely",
        "artist": "prxz",
        "album": " Shiloh Dynasty",
        "track": "Love Wounds",
        "year": "2",
        "img_src": "./songs_images/Alone and Lonely_Cover (front)_e.jpg",
        "src": "./songs/Alone and Lonely.mp3"
    },
    {
        "title": "Baby You're Worth It",
        "artist": "Kina",
        "album": "Baby You're Worth It",
        "track": "1",
        "year": "",
        "img_src": "./songs_images/Baby You're Worth It_Cover (front)_e.jpg",
        "src": "./songs/Baby You're Worth It.mp3"
    },
    {
        "title": "Backpack City",
        "artist": "Flovry",
        "album": " tender spring",
        "track": "Ages Ago",
        "year": "4",
        "img_src": "./songs_images/ ",
        "src": "./songs/Backpack City.mp3"
    },
    {
        "title": "Beauty",
        "artist": "eyeroze",
        "album": "Heartless",
        "track": "4",
        "year": "",
        "img_src": "./songs_images/Beauty_Cover (front)_e.jpg",
        "src": "./songs/Beauty.mp3"
    },
    {
        "title": "Better Than He Can",
        "artist": "Jennifer Flores",
        "album": " Shiloh Dynasty",
        "track": " LofiCentral",
        "year": "All My Love",
        "img_src": "./songs_images/Better Than He Can_Cover (front)_e.jpg",
        "src": "./songs/Better Than He Can.mp3"
    },
    {
        "title": "Break My Heart Again",
        "artist": "90degrees",
        "album": "Break My Heart Again",
        "track": "1",
        "year": "",
        "img_src": "./songs_images/Break My Heart Again_Cover (front)_e.jpg",
        "src": "./songs/Break My Heart Again.mp3"
    },
    {
        "title": "Brightness",
        "artist": "eyeroze",
        "album": "Heartless",
        "track": "15",
        "year": "",
        "img_src": "./songs_images/Brightness_Cover (front)_e.jpg",
        "src": "./songs/Brightness.mp3"
    },
    {
        "title": "Call me",
        "artist": "90sFlav",
        "album": "Collection",
        "track": "1",
        "year": "",
        "img_src": "./songs_images/Call me_Cover (front)_e.jpg",
        "src": "./songs/Call me.mp3"
    },
    {
        "title": "Can We Kiss Forever?",
        "artist": "Kina",
        "album": " Adriana Proenza",
        "track": "Can We Kiss Forever?",
        "year": "1",
        "img_src": "./songs_images/Can We Kiss Forever _Cover (front)_e.jpg",
        "src": "./songs/Can We Kiss Forever .mp3"
    },

]);


const [currentSongIndex,setCurrentSongIndex] = useState(0);
const [nextSongIndex,setNextSongIndex] = useState(currentSongIndex + 1);


useEffect(()=>{
  setNextSongIndex(()=>{
  if (currentSongIndex + 1 >songs.length - 1 ){
    return 0;
  } else{
    return currentSongIndex + 1;
  }
});
},[currentSongIndex])

  return (
    <div className="App">
    <Player currentSongIndex={currentSongIndex} setCurrentSongIndex={setCurrentSongIndex} nextSongIndex={nextSongIndex} songs={songs} />
    </div>
  );
}

export default App;

Enter fullscreen mode Exit fullscreen mode

完整文章请见此处 => https://aviyel.com/post/1193

编码愉快!!

如果您是项目维护者、贡献者或只是开源爱好者,请关注@aviyelHQ或在 Aviyel 上注册以获得早期访问权限。

加入 Aviyel 的 Discord => Aviyel 的世界

推特 =>[ https://twitter.com/AviyelHq ]

文章来源:https://dev.to/aviyel/building-a-music-player-application-in-react-js-3ngd
PREV
以你从未见过的方式解释现代 JavaScript🔥
NEXT
从零开始构建一个 MERN 堆栈的简单博客网站🔥