Resolved: React async/promises not resolving

Question:

I have problem with async await approach in react. I want to run async function after click ing button and it works but looks like sometimes promise won’t be resolved properly. It seems like typical await/async/then problem. I’ve never really worked with it in react so my knowledge here is pretty non-existent.
Here is my main code. It sets state to url and execute api call after clicking button. It works 70% of time.
import { useApi } from "../../services/UrlShortener/useApi";
import { shortenUrl } from "../../services/UrlShortener/UrlShortenerService";

export const Index = () => {
  const [url, setUrl] = useState("");
  const [shortenedUrl, setShortenedUrl] = useState("");
  const getShortUrl = useApi(shortenUrl);
  const getShortenedUrl = async () => {
    if (url) {
      await getShortUrl
        .request(url) //request is promise.
        .then(() => setShortenedUrl(getShortUrl.data?.value));
      // console.log(shortenedUrl);
      //setShortenedUrl(getShortUrl.data.value);
    } else console.log("error");

    console.log(getShortUrl);
  };
  return (
    <>
      <button
        className="index__shortener__button"
        onClick={() => getShortenedUrl()}
      >
        <BiCut size="2em" />{" "}
        <span className="index__shortener__text"> Shorten URL</span>
      </button>
 <input
          className="index__shortener__input"
          type="text"
          placeholder="Enter your original URL eg.http://interia.pl/news/nie_uwierzysz"
          required
          onChange={(e) => setUrl(e.target.value)}
        />
      <p className="index__shortener__shortener_link">
        <a aria-label={shortenedUrl} href={shortenedUrl}>
          {shortenedUrl}
        </a>
      </p>
    </>
  );
};
Where useApi is reusable hook and shortenUrl is call to api:
export const shortenUrl = async (url) => await axios.post(`${SERVICE_API_URL}`, { value: url});
Typical reusable hook used to fetch data.
export const useApi = (apiFunc) => {
  const request = async (...args) => {
  const [data, setData] = useState(null);
  const [error, setError] = useState("");
  const [loading, setLoading] = useState(false);

  const request = async (...args) => {
    setLoading(true);
    try {
      const result = await apiFunc(...args);
      setData(result.data);
    } catch (err) {
      setError(err.message || "Unexpected Error!");
    } finally {
      setLoading(false);
    }
  };

  return {
    data, //data from api
    error,
    loading,
    request //axios promise function
  };
I see that data is sometimes null/undefined and trying to figure out how it works.
//@Konrad Linkowski approach, sadly arg will be undefined here always because i'm using internal state for useApi

 await getShortUrl.request(url)
        .then((a)=>{
   console.log('getShortUrl', getShortUrl);
          console.log("arg",a);
          setShortenedUrl(a.data?.value);
          })
//console logs
getShortUrl {data: {…}, error: '', loading: false, request: ƒ}data: {value: 'http://localhost:5000/api/urlShortener/oovybnzibofi'}error: ""loading: falserequest: async ƒ ()[[Prototype]]: Object
Index.js:19 arg undefined

Answer:

Correct way


Seems like your useApi returns a promise, but doesn’t do anything with it.
The correct way to do this in react is just to wait for state to update:
import { useApi } from "../../services/UrlShortener/useApi";
import { shortenUrl } from "../../services/UrlShortener/UrlShortenerService";

export const Index = () => {
  const [url, setUrl] = useState("");
  // get request and data from useApi, rename data to shortenedUrl for convenience
  // shortenedUrl will update automatically after request is called
  const { request, data: shortenedUrl } = useApi(shortenUrl);
  const getShortenedUrl = () => {
    if (url) {
      // this request function returns a promise, but this promise doesn't do anything, so no need to await
      request()
    } else {
      console.log("error");
    }
  };
  // ...
};

If you have better answer, please add a comment about this, thank you!

If you like this answer, you can give me a coffee by <a href=”https://violencegloss.com/mkw7pgdwwr?key=2a96ade6f3760c7e63343a320febb78e”>click here</a>

Source: Stackoverflow.com