DEV Community

Discussion on: Building custom hooks in React to fetch Data

Collapse
lukeshiru profile image
Luke Shiru

If the endpoint just returns a JSON, you can simplify it quite a bit by just using fetch and not using async/await:

import { useEffect, useState } from "react";

export const useFetch = url => {
    const [data, setData] = useState();
    const [error, setError] = useState();
    const [loading, setLoading] = useState(false);

    useEffect(() => {
        setLoading(true);
        fetch(url)
            .then(response => response.json())
            .then(setData)
            .catch(setError)
            .finally(() => setLoading(false));
    }, [url]);

    return { data, error, loading };
};
Enter fullscreen mode Exit fullscreen mode

Cheers!

Collapse
ehaynes99 profile image
Eric Haynes • Edited on

That's not any simpler... It's just using the old Promise api...

Collapse
lukeshiru profile image
Luke Shiru • Edited on

Do you objectively believe that? I mean, IDK about you, but this:

const fetchAPI = async () => {
    try {
        const response = await fetch("/api");
        const data = await response.json();
        setData(data);
    } catch (error) {
        setError(error);
    } finally {
        setLoading(false);
    }
};
Enter fullscreen mode Exit fullscreen mode

Is unnecessarily more complex than this:

const fetchAPI = () =>
    fetch("/api")
        .then(response => response.json())
        .then(setData)
        .catch(setError)
        .finally(() => setLoading(false));
Enter fullscreen mode Exit fullscreen mode

async/await + try/catch makes sense sometimes in really complex promise chains that depend on each other, but having something as simple as fetch, they don't make much sense :/

Thread Thread
hernanfarruggia profile image
Hernán Farruggia

I don't see any complexity in any of those... 🤷🏻‍♂️ They just have different sintax, and usually which one to use, will depend on the conventions of your project... Some companies decide to go with async to use latest festures, others prefer to stay with plain old promises... These sre just different points of view...

Thread Thread
lukeshiru profile image
Luke Shiru

I love how you keep using "old" as an argument. Using async/await instead of Promises is not the same as using class instead of prototype. When writing a "class", is always simpler to just use class instead of prototype, so in that case "old is worst", yes.

But in the case of async/await, in some scenarios the new approach is way more complex. You shouldn't use "one or the other", you should just use the one that makes the code easier to read. For simple stuff like fetch, the "new" approach makes it harder to read:

fetch("/api")
  .then((response) => response.json())
  .then(setData)
  .catch(setError);

// vs

try {
  const response = await fetch("/api")
  const data = await response.json();
  setData(data)
} catch (error) {
  setError(error);
}
Enter fullscreen mode Exit fullscreen mode

We should try to avoid using stuff just because is "new" and being like this...
Gif from "The Simpsons" where Smithers says: But she's got a new hat!

And instead use the new features where they are useful. For async/await that's when the promise chain becomes complex (promises that depend on other promises, async iterables and so on).

Thread Thread
ehaynes99 profile image
Eric Haynes • Edited on

Note that the former reply was someone else, so no one "kept using 'old'". It is, in fact, older, but we can refer to it as "the .then syntax".

To each their own, but the allure of async/await is that the code reads exactly the same as it would if it were synchronous. There is a valid argument for consistency. E.g. it's pretty much universally accepted that we don't intermittently use callbacks for async, even if it would save a line or 2.

It's not "way more complex", either. It's slightly more verbose, and only in a rare set of cases. The .then syntax is only cleaner where you can constrain the action to lambda expressions and have at least 4 of them. As soon as you need a block for any of them, IMO it's rather messy. What is simply another line with async/await becomes a new function, another level of nesting, and a new scope. It takes a hit for readability when shorter than 4 as well, since formatters like prettier will inline:

myApi.getOrder(123).then(setOrder).catch(setError);
Enter fullscreen mode Exit fullscreen mode

So yes, in the cases where you have the catch in the same place as the call, can chain 4 or more operations that are all one liners, and never need to refer to something in a prior step of the chain, it's slightly cleaner, but it's rare enough that I wouldn't fault anyone for just forgetting about the syntax altogether.

const fetch1 = (orderNum) => {
  fetch(`/api/orders/${orderNum}`)
    .then((response) => response.json())
    .then(({ products }) => {
      setProducts(products);
      setError(null);
    })
    .catch((error) => {
      logger.error(error);
      setError(error);
    })
    .finally(() => setLoading(false));
};

// vs
const fetch2 = async (orderNum) => {
  try {
    const response = await fetch(`/api/orders/${orderNum}`);
    const { products } = await response.json();

    setProducts(products);
    setError(null);
  } catch (error) {
    logger.error(error);
    setError(error);
  }
  setLoading(false);
};
Enter fullscreen mode Exit fullscreen mode
Collapse
juancor181192 profile image
Juan Carlos Otero Roca

I use axios

Collapse
lukeshiru profile image
Luke Shiru • Edited on

Try redaxios instead. Pretty much the same API, but closer to fetch and maintained by the same folks behind preact.