如何使用 React、Spotify 和 Fauna 创建音乐播放列表 Spotify 是什么?Spotify Web API 入门 Fauna 是什么?Fauna DB 入门 构建应用程序 结论

2025-06-07

如何使用 React、Spotify 和 Fauna 构建音乐播放列表

什么是 Spotify?

Spotify Web API 入门

什么是动物区系?

Fauna DB 入门

构建应用程序

结论

与“与动物一起写作”计划相关而撰写。

使用 React、Spotify API 和 Fauna 数据库,我们可以构建个性化的音乐播放列表。

在本文中,我将逐步展示普通开发人员构建此应用程序的步骤。我们将学习如何使用 Spotify Web API 验证用户身份并搜索音乐,同时使用 Fauna 进行数据管理。

什么是 Spotify?

Spotify是一家音乐流媒体服务提供商。它提供了一个开发者工具(Spotify Web API),允许开发者访问用户和音乐相关数据。在本文中,我们将使用 Spotify 进行用户身份验证并将其用作音乐目录。

Spotify Web API 入门

要在应用程序中使用 Spotify Web API:

什么是动物区系?

Fauna 是一个云 API,提供灵活、无服务器且友好的数据库实例。在本文中,我们将使用 Fauna 来存储应用程序中使用的用户和音乐相关数据。

Fauna DB 入门

要使用 Fauna DB:

为我们的应用程序创建数据库

  • 注册后,登录仪表板并单击CREATE DASHBOARD
  • 在出现的表单中,输入数据库名称并选择Classic区域。
  • 单击CREATE按钮。

创建集合

集合是存储在 JSON 对象中的一组相关数据。
在本应用中,我们需要两个集合:usersplaylists
创建这些集合的步骤如下:

  • 点击NEW COLLECTION
  • 输入收藏集名称。
  • 单击SAVE按钮。

对用户和播放列表集合重复上述步骤。

创建索引

索引是除默认引用之外的文档引用,用于增强文档的检索或查找。
对于此应用程序,我们需要两个索引:

  • playlist_for_user检索特定用户创建的所有播放列表。
  • user_by_user_id检索包含特定用户数据的文档。

要创建这些索引:

  • 点击NEW INDEX
  • 对于playlist_for_user索引,请在适用的情况下输入以下详细信息:

    1. 源集合 - 播放列表
    2. 索引名称 - playlist_for_user
    3. 条款 - data.user_id
    4. 独特的 -unchecked
  • 对于user_by_user_id索引,请在适用的情况下输入以下详细信息:

    1. 源集合-用户
    2. 索引名称 - user_by_user_id
    3. 条款 - data.user_id
    4. 独特的 -checked

生成你的 Fauna 密钥

这个密钥将我们的应用程序连接到数据库。
要生成密钥,请执行以下操作:

  • 在左侧导航菜单上,单击“安全”。
  • 点击NEW KEY
  • 输入您的密钥名称。
  • 单击SAVE后将为您生成一个新密钥。

确保将秘密保存在安全的地方。

构建应用程序

设置应用程序

首先,我创建了一个启动应用程序来引导我们的构建过程。
你需要从这个GitHub 仓库克隆它,并在终端中运行以下命令:

git clone https://github.com/wolz-CODElife/Spotify-Playlist-Manager-With-FaunaDB.git
Enter fullscreen mode Exit fullscreen mode

在下载的文件夹中,存在以下目录和文件:

我们将要使用的文件夹和文件是里面的src,它们是:

  1. app.js:此文件将包含视图(路线)。
  2. /utils/models.js:这个文件是我们与 Fauna 数据库进行通信的地方。
  3. /utils/Spotify.js:这个文件是我们与 Spotify Web API 通信的地方。
  4. 里面的文件components是我们用来构建应用程序用户界面的反应组件。

安装项目的依赖项

该应用程序使用了几个 Node 包,您需要安装这些包才能正常运行。要安装这些包,请在终端中运行以下代码:

cd Spotify-Playlist-Manager-With-FaunaDB
npm install
Enter fullscreen mode Exit fullscreen mode

