skip to Main Content

I have the following search bar with

        <div class="d-flex">
            <input class="form-control me-2" type="search" placeholder="Search Products" aria-label="Search" id="searchProduct">
        </div>

I would like to fire an onInput event whenever user types it will call an API.

const debounce = (fn, delay = 1000) => {
  let timerId = null;
  return (...args) => {
    clearTimeout(timerId);
    timerId = setTimeout(() => fn(...args), delay);
  };
};
let controller = new AbortController();
let signal = controller.signal;

const fetchProducts = (value, signal) => {
  axios
    .get(`/FYP-ECOMMERCE/api/products?search=${value}`, {
      signal, // Pass the AbortSignal to the request
    })
    .then((response) => {
      return response.data;
    })
    .then((data) => {
      console.log(data);
    })
    .catch((error) => {
      if (error.name === "AbortError") {
        // Handle the request abortion (e.g., ignore or log it)
        console.log("Request was aborted");
      } else {
        // Handle other errors
        console.error("An error occurred:", error);
      }
    });
};

const onInput = debounce(fetchProducts, 500);

const searchBar = document.getElementById("searchProduct");

searchBar.addEventListener("input", (e) => {
  const term = e.target.value;
  if (term !== "") {
    controller.abort();
    controller = new AbortController();
    signal = controller.signal;
    onInput(e.target.value, signal);
  }
});

So my question how to combine debounce and abortcontroller , so that the previous request gets aborted.

Thank you

2

Answers


    1. return controller in fethcProducts fn === create a new controller for each request
    const fetchProducts = (value, signal) => {
      const controller = new AbortController();
    
      axios
        .get(`/FYP-ECOMMERCE/api/products?search=${value}`, {
          signal: controller.signal,
        })
        .then((response) => {
          return response.data;
        })
        .then((data) => {
          console.log(data);
        })
        .catch((error) => {
          if (error.name === "AbortError") {
            console.log("Request was aborted");
          } else {
            console.error("An error occurred:", error);
          }
        });
      return controller
    };
    

    this will give you control over the controller

    1. Make the onInput a general function, add controller as param so that if controller exists, the current request will be aborted and fetch again with a new controller
    // ASIS
    const onInput = debounce(fetchProducts, 500);
    
    // TOBE
    const onInput = debounce((fn, value, controller) => {
      if (controller) controller.abort();
      return fn(value); // if you make a fetch fn that returns a controller, you can reuse it.
    }, 500);
    
    1. Other codes
    let currController;
    
    const searchBar = document.getElementById("searchProduct");
    
    // Separate handler so that you can remove the event
    function searchBarHandler(e) {
      const term = e.target.value;
      // `if` transforms the `term` as boolean to evaluate, "" will return false
      if (term) {
        currController = onInput(fetchProducts, term, currController)
      }
    } 
    
    searchBar.addEventListner("input", searchBarHandler);
    

    the onInput clearly can be written in a better way, but for now, I think this should do

    Login or Signup to reply.
  1. Within your approach, the onInput will be called every time user click button. However, thanks to debounce, the truth fetchProducts will be called only once every 500ms and no more.

    Therefore if you just simply need to call "abort" at the beginning of fetchProducts

    // 1. make your controller global and `null`
    let controller = null
    
    // 2. you don't need `signal` here
    const fetchProducts = (value) => {
        if (controller) {
            controller.abort()
        }
    
        controller = new AbortController()
    
        axios
            .get(`/FYP-ECOMMERCE/api/products?search=${value}`, {
                 controller.signal, // from signal to controller.signal
            })
            // put the rest of your code here...
    }
    

    You can learn more about it with the example from MDN
    https://developer.mozilla.org/en-US/docs/Web/API/AbortController

    Bonus:

    Since the controller is outside the fetch function, if you have a Cancel/Abort button, just simply do the same

    onCancelClick() {
        if (controller) {
            controller.abort()
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search