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

React+Artsy API猜艺术家游戏页面无响应问题求助

猜艺术家游戏加载卡死问题排查与修复

我用React和Artsy API开发猜艺术家游戏,通过首页按钮跳转进入游戏页面:

<Link to="/guessing-game">
  {" "}
  {/* Link to Guessing Game page */}
  <Button type="primary">Play Guessing Game</Button>{" "}
  {/* Button to navigate to Guessing Game page */}
</Link>          

点击按钮跳转后,游戏页面一直显示“Guess the Artist”和“Loading...”,完全无响应,甚至无法右键打开控制台查看错误。

初始GuessingsGame.tsx代码

import React, { useState, useEffect } from "react";
import axios from "axios"; // Import Axios
import "./GuessingGame.css";

interface Artwork {
  _embedded: {
    artists: { name: string }[];
  };
  _links: {
    thumbnail: { href: string };
    artists: { href: string };
  };
  title: string;
}

const GuessingGame: React.FC = () => {
  const [artwork, setArtwork] = useState<Artwork | null>(null);
  const [options, setOptions] = useState<string[]>([]);
  const [selectedOption, setSelectedOption] = useState<string | null>(null);
  const [score, setScore] = useState<number>(0);
  const [loading, setLoading] = useState<boolean>(true);

  useEffect(() => {
    const fetchArtworkData = async () => {
      try {
        const response = await axios.get("https://api.artsy.net/api/artworks", {
          headers: {
            "X-XAPP-Token":
              "My_API_KEY",
          },
        });
        const { _embedded } = response.data;
        if (_embedded && _embedded.artworks && _embedded.artworks.length > 0) {
          const randomArtwork: Artwork =
            _embedded.artworks[
              Math.floor(Math.random() * _embedded.artworks.length)
            ];
          setArtwork(randomArtwork);
          const randomArtists = await getRandomArtists(
            randomArtwork._links.artists.href
          );
          const correctArtist = randomArtwork._embedded.artists[0].name;
          setOptions([...randomArtists, correctArtist]);
          setLoading(false);
        }
      } catch (error) {
        console.error("Error fetching artwork data:", error);
        setLoading(false);
      }
    };

    fetchArtworkData();
  }, []);

  const getRandomArtists = async (artistsLink: string): Promise<string[]> => {
    try {
      const response = await axios.get(artistsLink, {
        headers: {
          "X-XAPP-Token":
            "My_API_KEY",
        },
      });
      const { _embedded } = response.data;
      if (_embedded && _embedded.artists) {
        const randomArtists: string[] = [];
        while (randomArtists.length < 3) {
          const randomIndex = Math.floor(
            Math.random() * _embedded.artists.length
          );
          const randomArtist = _embedded.artists[randomIndex].name;
          if (!randomArtists.includes(randomArtist)) {
            randomArtists.push(randomArtist);
          }
        }
        return randomArtists;
      }
    } catch (error) {
      console.error("Error fetching artists:", error);
    }
    return [];
  };

  const handleOptionSelect = (option: string) => {
    setSelectedOption(option);
    if (option === artwork?._embedded.artists[0].name) {
      setScore(score + 1);
    }
  };

  return (
    <div className="guessing-game-container">
      <h1>Guess the Artist</h1>
      {loading ? (
        <p>Loading...</p>
      ) : artwork ? (
        <div>
          <img
            src={artwork._links.thumbnail.href}
            alt={artwork.title}
            className="artwork-thumbnail"
          />
          <h2>{artwork.title}</h2>
          <h3>Options:</h3>
          <div className="options-container">
            {options.map((option, index) => (
              <button
                key={index}
                onClick={() => handleOptionSelect(option)}
                className={`option-button ${
                  selectedOption === option ? "selected" : ""
                }`}
              >
                {option}
              </button>
            ))}
          </div>
          <p>Score: {score}</p>
        </div>
      ) : (
        <p>No artwork available</p>
      )}
    </div>
  );
};

export default GuessingGame;

已尝试的修改(改用fetch API)

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

interface Artwork {
  _embedded: {
    artists: { name: string }[];
  };
  _links: {
    thumbnail: { href: string };
    artists: { href: string };
  };
  title: string;
}