安装 FaunaDB 节点包

为了让我们的应用程序能够与之前创建的数据库进行通信,我们需要安装 Fabia 提供的 Node 包。为此,请打开终端并输入:

npm install faunadb
Enter fullscreen mode Exit fullscreen mode

启动应用程序

要运行我们的应用程序,请打开终端并输入:

npm start
Enter fullscreen mode Exit fullscreen mode

这应该编译 React 应用程序并将其托管在 上http://localhost:3000/,终端应该显示以下结果:

现在,打开您的浏览器并搜索http://localhost:3000,这将在您的浏览器上显示下面的图像。

创建我们的路线

我们的应用程序将有四条路线Index、、CreateMyCollectionsError

  • 索引:用户在身份验证之前启动应用程序时首先看到的主页。
  • 创建:用户认证后搜索音乐并创建自己喜欢的音乐播放列表的页面。
  • MyCollections:用户浏览和管理其保存的播放列表的页面。
  • 错误:当用户进入未定义的路线时出现的页面。

我们将通过将以下代码放入 中来定义我们的路线App.js

import React from 'react'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Index from './components/Index';
import Create from './components/Create';
import Error from './components/Error'
import MyCollections from './components/MyCollections';
const App = () => {
    return (
    <Router>
        <Switch>
        <Route exact path="/" children={<Index />} />
        <Route path="/create" children={<Create />} /> 
        <Route path="/mycollections" children={<MyCollections /> } /> 
        <Route path="*" children={Error} />
        </Switch>
    </Router>
    )
}
export default App;
Enter fullscreen mode Exit fullscreen mode

我们在这里所做的是检查特定path用户,然后将相关组件呈现为children道具Route

获取 Spotify 数据

我们需要三个函数:getAccessTokengetUserIdsearch

  • getAccessToken:此函数向 Spotify 授权 API 发送请求,如果用户接受或授权 Spotify 与我们的应用程序共享他/她的数据,Spotify 将返回一个,accesstoken我们的应用程序稍后可以使用它来安全地向其他 Spotify API 路由发出请求。
  • getUserId:此函数向 Spotify 发送请求,如果accessToken通过身份验证,Spotify 会将用户数据返回给我们的应用程序。
  • search:此函数发送带有参数的请求term,Spotify 将返回符合term用户搜索的音乐曲目。
const clientId = "YOUR-SPOTIFY-CLIENT-ID"
const redirectUri = encodeURIComponent("http://localhost:3000/")
const scopes = encodeURIComponent("user-read-private user-read-email playlist-modify-public")
let accessToken
const Spotify = {
    getAccessToken : () => {
        if(localStorage.getItem('accessToken')){
            return JSON.parse(localStorage.getItem('accessToken'))
        }
        accessToken = window.location.hash
        .substring(1)
        .split('&')
        .reduce((initial, item) => {
            let parts = item.split('=')
            initial[parts[0]] = decodeURIComponent(parts[1])
            return initial
        }, {}).access_token
        if (accessToken) {            
            localStorage.setItem('accessToken', JSON.stringify(accessToken))
            return accessToken
        }
        else {
            const accessUrl = `https://accounts.spotify.com/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scopes}&response_type=token`
            window.location = accessUrl
        }
    },
    getUserId: () => {
        accessToken = Spotify.getAccessToken()
        if (accessToken) {
            const headers = { Authorization: `Bearer ${accessToken}` }
            return fetch("https://api.spotify.com/v1/me", { headers: headers })
            .then(response => response.json())
            .then(jsonResponse => {            
                if (jsonResponse) {
                    const { id, display_name, email, external_urls, images } = jsonResponse
                    const profile = {
                        user_id: id,
                        email: email,
                        name: display_name,
                        image: images[0].url,
                        url: external_urls.spotify
                    }
                    return profile
                }
            })
        }
    },
    search : (term) => {
        accessToken = Spotify.getAccessToken()
        if (accessToken) {
            const headers = {Authorization: `Bearer ${accessToken}`}
            return fetch(`https://api.spotify.com/v1/search?type=track&q=${term}`, {headers: headers})
            .then(response => { return response.json() })
            .then(jsonResponse => {
                if (!jsonResponse.tracks) {
                    return []
                }
                return jsonResponse.tracks.items.map(track => ({
                    id: track.id,
                    name: track.name,
                    artist: track.artists[0].name,
                    album: track.album.name,
                    image: track.album.images[1].url,
                    uri: track.uri
                }))
            })
        }
    }
}

