6 ways to get data in React
Published: 2023-08-10 01:03:18

Data fetching is a core aspect of any React application. It is important for React developers to understand the different methods of data fetching and which use cases work best for them.

But first, let’s understand JavaScript Promises
Simply put, a promise is a JavaScript object that will yield a value sometime in the future. This usually applies to asynchronous operations (such as data fetching).

A promise has three states:

Pending: where the promise is still in progress
Fulfilled: where the promise resolves successfully and returns a value
Rejected: Promise failed with error
If a promise is fulfilled or rejected, it’s resolved . Promises have different methods to do different things depending on the result. These methods are discussed in more detail in the next section.

Method #1 — Using Promise Method Get API
The Fetch API provides a global fetch() method that enables developers to fetch data in a straightforward way. Prior to fetch(), the traditional approach was to use XMLHttpRequest(). (This method is not covered in this article because fetch() has been superseded by a more powerful and flexible feature set.)

The fetch() method takes one parameter, the URL to request, and returns a promise. The second optional parameter options is an array of attributes. The return value of fetch() can be JSON or XML (an array of objects or a single object). With no options argument, fetch() will always make a GET request.

The first method is what you’ll typically see in simple data fetching use cases, and is often the first result when browsing API documentation. As mentioned earlier, we fetch data from an API that returns a random image of a dog, and render that image on the screen. Before making the request, we wrap the code in a useEffecthook with an empty dependencies array so that fetch()the method is only run when the component is initially installed.

 useEffect(() => {
    fetch(URL)
      // syntax for handling promises
      .then((res) => {
        // check to see if response is okay
        if (res.ok) {
          // if okay, take JSON and parse to JavaScript object
          return res.json();
        }
        throw res;
      })
      //  .json() returns a promise as well
      .then((data) => {
        console.log(data);
        // setting response as the data state
        setData(data);
      })
      // if res is not okay the res is thrown here for error
      .catch((err) => {
        console.error(`Error: ${err}`);
        // setting the error state
        setError(err);
      })
      //  regardless if promise resolves successfully or not we remove loading state
      .finally(() => {
        setLoading ( false );
      });
  }, []);
In , we call the method and pass in the URL of the API endpoint. In this method, we use the , , method of the promise object (recall that returns a promise). We use this method and pass in a callback function to check if the response is OK. If the response is OK, we take the returned JSON data and use the method to parse it into a JavaScript object. If the response is not normal, we will error.

Since the .json()method also returns a promise, we can chain another .then()and pass a function to set the state of the data, which is then used elsewhere in the component. In our example, the external API returns an object with a url property (which will be used as srcour image).

Continuing through the chain, the next part is .catch()to arrange for a function to be called when the promise is rejected. This also returns another promise, which we can then chain .finally()which will be called regardless of whether the promise is resolved (resolved or rejected). This .finally()approach allows us to avoid duplicating code in and .then(), .catch()making it a good place to remove loading state in our example.

Approach #2 — Libraries with Promise methods Axios
Axios is a popular HTTP client library for efficient data fetching. It can be easily installed into React applications via npm or other package managers. Using Axios is an alternative to the Fetch API, which has some advantages if you don’t mind installing external libraries.

The second example will be very close to the code of the first example, using the same promise method to handle the promise state and response. After fetch()importing the Axios library into our component, we can use axios.get()a method that can pass a URL to our external API endpoint instead. This returns a Promise, so we can take the same approach as Promise method chaining.

useEffect(() => {
    axios.get(URL)
      // syntax for handling promises
      .then((res) => {
        console.log(res.data);
        // axios converts json to object for us (shortens our code)
        setData(res.data);
      })
      // axios takes care of error handling for us instead of checking manually
      .catch((err) => {
        console.error(`Error: ${err}`);
        // setting the error state
        setError(err);
      })
      //  regardless if promise resolves successfully or not we remove loading state
      .finally(() => {
        setLoading ( false );
      });
  }, []);
The obvious difference between the code for the Fetch API and this Axios method is that with Axios we only need one, because Axios converts the .then()JSON to JavaScript objects for us (shortening our code). Also, we no longer write conditions to manually throw errors, because Axios will throw 400 and 500 range errors for you (shortening our code again).

Approach #3 — Asynchronous functions (async/await)
In this example, we’ll abandon the promise chaining we used in the previous two examples and instead introduce a more modern approach to writing asynchronous, promise-based code. This approach can be used with any fetching mechanism of your choice, but for this example we’ll stick with the Axios library.

The third example sets up the component in a similar fashion to the previous example by importing the Axios library and then wrapping the code for fetching the data in useEffecta with an empty dependencies array. In useEffect, we use the keyword to create an asynchronous function async, and then within that function we have three separate parts - try, catchand finally. This try/catch approach is used to handle errors in JavaScript. The code inside the block tryis executed first, if any errors are thrown, they will be "caught" in the block, catchand the inner code will be executed. Finally, the finallyblock will always be executed after the flow has passed the try/catch.

