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

React渲染映射数组异常:搜索流派按钮二次点击失效求助

React调用Spotify API二次点击「搜索流派」无响应问题修复

我是React新手,开发了一个调用Spotify API展示专辑的应用。当前遇到的问题是:首次点击「搜索流派」按钮可正常工作,点击「搜索新音乐」按钮也正常,但再次点击「搜索流派」按钮时毫无反应。已尝试为映射返回的每个元素添加key,但问题仍未解决,代码如下:

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

const CLIENT_ID = "2a65aa062a2948b09f7af884b5447c3d";
const CLIENT_SECRET = "e93fd764678a4ebe97accd512e302219";

function App() {
  const [accessToken, setAcessToken] = useState("");
  const [albums, setAlbums] = useState([]);
  const [genreAlbums, setGenreAlbums] = useState([]);
  const [searchInput, setSearchInput] = useState("");

  useEffect(() => {
    // API Acess Token
    var authParameters = {
      method: "POST",
      headers: {
        "content-type": "application/x-www-form-urlencoded",
      },
      body:
        "grant_type=client_credentials&client_id=" +
        CLIENT_ID +
        "&client_secret=" +
        CLIENT_SECRET,
    };
    fetch("https://accounts.spotify.com/api/token", authParameters)
      .then((result) => result.json())
      .then((data) => setAcessToken(data.access_token));
  }, []);

  // Get new music
  async function search() {
    var newAlbums = await fetch(
      "https://api.spotify.com/v1/browse/new-releases",
      {
        headers: {
          Authorization: "Bearer " + accessToken,
        },
      }
    )
      .then((response) => response.json())
      .then((data) => setAlbums(data.albums.items));
  }

  // Get specific genre
  async function searchGenre() {
    var newAlbumsGenre = await fetch(
      'https://api.spotify.com/v1/search?type=track&q=genre:"' +
        searchInput +
        '"&limit=50',
      {
        headers: {
          Authorization: "Bearer " + accessToken,
        },
      }
    )
      .then((response) => response.json())
      .then((data) => setGenreAlbums(data.tracks.items));
    console.log(genreAlbums);
  }

  return (
    <div className="App">
      <div>
        <button className="button" type="button" onClick={search}>
          Search for new music!
        </button>
        <form className="form">
          <input
            type="text"
            id="genre"
            autoComplete="off"
            onChange={(event) => setSearchInput(event.target.value)}
          />
        </form>
        <button className="genreButton" type="button" onClick={searchGenre}>
          Search Genre
        </button>
      </div>

      <div>
        {albums.map((album, i) => {
          return (
            <div className="card" key={i}>
              <img src={album.images[0].url} alt="alt text" />
              <div className="container">
                <h4>
                  <b>{album.name}</b>
                </h4>
                <p>{album.artists[0].name}</p>
              </div>
            </div>
          );
        })}
      </div>

      <div>
        {genreAlbums.map((track, i) => {
          return (
            <div className="card" key={i}>
              <img src={track.album.images[0].url} alt="alt text" />
              <div className="container">
                <h4>
                  <b>{track.name}</b>
                </h4>
                <p>{track.artists[0].name}</p>
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

export default App;

问题分析与修复方案

核心问题

  1. 异步代码混用+无错误捕获:searchGenre中混合使用await和.then(),且未处理API请求错误,一旦请求失败会抛出未捕获异常,导致后续点击无响应。
  2. URL未编码:搜索关键词直接拼接进URL,若包含空格、引号等特殊字符会破坏请求格式,触发API错误。
  3. 状态变量拼写错误:setAcessToken少写了字母c,虽不影响首次请求,但存在潜在隐患。
  4. 表单默认提交行为:输入框所在表单未阻止默认提交,用户按回车会刷新页面重置状态。

具体修复代码

1. 修正searchGenre函数(统一异步逻辑+错误捕获+URL编码)

async function searchGenre() {
  try {
    // 对搜索关键词编码,避免特殊字符破坏URL结构
    const encodedGenre = encodeURIComponent(searchInput);
    const response = await fetch(
      `https://api.spotify.com/v1/search?type=track&q=genre:"${encodedGenre}"&limit=50`,
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );

    // 检查API响应状态,非2xx状态抛出错误
    if (!response.ok) {
      throw new Error(`请求失败:${response.status} ${response.statusText}`);
    }

    const data = await response.json();
    setGenreAlbums(data.tracks.items);
    // 直接打印返回数据,React状态更新是异步的,无法立即获取新值
    console.log("搜索到的流派曲目:", data.tracks.items);
  } catch (error) {
    console.error("搜索流派出错:", error);
    // 可在此添加用户可见的错误提示,比如弹窗或页面文字提示
  }
}

2. 修正状态变量拼写

// 初始化状态时修正拼写
const [accessToken, setAccessToken] = useState("");

// useEffect中对应修改状态设置函数
useEffect(() => {
  var authParameters = {
    method: "POST",
    headers: {
      "content-type": "application/x-www-form-urlencoded",
    },
    body:
      "grant_type=client_credentials&client_id=" +
      CLIENT_ID +
      "&client_secret=" +
      CLIENT_SECRET,
  };
  fetch("https://accounts.spotify.com/api/token", authParameters)
    .then((result) => result.json())
    .then((data) => setAccessToken(data.access_token));
}, []);

3. 阻止表单默认提交行为

<form className="form" onSubmit={(e) => e.preventDefault()}>
  <input
    type="text"
    id="genre"
    autoComplete="off"
    onChange={(event) => setSearchInput(event.target.value)}
  />
</form>

4. 优化key值(避免使用索引)

用数据的唯一ID作为key,提升渲染性能并避免状态混乱:

// 新专辑列表
{albums.map((album) => (
  <div className="card" key={album.id}>
    <img src={album.images[0].url} alt={album.name} />
    <div className="container">
      <h4><b>{album.name}</b></h4>
      <p>{album.artists[0].name}</p>
    </div>
  </div>
))}

// 流派曲目列表
{genreAlbums.map((track) => (
  <div className="card" key={track.id}>
    <img src={track.album.images[0].url} alt={track.name} />
    <div className="container">
      <h4><b>{track.name}</b></h4>
      <p>{track.artists[0].name}</p>
    </div>
  </div>
))}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 09:35:02