const GuessingGame: React.FC = () => {
  const [artwork, setArtwork] = useState<Artwork | null>(null);
  const [options, setOptions] = useState<string[]>([]);
  const [selectedOption, setSelectedOption] = useState<string | null>(null);
  const [score, setScore] = useState<number>(0);
  const [loading, setLoading] = useState<boolean>(true);

  useEffect(() => {
    const fetchArtworkData = async () => {
      try {
        const response = await fetch("https://api.artsy.net/api/artworks", {
          headers: {
            "X-XAPP-Token":
              "eyJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6IiIsInN1YmplY3RfYXBwbGljYXRpb24iOiIzMDU2MDk0Yi05ODUwLTRjYTktOTJiNy05MWM4MWYwZTYyNDYiLCJleHAiOjE3MTUzMzQ0MDUsImlhdCI6MTcxNDcyOTYwNSwiYXVkIjoiMzA1NjA5NGItOTg1MC00Y2E5LTkyYjctOTFjODFmMGU2MjQ2IiwiaXNzIjoiR3Jhdml0eSIsImp0aSI6IjY2MzRiMjg1MTc0YmUxMDAxMmRiNDc2ZSJ9.hGIPQb-W4jHjitTo5KGWguRo0YpRqlUNIAQlKJpaDl8",
          },
        });
        const { _embedded } = await response.json();
        if (_embedded && _embedded.artworks && _embedded.artworks.length > 0) {
          const randomArtwork: Artwork =
            _embedded.artworks[
              Math.floor(Math.random() * _embedded.artworks.length)
            ];
          setArtwork(randomArtwork);
          const randomArtists = await getRandomArtists(
            randomArtwork._links.artists.href
          );
          const correctArtist = randomArtwork._embedded.artists[0].name;
          setOptions([...randomArtists, correctArtist]);
          setLoading(false);
        }
      } catch (error) {
        console.error("Error fetching artwork data:", error);
        setLoading(false);
      }
    };

    fetchArtworkData();
  }, []);

  const getRandomArtists = async (artistsLink: string): Promise<string[]> => {
    try {
      const response = await fetch(artistsLink, {
        headers: {
          "X-XAPP-Token":
            "eyJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6IiIsInN1YmplY3RfYXBwbGljYXRpb24iOiIzMDU2MDk0Yi05ODUwLTRjYTktOTJiNy05MWM4MWYwZTYyNDYiLCJleHAiOjE3MTUzMzQ0MDUsImlhdCI6MTcxNDcyOTYwNSwiYXVkIjoiMzA1NjA5NGItOTg1MC00Y2E5LTkyYjctOTFjODFmMGU2MjQ2IiwiaXNzIjoiR3Jhdml0eSIsImp0aSI6IjY2MzRiMjg1MTc0YmUxMDAxMmRiNDc2ZSJ9.hGIPQb-W4jHjitTo5KGWguRo0YpRqlUNIAQlKJpaDl8",
        },
      });
      const { _embedded } = await response.json();
      if (_embedded && _embedded.artists) {
        const randomArtists: string[] = [];
        while (randomArtists.length < 3) {
          const randomIndex = Math.floor(
            Math.random() * _embedded.artists.length
          );
          const randomArtist = _embedded.artists[randomIndex].name;
          if (!randomArtists.includes(randomArtist)) {
            randomArtists.push(randomArtist);
          }
        }
        return randomArtists; // Add return statement here
      }
    } catch (error) {
      console.error("Error fetching artists:", error);
    }
    return []; // Add return statement here to handle other cases
  };

  const handleOptionSelect = (option: string) => {
    setSelectedOption(option);
    if (option === artwork?._embedded.artists[0].name) {
      setScore(score + 1);
    }
  };

  return (
    <div className="guessing-game-container">
      <h1>Guess the Artist</h1>
      {loading ? (
        <p>Loading...</p>
      ) : artwork ? (
        <div>
          <img
            src={artwork._links.thumbnail.href}
            alt={artwork.title}
            className="artwork-thumbnail"
          />
          <h2>{artwork.title}</h2>
          <h3>Options:</h3>
          <div className="options-container">
            {options.map((option, index) => (
              <button
                key={index}
                onClick={() => handleOptionSelect(option)}
                className={`option-button ${
                  selectedOption === option ? "selected" : ""
                }`}
              >
                {option}
              </button>
            ))}
          </div>
          <p>Score: {score}</p>
        </div>
      ) : (
        <p>No artwork available</p>
      )}
    </div>
  );
};

