skip to Main Content
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>
  );
}

This is my code. My problem is mainly with the review titles. They are not displaying on the UI even though I am setting their state in the getReviews() function. There is nothing wrong with my API endpoints I have tested them with Postman and CORS is also handled. The Anilist URL is being displayed correctly after I set its state in the handleSubmit() function even Loading… from loadingReviews state appears but the first time I click on the analyze button it displays the anilist URL but says failed to fetch status 404 for the review titles and the second time I click analyze it displays the anilist URL as before shows Loading… from the loadingReviews state but doesn’t display anything. From what I have researched it looks like the problem is with the asynchronous nature of useState but I am not really able to understand how to solve this problem. When I console log the reviewTitles it works albeit after clicking the second time(the first time I get a failed to fetch status 404 as I said before) but nothing is displayed on the frontend as I expect. How do I solve this problem? Any help would be appreciated greatly.

I have tried setting reviewTitles using a callback function but that did not work either. Console logging helped me see that there was a problem but I do not understand why nothing displays the first time I click analyze even though I console logged directly after setting the state. I thought of using useEffect() but this does not seem to be the correct usecase for it.

2

Answers


  1. Chosen as BEST ANSWER

    Guys I have found an answer to my question I added a useEffect() hook to run getReviews() function whenever there is a change in the anilistURL instead of running it directly from the handleSubmit function and mapped the review titles using a key prop(thanks to Nick Parsons) which seemed to solve the problem for me. Here is the working code:

    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>
      );
    }
    

  2. In your case when data is updated there is no event listener for this one . as a example you have updated data but not reflect in your code .
    first define state variable then update it
    add use effect event listener to listener and update component.

    Here’s an updated version of your React component, with a proper state update and an useEffect hook to listen for changes and update the component when necessary:

    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 (

    {loading && Loading…}
    {errorMsg && {errorMsg}}
    {anilistURL && (

    Anilist URL:

    {anilistURL}

    )}
    {loadingReviews && Loading…}
    {reviewTitles.length > 0 && (
    reviewTitles.map((reviewTitle, index) => (

    {reviewTitle}

    ))
    )}

    Enter anime to be analyzed

    <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)}
    />

    Analyze

    );
    }

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search