Blog Posts

Effortless HTTP requests in React with the Facade pattern

Hey there! Today, I want to talk about the Facade pattern and the convenience of using a custom useHttp React hook.

In software development, the Facade pattern is a structural design pattern that provides a simplified interface to a complex system. The idea is to create a high-level interface that makes it easier to interact with the underlying system.

This pattern can be incredibly useful when dealing with HTTP requests. Making API calls can be a complex process that involves a lot of code. However, with a Facade, we can create a simplified interface that handles all the complexities of making an API call, allowing us to focus on the more important parts of our application.

That's where the useHttp React hook comes in. It provides a simple interface for making HTTP requests in a React component. With this hook, you don't have to worry about setting up fetch requests, handling loading states, or error handling. It abstracts all of these details and allows you to focus on what really matters - the data.

Let's take a closer look at the code for this hook:

import { useState } from "react";

type HttpMethods = "GET" | "POST" | "PUT" | "DELETE";

const useHttp = () => {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<unknown | null>(null);
  const [data, setData] = useState(null);

  const sendRequest = async (
    url: string,
    method: HttpMethods,
    options: RequestInit,
    body?: unknown
  ) => {
    try {
      setLoading(true);
      const response = await fetch(url, {
        method,
        mode: "cors",
        credentials: "include",
        ...options,
        body: body ? JSON.stringify(body) : undefined,
      });
      const json = await response.json();
      setLoading(false);

      // No error, operation successful
      setData(json);
      return json;
    } catch (err) {
      // for all unexpected errors not handled on backend error handling
      setError(err);
      setLoading(false);
      setData(null);
    }
  };

  const get = async (url: string, options: RequestInit = {}) => {
    return sendRequest(url, "GET", options);
  };

  const post = async (
    url: string,
    body: unknown = {},
    options: RequestInit = {}
  ) => {
    return sendRequest(url, "POST", options, body);
  };

  const put = async (
    url: string,
    body: unknown = {},
    options: RequestInit = {}
  ) => {
    return sendRequest(url, "PUT", options, body);
  };

  const remove = async (url: string, options: RequestInit = {}) => {
    return sendRequest(url, "DELETE", options);
  };

  return { loading, error, data, get, post, put, remove };
};

export default useHttp;

As you can see, the hook defines four different HTTP methods: get, post, put, and remove. These methods all make use of a sendRequest function that handles the details of making a fetch request.

The useHttp hook also manages loading and error states, allowing you to easily update your UI based on the status of the request. This can be incredibly useful when dealing with complex applications that make a lot of API calls.

Overall, the useHttp hook provides a simplified interface for making HTTP requests in a React component. It's a great example of how the Facade pattern can be used to simplify complex systems and make them more manageable.

The JavaScript Event Loop

The event loop is a way for the browser to deal with asynchronous events. Even if you are unfamiliar with the event loop, you have likely had encounters with the event loop unbeknownst to you. An infinite recursive call will crash the program by overloading the stack. You may also have had peculiar experiences with setTimeout(cb, 0) magically working. By itself, the Javascript V8 Engine does not provide APIs for callback-related events. These include event listeners, AJAX requests, or setTimeout. By default, JavaScript is single-threaded, which means it only has one call stack. We don’t want slow code to block the call stack, or else the user will not be able to interact with the page in a fluid, responsive way. 

For asynchronous processes, the async code is pushed onto the call stack. In the case of a setTimeout, the browser will utilize the setTimeout Web API, and create a timer for the completion of the setTimeout call. The callback inside of setTimeout will be pushed onto the task queue/callback queue. After the rest of the synchronous events have been popped off of the call stack(the call stack is empty), the event loop will push the callback inside of setTimeout to the call stack, which JavaScript finally runs. For cases where setTimeout(cb, 0) is used, the setTimeout only runs after the call stack is empty, which means it runs right after all of the synchronous code.

In the rendering process, the browser pushes the function for rendering onto the task queue on each 16.6 millisecond interval to ensure a fluid, 60 frames per second user experience. If the call stack has slow, synchronous code, the browser will not be able to push that render callback in the task queue onto the main stack, which leads to horrible user experiences. The render callback is given higher priority than the other async callbacks inside of the queue. The event loop is an important concept for JavaScript developers to understand to become better engineers.

 

Video that helped me understand the event loop: https://www.youtube.com/watch?v=8aGhZQkoFbQ