DEV Community

vinodchauhan7
vinodchauhan7

Posted on

React Hooks with Async-Await

Picture this, You have text box which can give list books from google store based on what you type on it. If no book available on that particular search query than show 'No Book Found'. By default, it will always show 'Search for Books'.

Scenario's :
1) No Search : 'Search for Books'.
2) No Result : 'No Book Found'.
3) Found Books : 'Show list of books'.

In above scenarios', we want our result to update after searching the topic on Google API's. This clearly shows that we need to use promises or 'Async-await' to achieve the results. But here we want to make our custom hook which search for books when we hit the search button and show the results.

Now the question is Why we want hooks in this case. Answer is very simple, because we want to make our code cleaner and single liner on final usage. It must be not redundant i.e DRY(Don't Repeat Yourself).

function App() {
  const [search, setSearch] = React.useState("");
  const [query, setQuery] = React.useState("");
  return (
    <div className="App">
      <h1>Search for Books on any Topic</h1>
      <form
        onSubmit={e => {
          e.preventDefault();
          setQuery(search);
        }}
      >
        <label>Search : </label>
        <input type="text" onChange={e => setSearch(e.target.value)} />
        <input type="submit" value="search" />
      </form>

      <h1>List Result on {query}</h1>
    </div>
  );

Till now this is our simple code for getting the final search value in state 'query'. Now we make our custom Async hook to search on google Api's.

function useAsyncHook(searchBook) {
  const [result, setResult] = React.useState([]);
  const [loading, setLoading] = React.useState("false");

  React.useEffect(() => {
    async function fetchBookList() {
      try {
        setLoading("true");
        const response = await fetch(
          `https://www.googleapis.com/books/v1/volumes?q=${searchBook}`
        );

        const json = await response.json();
        // console.log(json);
        setResult(
          json.items.map(item => {
            console.log(item.volumeInfo.title);
            return item.volumeInfo.title;
          })
        );
      } catch (error) {
        setLoading("null");
      }
    }

    if (searchBook !== "") {
      fetchBookList();
    }
  }, [searchBook]);

  return [result, loading];
}

We cannot use 'async' keyword with 'useEffect' callback method. It will result in race conditions.

We are fetching our books from google api and then updating our 'setResult' state with book title. React.useEffect method will only run when our 'searchBook' got change.

//Updated App Component
function App() {
  const [search, setSearch] = React.useState("");
  const [query, setQuery] = React.useState("");
  const [result, loading] = useAsyncHook(query);
  return (
    <div className="App">
      <h1>Search for Books on any Topic</h1>
      <form
        onSubmit={e => {
          e.preventDefault();
          setQuery(search);
        }}
      >
        <label>Search : </label>
        <input type="text" onChange={e => setSearch(e.target.value)} />
        <input type="submit" value="search" />
      </form>

      {loading === "false" ? (
        <h1>Search for Books</h1>
      ) : loading === "null" ? (
        <h1>No Book Found</h1>
      ) : (
        result.map(item => {
          return <p>Book Title : {item}</p>;
        })
      )}
    </div>
  );
}

Alt Text

Src : Because Its One Life

Top comments (5)

Collapse
 
aderchox profile image
aderchox • Edited

Thank you so much. I've also made a quick and minimal working example for future visitors here:
codesandbox.io/s/react-async-custo...
Loading is not necessary unless specifically needed, so in the sandbox above I've removed the loading from one of the hooks.

Collapse
 
nikhilknoldus profile image
Nikhil

It was useful for me, I wanted to use async in useEffect for api call.
Thanks

Collapse
 
baavgai profile image
Baavgai

Thank you! This solved a major issue for me. Very helpful.

Collapse
 
avkonst profile image
Andrey

You might be interested in to consider Hookstate, which supports Promise in useState: hookstate.js.org/docs/asynchronous...

Collapse
 
eliastapiaeta profile image
Elías Tapia Aguilera

Thank very much!!!