export default Spotify
Enter fullscreen mode Exit fullscreen mode

创建模型

我们的应用程序有六个模型函数createUser、、、getUsersavePlaylistgetPlaylistsgetPlaylistdeletePlaylist

  • createUser:获取用户数据并在我们的 Fauna 数据库中创建一个新文档。如果已经有用户使用相同的 Spotify ID 注册,应用程序将抛出错误,因为我们将索引设置user_by_user_id为仅接受唯一值user_id,否则,将用户数据存储在数据库中,并创建一个本地存储项,该项将保存用户的详细信息,直到用户注销。
  • getUser:接受一个参数user_id并使用索引查询数据库user_by_user_id
  • savePlaylist:获取user_id,以及用户已选择的音乐列表。
  • getPlaylists;获取user_id并返回该用户创建的所有播放列表集合。
  • getPlaylist:获取id播放列表并返回该播放列表中的音乐列表。
  • deletePlaylist:获取id播放列表并删除该收藏集。

要创建我们的模型,/utils/models.js将包含以下代码:

import faunadb, { query as q } from 'faunadb'
const client = new faunadb.Client({ secret: "YOUR-FAUNA-SECRET-KEY" })
export const createUser = async ({user_id, email, name, image, url}) => {
    try {
        const user = await client.query(
            q.Create(
                q.Collection('users'),
                {
                    data: {user_id, email, name, image, url}
                }
            )
        )
        localStorage.setItem('user', JSON.stringify(user.data))
        return user.data
    } catch (error) {
        return
    }
}
export const getUser = async (user_id) => {
    try {
        const user = await client.query(
            q.Get(
                q.Match(q.Index('user_by_user_id'), user_id)
            )
            )
        localStorage.setItem('user', JSON.stringify(user.data))
        return user.data
    }
    catch (error) {
        return
    }
}
export const savePlaylist = async (user_id, name, tracks) => {
    if(!name || !tracks.length){
        return 
    }
    try {
        const playlists = await client.query(
            q.Create(
                q.Collection('playlists'),
                {
                    data: {user_id, name, tracks}
                }
            )
        )
        return playlists.data
    } catch (error) {
        return
    }
}
export const getPlaylists = async (user_id) => {
    let playlistList = []
    try {
        const playlists = await client.query(
            q.Paginate(
                q.Match(q.Index('playlist_for_user'), user_id)
            )
        )
        for (let playlistID of playlists.data) {
            let playlist = await getPlaylist(playlistID.value.id)
            playlistList.push(playlist)
        }
        return playlistList
    } catch (error) {
        return
    }
}
export const getPlaylist = async (id) => {
    try {

        const playlist = await client.query(
            q.Get(q.Ref(q.Collection('playlists'), id))
        )
        playlist.data.id = playlist.ref.value.id
        return playlist.data
    } catch (error) {
        return
    }
}

export const deletePlaylist = async (id) => {
    try {   
        const playlist = await client.query(
            q.Delete(
                q.Ref(q.Collection('playlists'), id)
            )
        )
        playlist.data.id = playlist.ref.value.id
        return playlist.data
    } catch (error) {
        return
    }
}
Enter fullscreen mode Exit fullscreen mode

创建索引页

当应用程序首次运行或用户转到/路由时,我们希望应用程序呈现身份验证页面。

当 Index 组件加载时:如果用户已登录,我们使用useHistory钩子将用户重定向到“/create”,否则,我们希望显示 Index 组件的内容。

登录和注册按钮有一个 onClick 事件监听器,当点击时会调用相应的函数。

