代码之家  ›  专栏  ›  技术社区  ›  Na N

为什么ReactJS没有将数组中的更改反映到我的UI中?

  •  0
  • Na N  · 技术社区  · 11 月前
    import React, { useState } from "react";
    
    export default function Recommend() {
      const [animeName, setAnimeName] = useState("");
      const [anilistURL, setanilistURL] = useState("");
      const [errorMsg, setErrorMsg] = useState("");
      const [loading, setLoading] = useState(false);
      const [loadingReviews, setLoadingReviews] = useState(false);
      const [reviewTitles, setreviewTitles] = useState([]);
    
      const handleSubmit = async () => {
        try {
          setLoading(true);
          setreviewTitles([]);
          setanilistURL("");
          setErrorMsg("");
    
          const response = await fetch("http://localhost:8000/get-anilist-url", {
            method: "POST",
            headers: {
              "Content-Type": "application/json",
            },
            body: JSON.stringify({
              anime_title: animeName,
            }),
          });
          if (!response.ok) {
            const errorData = await response.json();
            setErrorMsg(errorData.error || "Unable to get response");
          }
          const data = await response.json();
          setanilistURL(data.anilist_url || "No URL found");
          setErrorMsg("");
        } catch (error) {
          setErrorMsg("An error occurred: " + error.message);
        } finally {
          setLoading(false);
          getReviews();
        }
      };
    
      const getReviews = async () => {
        try {
          setLoadingReviews(true);
          setErrorMsg("");
          const anilistReviewsURL = anilistURL + "/reviews";
          const response = await fetch(
            "http://localhost:3000/api/v1/scrape/get-review-titles",
            {
              method: "POST",
              headers: {
                "Content-Type": "application/json",
              },
              body: JSON.stringify({ reviewsLink: anilistReviewsURL }),
            }
          );
          const data = await response.json();
          if (!response.ok) {
            setErrorMsg(data.error || "Unable to get response");
          }
          setreviewTitles((prevTitles) => [
            ...prevTitles,
            data.reviewTitles
          ]);
          setErrorMsg("");
    
        } catch (error) {
          setErrorMsg("An error occurred: " + error);
        }
        finally {
          setLoadingReviews(false);
        }
      };
    
      return (
        <div className="flex flex-col h-screen bg-slate-700">
          <div className="flex-grow bg-slate-600 p-5 font-mono">
            {loading && <div className="text-white">Loading...</div>}
            {errorMsg && <div className="text-red-500">{errorMsg}</div>}
            {anilistURL && (
              <div className="text-yellow-100">
                <p>Anilist URL:</p>
                <a href={anilistURL} target="_blank" rel="noopener noreferrer">
                  {anilistURL}
                </a>
              </div>
            )}
            {loadingReviews && <div className="text-white">Loading...</div>}
            {reviewTitles && (
              reviewTitles.map((reviewTitle) => {
                (
                  <div className = "text-yellow-500 pt-3">
                    {reviewTitle}
                  </div>
                )
              })
            )}
          </div>
          <div className="p-4 flex items-center justify-between bg-slate-800">
            <h3 className="text-gray-500 text-3xl font-mono">
              Enter anime to be analyzed
            </h3>
            <textarea
              type="text"
              className="bg-slate-700 font-mono text-white text-2xl p-3 border-none outline-none w-full rounded-full"
              placeholder="Enter your prompt..."
              value={animeName}
              onChange={(e) => setAnimeName(e.target.value)}
            />
            <button
              className="bg-slate-700 font-mono text-white text-2xl p-4 rounded-full hover:bg-slate-600 transition duration-200 ml-5"
              onClick={handleSubmit}
            >
              Analyze
            </button>
          </div>
        </div>
      );
    }
    

    这是我的代码。我的问题主要在于评论标题。即使我在getReviews()函数中设置了它们的状态,它们也不会显示在UI上。我的API端点没有任何问题,我已经用Postman对它们进行了测试,CORS也得到了处理。在handleSubmit()函数中设置Anilist URL的状态后,即使在Loading。。。从loadingReviews状态出现,但我第一次点击分析按钮时,它显示了anilist URL,但表示无法获取评论标题的状态404,第二次点击分析时,它像以前一样显示anilist URL。。。从loadingReviews状态,但不显示任何内容。根据我的研究,问题似乎在于useState的异步特性,但我真的不明白如何解决这个问题。当我控制台记录reviewTitles时,尽管在第二次点击后(正如我之前所说,这是我第一次收到无法获取状态404),它仍然有效,但前端没有显示我所期望的任何内容。我该如何解决这个问题?任何帮助都将不胜感激。

    我尝试过使用回调函数设置reviewTitles,但也不起作用。控制台日志记录帮助我发现了问题,但我不明白为什么我第一次单击分析时没有显示任何内容,即使我在设置状态后直接进行了控制台日志记录。我想使用useEffects(),但这似乎不是正确的用例。

    3 回复  |  直到 11 月前
        1
  •  0
  •   samuei Sachinda Nirmal    11 月前

    在您的情况下,当数据更新时,此事件没有事件侦听器。例如,您已经更新了数据,但没有反映在代码中。 首先定义状态变量,然后更新它 将use effect事件监听器添加到监听器并更新组件。

    这是React组件的更新版本,具有适当的状态更新和useEffect挂钩,用于监听更改并在必要时更新组件:

        import React, { useState, useEffect } from "react";
    
    export default function Recommend() {
      const [animeName, setAnimeName] = useState("");
      const [anilistURL, setAnilistURL] = useState("");
      const [errorMsg, setErrorMsg] = useState("");
      const [loading, setLoading] = useState(false);
      const [loadingReviews, setLoadingReviews] = useState(false);
      const [reviewTitles, setReviewTitles] = useState([]);
    
      useEffect(() => {
        if (anilistURL) {
          getReviews();
        }
      }, [anilistURL]);
    
      const handleSubmit = async () => {
        try {
          setLoading(true);
          setReviewTitles([]);
          setAnilistURL("");
          setErrorMsg("");
    
          const response = await fetch("http://localhost:8000/get-anilist-url", {
            method: "POST",
            headers: {
              "Content-Type": "application/json",
            },
            body: JSON.stringify({
              anime_title: animeName,
            }),
          });
    
          if (!response.ok) {
            const errorData = await response.json();
            throw new Error(errorData.error || "Unable to get response");
          }
    
          const data = await response.json();
          setAnilistURL(data.anilist_url || "No URL found");
        } catch (error) {
          setErrorMsg("An error occurred: " + error.message);
        } finally {
          setLoading(false);
        }
      };
    
      const getReviews = async () => {
        try {
          setLoadingReviews(true);
          setErrorMsg("");
    
          const anilistReviewsURL = `${anilistURL}/reviews`;
          const response = await fetch(
            "http://localhost:3000/api/v1/scrape/get-review-titles",
            {
              method: "POST",
              headers: {
                "Content-Type": "application/json",
              },
              body: JSON.stringify({ reviewsLink: anilistReviewsURL }),
            }
          );
    
          if (!response.ok) {
            const errorData = await response.json();
            throw new Error(errorData.error || "Unable to get response");
          }
    
          const data = await response.json();
          setReviewTitles(data.reviewTitles || []);
        } catch (error) {
          setErrorMsg("An error occurred: " + error.message);
        } finally {
          setLoadingReviews(false);
        }
      };
    
      return (
        <div className="flex flex-col h-screen bg-slate-700">
          <div className="flex-grow bg-slate-600 p-5 font-mono">
            {loading && <div className="text-white">Loading...</div>}
            {errorMsg && <div className="text-red-500">{errorMsg}</div>}
            {anilistURL && (
              <div className="text-yellow-100">
                <p>Anilist URL:</p>
                <a href={anilistURL} target="_blank" rel="noopener noreferrer">
                  {anilistURL}
                </a>
              </div>
            )}
            {loadingReviews && <div className="text-white">Loading...</div>}
            {reviewTitles.length > 0 && (
              reviewTitles.map((reviewTitle, index) => (
                <div key={index} className="text-yellow-500 pt-3">
                  {reviewTitle}
                </div>
              ))
            )}
          </div>
          <div className="p-4 flex items-center justify-between bg-slate-800">
            <h3 className="text-gray-500 text-3xl font-mono">
              Enter anime to be analyzed
            </h3>
            <textarea
              type="text"
              className="bg-slate-700 font-mono text-white text-2xl p-3 border-none outline-none w-full rounded-full"
              placeholder="Enter your prompt..."
              value={animeName}
              onChange={(e) => setAnimeName(e.target.value)}
            />
            <button
              className="bg-slate-700 font-mono text-white text-2xl p-4 rounded-full hover:bg-slate-600 transition duration-200 ml-5"
              onClick={handleSubmit}
            >
              Analyze
            </button>
          </div>
        </div>
      );
    }
    
        2
  •  0
  •   Na N    11 月前

    伙计们,我已经找到了问题的答案,我添加了一个useEffects()钩子,以便在anilistURL发生变化时运行getReviews()函数,而不是直接从handleSubmit函数运行它,并使用一个关键道具映射评论标题(感谢Nick Parsons),这似乎为我解决了问题。以下是工作代码:

    import React, { useState, useEffect } from "react";
    
    export default function Recommend() {
      const [animeName, setAnimeName] = useState("");
      const [anilistURL, setAnilistURL] = useState("");
      const [errorMsg, setErrorMsg] = useState("");
      const [loading, setLoading] = useState(false);
      const [loadingReviews, setLoadingReviews] = useState(false);
      const [reviewTitles, setReviewTitles] = useState([]);
    
      useEffect(() => {
        if (anilistURL) {
          getReviews(anilistURL);
        }
      }, [anilistURL]);
    
      const handleSubmit = async () => {
        try {
          setLoading(true);
          setAnilistURL("");
          setReviewTitles([]);
          setErrorMsg("");
    
          const response = await fetch("http://localhost:8000/get-anilist-url", {
            method: "POST",
            headers: {
              "Content-Type": "application/json",
            },
            body: JSON.stringify({
              anime_title: animeName,
            }),
          });
    
          if (!response.ok) {
            const errorData = await response.json();
            setErrorMsg(errorData.error || "Unable to get response");
            setLoading(false);
            return;
          }
    
          const data = await response.json();
          setAnilistURL(data.anilist_url || "No URL found");
        } catch (error) {
          setErrorMsg("An error occurred: " + error.message);
          setLoading(false);
        }
      };
    
      const getReviews = async (url) => {
        try {
          setLoadingReviews(true);
          setErrorMsg("");
          const anilistReviewsURL = url + "/reviews";
    
          const response = await fetch(
            "http://localhost:3000/api/v1/scrape/get-review-titles",
            {
              method: "POST",
              headers: {
                "Content-Type": "application/json",
              },
              body: JSON.stringify({ reviewsLink: anilistReviewsURL }),
            }
          );
    
          if (!response.ok) {
            const errorData = await response.json();
            setErrorMsg(errorData.error || "Unable to get response");
            setLoadingReviews(false);
            return;
          }
    
          const data = await response.json();
          setReviewTitles(data.reviewTitles || []);
        } catch (error) {
          setErrorMsg("An error occurred: " + error.message);
        } finally {
          setLoadingReviews(false);
        }
      };
    
      return (
        <div className="flex flex-col h-screen bg-slate-700">
          <div className="flex-grow bg-slate-600 p-5 font-mono">
            {loading && <div className="text-white">Loading...</div>}
            {errorMsg && <div className="text-red-500">{errorMsg}</div>}
            {anilistURL && (
              <div className="text-yellow-100">
                <p>Anilist URL:</p>
                <a href={anilistURL} target="_blank" rel="noopener noreferrer">
                  {anilistURL}
                </a>
              </div>
            )}
            {loadingReviews && <div className="text-white">Loading reviews...</div>}
            {reviewTitles.length > 0 && (
              <div>
                <h3 className="text-white">Review Titles:</h3>
                {reviewTitles.map((reviewTitle, index) => (
                  <div key={index} className="text-yellow-500 pt-3">
                    {reviewTitle}
                  </div>
                ))}
              </div>
            )}
          </div>
          <div className="p-4 flex items-center justify-between bg-slate-800">
            <h3 className="text-gray-500 text-3xl font-mono">
              Enter anime to be analyzed
            </h3>
            <textarea
              type="text"
              className="bg-slate-700 font-mono text-white text-2xl p-3 border-none outline-none w-full rounded-full"
              placeholder="Enter your prompt..."
              value={animeName}
              onChange={(e) => setAnimeName(e.target.value)}
            />
            <button
              className="bg-slate-700 font-mono text-white text-2xl p-4 rounded-full hover:bg-slate-600 transition duration-200 ml-5"
              onClick={handleSubmit}
            >
              Analyze
            </button>
          </div>
        </div>
      );
    }
    
        3
  •  -2
  •   Sachinda Nirmal    11 月前

    实际上,react使用虚拟DOM而不是实际DOM。用于管理单个组件数据的使用状态。若状态更新,它会自动渲染组件。这是反应的基本要素。use Effect也是反应的事件监听器。侦听器中的某些值发生变化,它也会自动更新组件