useEffect(() => {
    // create async function b/c cannot use async in useEffect arg cb
    const fetchData = async () => {
    //   with async/await use the try catch block syntax for handling
      try {
        // using await to make async code look sync and shorten 
        const res = await axios.get(URL);
        setData(res.data);
      } catch (err) {
        console.error(`Error: ${err}`);
        // setting the error state
        setError(err);
      } finally {
        setLoading ( false );
      }
    };

    fetchData ();
  }, []);
In this example, the try block creates a variable called res (short for response) that uses the async keyword. This allows the code to look synchronous (shorter and easier on the eyes). In this example, axios.get(URL) is “waiting” until it stabilizes. If the promise is fulfilled, then we set the data into the state. If the promise is rejected (throws an error), it goes into the catch block.

Method #4 — Create a “ useFetch" custom React Hook
The fourth approach is to create our own custom React hook, call useFetchit that can be reused in different components in our application, and remove the bulky fetching code from each component. This example is really just taking the fourth example (using the same techniques of the Axios library and async/await) and moving that code into its own custom hook.

To do this, we create a function called useFetch.js. Then we useEffectadd all the code from the previous example, along with the different states we're tracking, into the function useFetch. Finally, this function will return an object containing each state, which is then useFetchaccessed where the hook is called. Our useFetchhook will also accept one parameter, the URL , to allow for more reusability and the possibility to make fetch requests to different endpoints.

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

  useEffect(() => {
    // create async function b/c cannot use asyc in useEffect arg cb
    const fetchData = async () => {
      //   with async/await use the try catch block syntax for handling
      try {
        // using await to make async code look sync and shorten
        const res = await axios.get(url);
        setData(res.data);
      } catch (err) {
        console.error(`Error: ${err}`);
        // setting the error state
        setError(err);
      } finally {
        setLoading ( false );
      }
    };

    fetchData ();
  }, []);

  return {
    data,
    loading,
    error,
  };
};
Finally, we import this new custom hook into the component that will use it, and call it like any other React hook. As you can see, this greatly helps with code readability and shortens our component.

The final point of this approach is that you can also install external libraries instead of creating your own custom hooks. A popular library react-fetch-hook has very similar functionality to the hook we just built.

Method #5 — React Querying the library
One of the most modern and powerful ways to fetch data in React is by using the React Query library. It has many features beyond simple data fetching, but for this example, we will learn how to simply fetch data from the same example external API.

Once installed and imported, React Query provides a number of custom hooks that can be reused across our components in a very clean way. In this example, we import QueryClient from and then wrap our application with a provider and pass the QueryClientProvider instance as a property to the wrapper. This enables us to use the library in our application.

To make this simple GET request, we import and use useQueryhooks. Unlike the previous example using a custom hook, we pass in two parameters. The first required parameter is queryKey , which is used as a reference key for this specific query. The second required parameter is queryFn , which is the function that the query will use to request data. We'll use this query function and then use the Fetch API and promise method syntax for the initial fetch instead of just passing a simple URL as in our previous custom hook example. ( This hook has many other optional parameters. )

const { isLoading, error, data } = useQuery("dogData", () => fetch(URL).then((res) => res.json()));
isLoadingFrom here, React Query will do all the extra work behind the scenes, in this case we can destructure , error, and from this hook call to use datain our application, although we can access many other values ​​as well.

In a larger example than our current Dog Image API example, the power and advantages of using React Query are evident. Some additional features to mention include: caching, updating “stale” data in the background, and other performance-related benefits.

Method #6 — Redux Toolkit RTK Queries
The last method of this article is to use Redux Toolkit’s RTK Query for data acquisition. It’s very common for applications to use Redux for state management. If your company or your current side project is currently using Redux, a good option is to use RTK queries to fetch data, as it offers similar simplicity and benefits to React queries.

To start using RTK queries wherever your Redux code is stored, create a rtkQueryService.jsfile to set up data fetching. After creation, you add the service to your Redux store, and assuming you're already using Redux, you'll already have a <Provider>store component that contains your application.

From here, it’s very similar to using custom hooks with React Query methods, you import and then use query hooks and deconstruct data, errorthen isLoadingbe able to use in your components.

const { data, error, isLoading } = useGetDogQuery();
As you can see, Redux has a lot of settings, so this might not be the best approach for our use case, but if you’re already using Redux in a React app and want a simple and modern way of getting data, RTK Query might be great Valuable this also provides benefits like caching.

final thoughts
If you get to this point, support you! The purpose of this article is to introduce some of the different data fetching methods for those learning React. This is a centralized place to compare with the same use case for better understanding. My goal is not to make a statement or go into details about the “best” approach.

Also, there are other current data fetching methods not mentioned here, and I’m sure others will emerge as the React ecosystem grows. That said, I believe this article provides a solid foundation for understanding the field. I hope you find this useful!