Signup函数从函数中获取用户的 Spotify ID Spotify.getUserId,然后尝试使用获取的 Spotify ID 在我们的 Fauna 数据库中创建一个新用户。如果该 ID 在显示错误消息之前已被注册,我们将用户重定向到“/create”路由。

Login函数还会从函数中获取用户的 Spotify ID Spotify.getUserId,然后查询 Fauna 数据库,查找具有该 ID 的用户。如果未找到该 ID 作为用户,则显示错误消息,否则重定向到“/create”路由。

/components/Index.js将包含以下代码:

import React from 'react'
import { useHistory } from 'react-router-dom'
import { createUser, getUser } from '../utils/model'
import Spotify from '../utils/Spotify'
const Index = () => {
    const history = useHistory()

    if (localStorage.getItem('user')) {            
        history.push('/create')
    }
    const Signup = () => {
        Spotify.getUserId().then((newUserData) => {
            createUser(newUserData)
            .then(req => {
                if (req)
                    history.push('/create')
                else
                    alert("Spotify account already registered!")
            })
            .catch((err) => console.log(err.message))
        })
    }

    const Login = () => {
        Spotify.getUserId().then((newUserData) => {
            getUser(newUserData.user_id)
            .then(req => {
                if (req)
                    history.push('/create')
                else
                    alert('Spotify account not found! Signup first')
            })
            .catch((err) => console.log(err.message))
        })
    }
    return (
        <>
            <div className="container">
                <br /><br /><br />
                <h1>MusicBuddy</h1>
                <br /><br />
                <span className="btn" onClick={() => Login()}>Login</span>
                <br /><br />
                <p>OR</p>
                <span className="btn" onClick={() => Signup()}>SignUp</span>
            </div>
        </>
    )
}
export default Index
Enter fullscreen mode Exit fullscreen mode

创建 NavBar 组件

NavBar 组件是我们存放用户资料、导航链接和注销按钮的地方。

NavBar 接受一个名为 的 props userData。我们还设置了一个 state,用于检查用户的个人资料下拉菜单是否可见。带有 atrribute 的 divclassName="dropDown"具有 onMouseEnter 和 onMouseLeave 事件,用于将userProfile状态更改为 true 或 false。当userProfile状态为 true 时,<ul>包含用户个人资料的标签将被渲染,否则将被隐藏。

注销按钮有一个 onClick 事件监听器,它会清除本地存储。

components/NavBar.js将包含以下代码:

import React, { useState} from 'react'
import { Link } from 'react-router-dom'
import userImg from '../assets/justin.PNG'
const NavBar = ({ userData }) => {
    const [userProfile, setUserProfile] = useState(false)
    return (
        <>
            <div >
                <div className="dropDown" onMouseEnter={() => setUserProfile(!userProfile)} onMouseLeave={() => setUserProfile(false)}>
                    <img src={userData?.image || userImg} alt="user" />
                    {userProfile && <ul>
                        <li><h3>{ userData?.name || 'John Doe' }</h3></li>
                        <li>
                            <p >
                                <a href={userData?.url || '/'} target="_blank" rel="noopener noreferrer">{'Profile >>'}</a>
                            </p>
                        </li>
                    </ul>}
                </div>
                <div>
                    <Link to="/" className="btn">Home</Link>
                    <Link to="/mycollections" className="btn">Collections</Link>
                    <Link to="/" className="btn" onClick={() => localStorage.clear()}>Logout</Link>
                </div>
            </div>
        </>
    )
}
export default NavBar
Enter fullscreen mode Exit fullscreen mode

创建新播放列表页面

该组件包含另外三个组件:NavBarPlayListSearchResults

  • SearchResults允许用户在我们的应用程序中搜索音乐并从 Spotify API 获取结果。
  • PlayList允许用户创建一些选定音乐的播放列表并将其存储在数据库中。

/components/create.js将包含以下代码:

import React, { useState, useEffect } from 'react'
import PlayList from './PlayList'
import SearchResults from './SearchResults'
import Spotify from '../utils/Spotify'
import NavBar from './NavBar'
import { useHistory } from 'react-router-dom'
import { savePlaylist } from '../utils/model'
const Create = () => {
    const history = useHistory()
    const [userData, setUserData] = useState(JSON.parse(localStorage.getItem("user")))
    useEffect(() => {
        if (!localStorage.getItem('user')) {
            history.push('/')       
        }
        setUserData(JSON.parse(localStorage.getItem("user")))
    }, [history])
    const [searchResults, setSearchResults] = useState([])
    const [playListName, setPlayListName] = useState("")
    const [playListTracks, setPlayListTracks] = useState([])
    const search = (term) => {
        if (term !== "") {
            Spotify.search(term).then((searchResults) => setSearchResults(searchResults))
        }
        else {
        document.querySelector("#searchBar").focus()
        }
    }
    const addTrack = (track) => {
        if (playListTracks.find((savedTrack) => savedTrack.id === track.id)) {
        return
        }
        const newPlayListTracks = [...playListTracks, track]
        setPlayListTracks(newPlayListTracks)
    }
    const removeTrack = (track) => {
        const newPlayListTracks = playListTracks.filter((currentTrack) => currentTrack.id !== track.id)
        searchResults.unshift(track)
        setPlayListTracks(newPlayListTracks)
    }
    const removeTrackSearch = (track) => {
        const newSearchResults = searchResults.filter((currentTrack) => currentTrack.id !== track.id)
        setSearchResults(newSearchResults)
    }
    const doThese = (track) => {
        addTrack(track)
        removeTrackSearch(track)
    }
    const updatePlayListname = (name) => {
        setPlayListName(name)
    }
    const savePlayList = (e) => {
        e.preventDefault()
        if (playListName !== "") {
            alert('Playlist added successfully...')
            savePlaylist(userData.user_id, playListName, playListTracks)
            .then(req => {
                if (req) {
                    setPlayListName("")
                    setPlayListTracks([])
                }
            })
        }
        else {
        document.querySelector('#playListName').focus()
        }
    }
    return (
        <>
            <NavBar userData={userData}/>
            <div className="container">
                <h1 >MusicBuddy</h1>
                <article className="section">
                    <SearchResults search={search} searchResults={searchResults} onAdd={doThese} />
                    <PlayList playListTracks={playListTracks} playListName={playListName} onNameChange={updatePlayListname} onRemove={removeTrack} onSave={savePlayList} />
                </article>
            </div>
        </>
    )
}
export default Create
Enter fullscreen mode Exit fullscreen mode

创建搜索结果组件

该组件包含SearchBarTrackList组件。

  • SearchBar组件包含一个表单,供用户从 Spotify 搜索随机歌曲。
  • TrackList组件显示搜索结果。

/components/SearchResults.js应该包含以下代码:

import React, { useState } from 'react'
import TrackList from './TrackList'
const SearchResults = ({ search, searchResults, onAdd }) => {
    return (
        <>
            <div className="trackList">
                <SearchBar onSearch={search} />
                <TrackList tracks={searchResults} onAdd={onAdd} />
            </div>
        </>
    )
}
const SearchBar = ({ onSearch }) => {
    const [term, setTerm] = useState("");
    const handleSubmit = (e) => {
    e.preventDefault();
    onSearch(term);
    };
    return (
    <>
        <form className="form" onSubmit={handleSubmit}>
        <input
            id="searchBar"
            type="text"
            placeholder="Song, album or artist name"
            onChange={(e) => setTerm(e.target.value)}
        />
        <button className="btn" onClick={handleSubmit}>
            SEARCH
        </button>
        </form>
    </>
    );
};

export default SearchResults
Enter fullscreen mode Exit fullscreen mode

创建播放列表组件

该组件包含一个表单和TrackList组件。

  • 该表单用于设置用户正在创建的播放列表的名称。
  • TrackList显示用户将创建的播放列表中包含的音乐列表。

/components/PlayList.js将包含以下代码:

