Fetch data from an api in useEffect or in event handler directly in react

Viewed 1873

How a fetch should be performed in react hooks? Among these two code snippets, which one is called a best practice or a best react pattern to perform a fetch?

This example is using useEffect to perform a fetch operation.

import React, { useState, useEffect } from "react";

export default function App() {
  const [query, setQuery] = useState("");
  const [loading, setLoading] = useState(false);
  const [data, setData] = useState([]);

  useEffect(() => {
    if (!loading) return;
    const fetchData = async () => {
      const response = await fetch(
        `https://api.github.com/search/repositories?q=${query}`
      );
      const data = await response.json();
      setData(data.items);
      setLoading(false);
    };
    fetchData();
  }, [loading, query]);

  const onSubmit = e => {
    e.preventDefault();
    setLoading(true);
  };

  return (
    <div className="App">
      <h1>Search Github Repo.</h1>
      <h3>Implementation One</h3>
      <form onSubmit={onSubmit}>
        <input
          type="text"
          value={query}
          onChange={e => setQuery(e.target.value)}
        />
        <button type="submit">Search</button>
      </form>
      {loading && <div>Loading...</div>}
      {!loading &&
        data.map(repo => (
          <div key={repo.id}>
            <h4>{repo.name}</h4>
            <p>{repo.description}</p>
          </div>
        ))}
    </div>
  );
}

This example is using the onClick event handler to perform a fetch operation.

import React, { useState } from "react";

export default function App() {
  const [query, setQuery] = useState("");
  const [loading, setLoading] = useState(false);
  const [data, setData] = useState([]);

  const onSubmit = e => {
    e.preventDefault();
    setLoading(true);
    const fetchData = async () => {
      const response = await fetch(
        `https://api.github.com/search/repositories?q=${query}`
      );
      const data = await response.json();
      setData(data.items);
      setLoading(false);
    };
    fetchData();
  };

  return (
    <div className="App">
      <h1>Search Github Repo.</h1>
      <h3>Implementation Two</h3>
      <form onSubmit={onSubmit}>
        <input
          type="text"
          value={query}
          onChange={e => setQuery(e.target.value)}
        />
        <button type="submit">Search</button>
      </form>
      {loading && <div>Loading...</div>}
      {!loading &&
        data.map(repo => (
          <div key={repo.id}>
            <h4>{repo.name}</h4>
            <p>{repo.description}</p>
          </div>
        ))}
    </div>
  );
}

Running example in codesandbox

2 Answers

It depend on your usecase as to which method you should take

fetching data in useEffect is useful in following scenarios

  • Fetching data during some lifecycles like initial render
  • Fetching data when some prop changes
  • fetching data in an interval but setting up a subscription or setInterval

Fetching data in handler is useful in below scenario

  • Based on a user interaction such as search button click, search input change

Since your case is a based on a use interaction its better and more controlled to actually call the API in a handler instead of a useEffect hook

I would say you should go with the hooks implementation. It's recommended in the React docs for performing side effects:

Data fetching, setting up a subscription, and manually changing the DOM in React components are all examples of side effects. Whether or not you’re used to calling these operations “side effects” (or just “effects”), you’ve likely performed them in your components before. (link to the quote)

There is one more consideration to think about. In your hook implementation:

useEffect(() => {
    if (!loading) return;
    const fetchData = async () => {
      const response = await fetch(
        `https://api.github.com/search/repositories?q=${query}`
      );
      const data = await response.json();
      setData(data.items);
      setLoading(false);
    };
    fetchData();
  }, [loading, query]);

you used an optimization. You're passing [loading, query] into your hook, which means the function will run only when one of those values change. In your second implementation you will have your fetch running on every single input change

Here's a link to the React docs about this aspect of hooks.

Related