export default GuessingGame;

但问题仍未解决,求代码修改方案或解决思路。


问题排查与修复方案

1. 终止挂起请求,防止页面卡死

页面无法响应大概率是请求挂起或无限循环导致的,给所有请求添加终止信号,组件卸载时强制终止未完成的请求:

useEffect(() => {
  const controller = new AbortController();
  const signal = controller.signal;

  const fetchArtworkData = async () => {
    try {
      const response = await fetch("https://api.artsy.net/api/artworks", {
        headers: {
          "X-XAPP-Token": "你的API_TOKEN",
        },
        signal, // 绑定终止信号
      });
      const { _embedded } = await response.json();
      if (_embedded && _embedded.artworks && _embedded.artworks.length > 0) {
        const randomArtwork: Artwork =
          _embedded.artworks[Math.floor(Math.random() * _embedded.artworks.length)];
        setArtwork(randomArtwork);
        const randomArtists = await getRandomArtists(randomArtwork._links.artists.href, signal);
        const correctArtist = randomArtwork._embedded.artists[0].name;
        setOptions([...randomArtists, correctArtist]);
        setLoading(false);
      } else {
        setLoading(false);
      }
    } catch (error) {
      if (error instanceof DOMException && error.name === "AbortError") {
        return;
      }
      console.error("Error fetching artwork data:", error);
      setLoading(false);
    }
  };

  fetchArtworkData();

  // 组件卸载时终止请求
  return () => controller.abort();
}, []);

// 给getRandomArtists添加signal参数
const getRandomArtists = async (artistsLink: string, signal: AbortSignal): Promise<string[]> => {
  try {
    const response = await fetch(artistsLink, {
      headers: {
        "X-XAPP-Token": "你的API_TOKEN",
      },
      signal,
    });
    const { _embedded } = await response.json();
    if (_embedded && _embedded.artists) {
      const randomArtists: string[] = [];
      let maxAttempts = 10; // 防止无限循环
      while (randomArtists.length < 3 && maxAttempts > 0) {
        maxAttempts--;
        const randomIndex = Math.floor(Math.random() * _embedded.artists.length);
        const randomArtist = _embedded.artists[randomIndex].name;
        if (!randomArtists.includes(randomArtist)) {
          randomArtists.push(randomArtist);
        }
      }
      // 补充不足的选项
      while (randomArtists.length < 3) {
        randomArtists.push(`艺术家${randomArtists.length + 1}`);
      }
      return randomArtists;
    }
  } catch (error) {
    if (error instanceof DOMException && error.name === "AbortError") {
      return [];
    }
    console.error("Error fetching artists:", error);
    return [];
  }
  return [];
};

2. 修复循环阻塞风险

原代码中getRandomArtists的while循环如果遇到_embedded.artists长度不足3的情况,会陷入无限循环,添加最大尝试次数限制并补充默认选项,避免阻塞渲染。

3. 添加错误边界兜底

给组件套上错误边界,防止组件崩溃导致页面完全无响应:

class ErrorBoundary extends React.Component<{children: React.ReactNode}, {hasError: boolean}> {
  constructor(props: {children: React.ReactNode}) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error: Error) {
    return { hasError: true };
  }

  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
    console.error("组件错误:", error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return <h2>游戏加载失败,请刷新重试</h2>;
    }
    return this.props.children;
  }
}

在路由中使用:

<Route path="/guessing-game">
  <ErrorBoundary>
    <GuessingGame />
  </ErrorBoundary>
</Route>

4. 强制打开控制台的技巧

如果页面卡死无法右键,直接用键盘快捷键打开控制台:

  • Windows/Linux:F12 或 Ctrl+Shift+I
  • Mac:Cmd+Opt+I

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 04:09:54