import React from "react";
import TrackList from "./TrackList";
const PlayList = ({ onNameChange, playListTracks, playListName, onRemove, onSave }) => {
    return (
    <>
        <div className="trackList">
        <form className="form" onSubmit={onSave}>
            <input id="playListName" type="text" onChange={(e) => onNameChange(e.target.value)} defaultValue={playListName} placeholder="Playlist Name" />
            {(playListTracks.length > 0) &&        
            <button className="btn" onClick={onSave}>
                Save to Collections
            </button>}
        </form>
        <TrackList tracks={playListTracks} isRemoval={true} onRemove={onRemove} />
        </div>
    </>
    );
};
export default PlayList;
Enter fullscreen mode Exit fullscreen mode

到目前为止,您应该已经观察到SearchResultsPlayList导入的组件TrackList

创建曲目列表组件

该组件包含Track映射到轨道列表的每个项目的组件。
/components/TrackList.js将包含以下代码:

import React from 'react'
import Track from './Track'
import Img from '../assets/omo.png'
const TrackList = ({ tracks, onAdd, isRemoval, onRemove }) => {
    return (
        <>
            {(tracks.length > 0) &&
                <div className="playList">
                    {tracks.map((track) => {
                        return (
                            <Track key={track.id} track={track} onAdd={onAdd} isRemoval={isRemoval} onRemove={onRemove} />
                        )
                    })}
                </div >
            }
            {(tracks.length === 0) &&
                <div className="playList">
                <img src={Img} alt="Oops!" />
                    <h3>Oops! No Tracks founds</h3>
                    <p>Search and add for a track</p>
                </div>
            }
        </>
    )
}
export default TrackList
Enter fullscreen mode Exit fullscreen mode

创建轨道组件

此组件接受曲目数据作为对象,并在 中呈现 Spotify 播放器<iframe>。它还包含一个 TrackAction,允许用户从曲目列表中添加或删除曲目。
/components/Track.js将包含以下代码:

import React, { useState, useEffect } from 'react'
import bgImg from '../assets/justin.PNG'
const Track = ({ track, onAdd, onRemove, isRemoval }) => {
    const [trackBg, setTrackBg] = useState('')
    useEffect(() => {
        track.image? setTrackBg(track.image) : setTrackBg(bgImg)
    }, [track.image])
    const addTrack = () => onAdd(track)
    const removeTrack = () => onRemove(track)
    return (
        <ul className="track">
            <li>
                <div>
                    <div className="item" >                        
                        <div>
                            <h3>{track.name}</h3>
                            {track.artist} | {track.album}
                        </div>
                        {
                            onAdd || onRemove ?
                                <TrackAction isRemoval={isRemoval} removeTrack={removeTrack} addTrack={addTrack} />
                            :
                                ""
                        }
                    </div>
                </div>
            </li>
            <li>
                <iframe src={"https://open.spotify.com/embed/track/" + track.id} width="100%" height="80" frameBorder="0" allowtransparency="True" allow="encrypted-media" title="preview" />
            </li>
        </ul>
    )
}
const TrackAction = ({ isRemoval, removeTrack, addTrack }) => {
    return (
        <>
            {
                isRemoval ?
                    <button className="btn" onClick={removeTrack}> - </button>
                :
                    <button className="btn" onClick={addTrack}> + </button>
            }
        </>
    )
}

export default Track
Enter fullscreen mode Exit fullscreen mode

创建用户的播放列表收藏页面

该组件包含用户保存到 Fauna 数据库的所有播放列表的列表。

getPlaylists函数获取经过身份验证的用户创建的所有播放列表。

播放列表曲目默认是隐藏的,直到用户点击特定的播放列表,然后该togglePlaylist函数将点击的播放列表设置为活动状态,然后呈现属于活动播放列表的曲目。

removePlaylist函数获取播放列表的 ID 并将其从数据库中删除。
/components/MyCollections.js将包含以下代码:

import React, { useState, useEffect } from "react";
import NavBar from "./NavBar";
import { useHistory } from "react-router-dom";
import { deletePlaylist, getPlaylists } from "../utils/model";
import bgImg from '../assets/justin.PNG'
import Track from './Track'
const MyCollections = () => {
    const history = useHistory();
    const [userData, setUserData] = useState(JSON.parse(localStorage.getItem("user")));
    const [playlists, setPlaylists] = useState([])
    const [activePlaylist, setactivePlaylist] = useState()
    useEffect(() => {
    if (!localStorage.getItem("user")) {
        history.push("/");
    }
    getPlaylists(userData?.user_id)
    .then(req => {
        return setPlaylists(req)
    })
    .catch((err) => console.log(err.message))
    if (!userData) {
        setUserData(JSON.parse(localStorage.getItem("user")))
    }
    }, [userData, history]);

    const togglePlaylist = (id) => {
        if (activePlaylist === id) {
            setactivePlaylist()
        }
        else {
            setactivePlaylist(id)
        }
    }
    const removePlaylist = (playlist) => {
        deletePlaylist(playlist.id)
        .then(req => {
            const newPlaylist = playlists.filter((list) => list.id !== playlist.id)
            playlists.unshift(playlist)
            return setPlaylists(newPlaylist)
        })
        .catch((err) => console.log(err.message))
    } 
    return (
    <>
        <NavBar userData={userData} />
        <div className="container">
        <h1 >
            My Collections
        </h1>
        <article className="section">            
            <div className="trackList">
                <div className="playList">
                    {playlists.length ?
                        playlists?.map((playlist) => { return (
                            <ul className="track" key={playlist.id}>
                                <li onClick={() => togglePlaylist(playlist.id)}>
                                    <div >
                                        <div className="item" >                        
                                            <div>
                                                <h3>{playlist.name}</h3>
                                            </div>
                                            <button className="btn" onClick={(e) => {
                                                e.preventDefault()
                                                removePlaylist(playlist)
                                            }}> Delete </button>
                                        </div>
                                    </div>
                                </li>
                                {activePlaylist === playlist.id &&
                                    <div >
                                        {playlist.tracks.map((track) => {
                                            return (
                                                <Track
                                                    key={track.id}
                                                    track={track}
                                                />
                                        )})}
                                    </div>
                                }
                            </ul>
                        )
                        })
                    :
                        <h2 >No Playlist saved . . .</h2>
                    }
                </div>
            </div>
        </article>
        </div>
    </>
    );
};
export default MyCollections;
Enter fullscreen mode Exit fullscreen mode

这样设置好组件后,我们的应用程序应该可以正常运行了。
我们不会就此止步。注意:如果我们访问了未定义的路由,就会出现错误。这是因为我们必须创建一个错误处理组件。

错误处理

我们将创建一个组件,当用户转到我们未定义的任何路线时,该组件将被渲染。
/components/Error.js将包含以下代码:

import React from 'react'
import { Link } from 'react-router-dom' 
const Error = () => {
    return (
        <div >
            <h1> Oops! Page Not found. </h1>
            <h3><Link to="/create">Go back to safety</Link></h3>
        </div>
    )
}
export default Error
Enter fullscreen mode Exit fullscreen mode

结论

在成功创建了此应用程序并将 Fauna 和 Spotify 集成到 React 中后,我们学习了如何使用 Spotify Web API 进行无需邮箱地址和密码的用户身份验证,以及如何使用 Fauna 数据库存储用户数据。我们还探索了 Spotify Web API 的搜索端点,以及如何在使用 Fauna 数据库作为存储介质的情况下处理来自 API 的请求和响应。

您可以从我的GitHub 仓库下载该应用程序的源代码,或点击此处访问演示。您也可以通过Twitter联系我

与“与动物一起写作”计划相关而撰写。

文章来源:https://dev.to/wolzcodelife/how-to-build-a-music-playlist-with-react-spotify-and-fauna-40k6
PREV
通过 Pokedex com Spring WebFlux 设置项目 导入项目 Configurando 或 pom.xml Subindo pela primeira 和应用程序 Criando 模型 Criando 和存储库 Inicializando MongoDB Criando 控制器 Testando 应用程序 Testando 事件流 部署 Heroku 结论
NEXT
使用 React 进行拖放(无需库)修订版