Introducing.png

Introduction

In React applications, handling API requests, different states & callbacks associated with these API requests can become complex and repetitive. To simplify the process and provide a better DX over native fetch, we've created React Fetcher, a lightweight library that provides a clean and intuitive way to handle API requests within your React components. In this blog post, we'll introduce React Fetcher and demonstrate how it can simplify making & handling API requests.

Getting Started

To get started with React Fetcher, we’ll need to install it in our React project:

npm install @oaktree/react-fetcher

Once installed, we can import the createFetcher function to create an instance of the Fetcher class:

// fetcher.js

import { createFetcher } from '@oaktree/react-fetcher';

const fetcher = createFetcher({
  baseUrl: 'https://api.example.com', // defaults to same origin
  headers: () => ({ // optional
    Authorization: 'Bearer ' + localStorage.getItem("auth_token"),
  }),
});

export default fetcher;

We can now import this fetcher object and make requests just by calling fetcher.useQuery, fetcher.useMutation or fetcher.request functions.

Under the hood, all of these functions calculate the headers by calling the headers function on every request and whatever the url & url params we pass in arguments will be concatenated in the provided baseUrl itself.

Side note: We know that everyone hates when simple libraries like is-odd use multiple external dependencies, Therefore, we’ve used native fetch API under the hood and only dependency that the package depends on is react itself.

Example 1: fetching data using useQuery

Let's start with a common scenario: fetching data from an API endpoint. With React Fetcher, we can accomplish this with the useQuery hook. Here's an example:

import fetcher from './fetcher';

const MyComponent = () => {
  const { data, error, isLoading, isError } = fetcher.useQuery('users');

  if (isLoading) {
    return <p>Loading...</p>;
  }

  if (isError) {
    return <p>Error: {error.message}</p>;
  }

  return (
    <div>
      {data.map((user) => (
        <p key={user.id}>{user.name}</p>
      ))}
    </div>
  );
};

In the example above, we use the useQuery hook to fetch a list of users from the /users endpoint.

The data variable holds the response data, error captures any error that occurred during the request, isLoading indicates if the request is still loading, and isError indicates if an error occurred. Based on these states, we can conditionally render loading spinners or error messages.

Example 2: Mutating Data with useMutation

React Fetcher also simplifies data mutation scenarios, such as creating or updating resources. The useMutation hook allows us to perform such operations effortlessly. Consider the following example:

import fetcher from './fetcher';

const MyForm = () => {
  const { mutate, isLoading } = fetcher.useMutation('users', {
        onSuccess: (responseData) => console.log("User created successfully", responseData),
        onError: (error) => console.log("Some error occured", error.status, error.data),
    });

  const handleSubmit = (event) => {
    event.preventDefault();

    const formData = new FormData(event.target);
    const user = {
      name: formData.get('name'),
      email: formData.get('email'),
    };

    mutate(user)
  };

  return (
    <form onSubmit={handleSubmit}>
      <label htmlFor="name">Name:</label>
      <input type="text" id="name" name="name" required />

      <label htmlFor="email">Email:</label>
      <input type="email" id="email" name="email" required />

      <button type="submit" disabled={isLoading}>
        {isLoading ? 'Creating...' : 'Create User'}
      </button>
    </form>
  );
};

In this example, we use the useMutation hook to create a new user by sending a POST request to the /users endpoint. The mutate function is called when the form is submitted, sending the user data to the server. We disable the submit button when the request is loading to prevent duplicate submissions.

Example 3: Using other REST methods

import { useMutation } from '@oaktree/react-fetcher';

const EditUserForm = ({ user }) => {
  const { mutate: patchUser, isLoading: isPatching } = fetcher.useMutation(
        `/users/${user.id}`,
        { method: 'PATCH' }
    );

    const { mutate: deleteUser, isLoading: isDeleting } = fetcher.useMutation(
        `/users/${user.id}`,
        { method: 'DELETE' }
    );

  const handleSubmit = (event) => {
    event.preventDefault();

    const formData = new FormData(event.target);
    const updatedUser = {
      name: formData.get('name'),
      email: formData.get('email'),
    };

    patchUser(updatedUser)
  };

  const handleDelete = () => {
        deleteUser()
  };

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <label htmlFor="name">Name:</label>
        <input type="text" id="name" name="name" defaultValue={user.name} required />

        <label htmlFor="email">Email:</label>
        <input type="email" id="email" name="email" defaultValue={user.email} required />

        <button type="submit" disabled={isLoading}>
          {isPatching ? 'Updating...' : 'Update User'}
        </button>
      </form>

      <button onClick={handleDelete} disabled={isLoading}>
        {isDeleting ? 'Deleting...' : 'Delete User'}
      </button>
    </div>
  );
};

In this example, we have an EditUserForm component that allows users to update and delete a specific user. We use the useMutation hook and specify the appropriate REST method (PATCH for updating and DELETE for deletion) in the options object passed in the hooks.

When the form is submitted, the user's data is sent with a PUT request to update the user's information. When the delete button is clicked, a DELETE request is sent to remove the user from the server.

Conclusion

React Fetcher simplifies API requests in React applications, allowing you to focus on building your application's features rather than dealing with the intricacies of network requests. With the useQuery and useMutation hooks provided by React Fetcher, you can easily fetch data and perform mutations, while managing loading and error states effortlessly.

Give React Fetcher a try in your next React project, and experience the benefits of cleaner and more maintainable code when handling API requests. You can find more details and examples in the official documentation. Happy coding!

0