BetterTyped / hyper-fetch

⚡ Fetching and realtime data exchange framework.
https://hyperfetch.bettertyped.com/
Apache License 2.0
1.02k stars 27 forks source link

Allow removing request / response interceptors #62

Closed stefanullinger closed 1 year ago

stefanullinger commented 1 year ago

Is your feature request related to a problem? Please describe. In my React app I would like to add some visual activity log for API requests / responses similar to the network tab of the browser's developer tools. The app user should be globally notified about API errors and should not need to open the developer tools.

I saw that I can use client.onRequest and client.onResponse methods to add some listeners.

Now I wanted to put this into a custom React hook. I have added the listeners inside of some useEffect hook, but it seems that I am not able to do some cleanup, because there is no way to remove the listeners yet, right?

import { useEffect, useState } from 'react';

import { client } from './client-hyperfetch';

export type ApiRequest = {
  endpoint: string;
  method: 'GET' | 'PUT' | 'POST' | 'DELETE';
  queryParams: { [key: string]: string };
  data: { [key: string]: any };
};

export type ApiRequestWithResponse = ApiRequest & {
  response: {
    status: number | null;
  }
};

export function useInspectApiActivity() {
  const [pendingRequests, setPendingRequests] = useState<ApiRequest[]>([]);
  const [requests, setRequests] = useState<ApiRequestWithResponse[]>([]);

  useEffect(() => {
    client.onRequest(async (request) => {
      setPendingRequests((pendingRequests) => [...pendingRequests, {
        endpoint: request.endpoint,
        method: request.method,
        queryParams: request.queryParams,
        data: request.data,
      }]);

      return request;
    });

    client.onResponse(async (response, request) => {
      setRequests((existingRequests) => [...existingRequests, {
        endpoint: request.endpoint,
        method: request.method,
        queryParams: request.queryParams,
        data: request.data,
        response: {
          status: response.status,
        },
      }]);

      // TODO: remove request from pendingRequests

      return response;
    });

    return () => {
      // I would like to remove the listeners here, but I don't know how to do that.
    };
  }, []);

  return {
    pendingRequests,
    requests,
  };
}

Is there some way to remove the listeners, or would you recommend another approach?

GerasNyx commented 1 year ago

Hi @stefanullinger, we will add the methods for removing listeners this week - that's a good idea. If you need to remove it now, I guess the current approach would be:

  1. Assign your callback function to the variable.
    const requestCallback = ...
    const resposneCallback = ...
  2. Add it as you do:
    client.onRequest(requestCallback)
    client.onResponse(responseCallback)
  3. Filter it out from client callbacks:
    client.__onRequestCallbacks = client.__onRequestCallbacks.filter((callback) => callback !== requestCallback);
    client.__onResponseCallbacks = ...

    Also, we have it on our roadmap to add the hook that allows you to look up all the requests!

github-actions[bot] commented 1 year ago

:tada: This issue has been resolved in version 5.5.0 :tada:

The release is available on:

Your semantic-release bot :package::rocket: