DEV Community

collegewap
collegewap

Posted on • Edited on • Originally published at codingdeft.com

Cancelling previous requests in Search bar using Axios in React

Why do we really need to cancel?

If you are thinking what is the use of canceling? Apart from the obvious reason for saving the resources, there is one more reason: That is, if the response of the first request arrives after the response of the second request, then we might render inconsistent data.

Of course, we can make use of a debounce value. However, if the user types slower than the set debounce value, then it might not help!

The Solution

The solution here is to cancel the previous request. This can be done by storing a reference to the Axios call in a variable and canceling whenever a new request is triggered.

Let's dive in:

Setup a mock server

For our demonstration let's set up a json-server
Install the json-server globally. Installing globally because json-server will come handy anytime!

npm install -g json-server
Enter fullscreen mode Exit fullscreen mode

Create a db.json file in the place of your choice with the following JSON data:

{"animals":[{"id":0,"name":"Aardvark"},{"id":1,"name":"Albatross"},{"id":2,"name":"Alligator"},{"id":3,"name":"Alpaca"},{"id":4,"name":"Ant"},{"id":5,"name":"Anteater"},{"id":6,"name":"Antelope"},{"id":7,"name":"Ape"},{"id":8,"name":"Armadillo"},{"id":9,"name":"Donkey"},{"id":10,"name":"Baboon"},{"id":11,"name":"Badger"},{"id":12,"name":"Barracuda"},{"id":13,"name":"Bat"},{"id":14,"name":"Bear"},{"id":15,"name":"Beaver"},{"id":16,"name":"Bee"},{"id":17,"name":"Bison"},{"id":18,"name":"Boar"},{"id":19,"name":"Buffalo"},{"id":20,"name":"Butterfly"},{"id":21,"name":"Camel"},{"id":22,"name":"Capybara"},{"id":23,"name":"Caribou"},{"id":24,"name":"Cassowary"},{"id":25,"name":"Cat"},{"id":26,"name":"Caterpillar"},{"id":27,"name":"Cattle"},{"id":28,"name":"Chamois"},{"id":29,"name":"Cheetah"},{"id":30,"name":"Chicken"},{"id":31,"name":"Chimpanzee"},{"id":32,"name":"Chinchilla"},{"id":33,"name":"Chough"},{"id":34,"name":"Clam"},{"id":35,"name":"Cobra"},{"id":36,"name":"Cockroach"},{"id":37,"name":"Cod"},{"id":38,"name":"Cormorant"},{"id":39,"name":"Coyote"},{"id":40,"name":"Crab"},{"id":41,"name":"Crane"},{"id":42,"name":"Crocodile"},{"id":43,"name":"Crow"},{"id":44,"name":"Curlew"},{"id":45,"name":"Deer"},{"id":46,"name":"Dinosaur"},{"id":47,"name":"Dog"},{"id":48,"name":"Dogfish"},{"id":49,"name":"Dolphin"},{"id":50,"name":"Dotterel"},{"id":51,"name":"Dove"},{"id":52,"name":"Dragonfly"},{"id":53,"name":"Duck"},{"id":54,"name":"Dugong"},{"id":55,"name":"Dunlin"},{"id":56,"name":"Eagle"},{"id":57,"name":"Echidna"},{"id":58,"name":"Eel"},{"id":59,"name":"Eland"},{"id":60,"name":"Elephant"},{"id":61,"name":"Elk"},{"id":62,"name":"Emu"},{"id":63,"name":"Falcon"},{"id":64,"name":"Ferret"},{"id":65,"name":"Finch"},{"id":66,"name":"Fish"},{"id":67,"name":"Flamingo"},{"id":68,"name":"Fly"},{"id":69,"name":"Fox"},{"id":70,"name":"Frog"},{"id":71,"name":"Gaur"},{"id":72,"name":"Gazelle"},{"id":73,"name":"Gerbil"},{"id":74,"name":"Giraffe"},{"id":75,"name":"Gnat"},{"id":76,"name":"Gnu"},{"id":77,"name":"Goat"},{"id":78,"name":"Goldfinch"},{"id":79,"name":"Goldfish"},{"id":80,"name":"Goose"},{"id":81,"name":"Gorilla"},{"id":82,"name":"Goshawk"},{"id":83,"name":"Grasshopper"},{"id":84,"name":"Grouse"},{"id":85,"name":"Guanaco"},{"id":86,"name":"Gull"},{"id":87,"name":"Hamster"},{"id":88,"name":"Hare"},{"id":89,"name":"Hawk"},{"id":90,"name":"Hedgehog"},{"id":91,"name":"Heron"},{"id":92,"name":"Herring"},{"id":93,"name":"Hippopotamus"},{"id":94,"name":"Hornet"},{"id":95,"name":"Horse"},{"id":96,"name":"Human"},{"id":97,"name":"Hummingbird"},{"id":98,"name":"Hyena"},{"id":99,"name":"Ibex"},{"id":100,"name":"Ibis"},{"id":101,"name":"Jackal"},{"id":102,"name":"Jaguar"},{"id":103,"name":"Jay"},{"id":104,"name":"Jellyfish"},{"id":105,"name":"Kangaroo"},{"id":106,"name":"Kingfisher"},{"id":107,"name":"Koala"},{"id":108,"name":"Kookabura"},{"id":109,"name":"Kouprey"},{"id":110,"name":"Kudu"},{"id":111,"name":"Lapwing"},{"id":112,"name":"Lark"},{"id":113,"name":"Lemur"},{"id":114,"name":"Leopard"},{"id":115,"name":"Lion"},{"id":116,"name":"Llama"},{"id":117,"name":"Lobster"},{"id":118,"name":"Locust"},{"id":119,"name":"Loris"},{"id":120,"name":"Louse"},{"id":121,"name":"Lyrebird"},{"id":122,"name":"Magpie"},{"id":123,"name":"Mallard"},{"id":124,"name":"Manatee"},{"id":125,"name":"Mandrill"},{"id":126,"name":"Mantis"},{"id":127,"name":"Marten"},{"id":128,"name":"Meerkat"},{"id":129,"name":"Mink"},{"id":130,"name":"Mole"},{"id":131,"name":"Mongoose"},{"id":132,"name":"Monkey"},{"id":133,"name":"Moose"},{"id":134,"name":"Mosquito"},{"id":135,"name":"Mouse"},{"id":136,"name":"Mule"},{"id":137,"name":"Narwhal"},{"id":138,"name":"Newt"},{"id":139,"name":"Nightingale"},{"id":140,"name":"Octopus"},{"id":141,"name":"Okapi"},{"id":142,"name":"Opossum"},{"id":143,"name":"Oryx"},{"id":144,"name":"Ostrich"},{"id":145,"name":"Otter"},{"id":146,"name":"Owl"},{"id":147,"name":"Oyster"},{"id":148,"name":"Panther"},{"id":149,"name":"Parrot"},{"id":150,"name":"Partridge"},{"id":151,"name":"Peafowl"},{"id":152,"name":"Pelican"},{"id":153,"name":"Penguin"},{"id":154,"name":"Pheasant"},{"id":155,"name":"Pig"},{"id":156,"name":"Pigeon"},{"id":157,"name":"Pony"},{"id":158,"name":"Porcupine"},{"id":159,"name":"Porpoise"},{"id":160,"name":"Quail"},{"id":161,"name":"Quelea"},{"id":162,"name":"Quetzal"},{"id":163,"name":"Rabbit"},{"id":164,"name":"Raccoon"},{"id":165,"name":"Rail"},{"id":166,"name":"Ram"},{"id":167,"name":"Rat"},{"id":168,"name":"Raven"},{"id":169,"name":"Red deer"},{"id":170,"name":"Red panda"},{"id":171,"name":"Reindeer"},{"id":172,"name":"Rhinoceros"},{"id":173,"name":"Rook"},{"id":174,"name":"Salamander"},{"id":175,"name":"Salmon"},{"id":176,"name":"Sand Dollar"},{"id":177,"name":"Sandpiper"},{"id":178,"name":"Sardine"},{"id":179,"name":"Scorpion"},{"id":180,"name":"Seahorse"},{"id":181,"name":"Seal"},{"id":182,"name":"Shark"},{"id":183,"name":"Sheep"},{"id":184,"name":"Shrew"},{"id":185,"name":"Skunk"},{"id":186,"name":"Snail"},{"id":187,"name":"Snake"},{"id":188,"name":"Sparrow"},{"id":189,"name":"Spider"},{"id":190,"name":"Spoonbill"},{"id":191,"name":"Squid"},{"id":192,"name":"Squirrel"},{"id":193,"name":"Starling"},{"id":194,"name":"Stingray"},{"id":195,"name":"Stinkbug"},{"id":196,"name":"Stork"},{"id":197,"name":"Swallow"},{"id":198,"name":"Swan"},{"id":199,"name":"Tapir"},{"id":200,"name":"Tarsier"},{"id":201,"name":"Termite"},{"id":202,"name":"Tiger"},{"id":203,"name":"Toad"},{"id":204,"name":"Trout"},{"id":205,"name":"Turkey"},{"id":206,"name":"Turtle"},{"id":207,"name":"Viper"},{"id":208,"name":"Vulture"},{"id":209,"name":"Wallaby"},{"id":210,"name":"Walrus"},{"id":211,"name":"Wasp"},{"id":212,"name":"Weasel"},{"id":213,"name":"Whale"},{"id":214,"name":"Wildcat"},{"id":215,"name":"Wolf"},{"id":216,"name":"Wolverine"},{"id":217,"name":"Wombat"},{"id":218,"name":"Woodcock"},{"id":219,"name":"Woodpecker"},{"id":220,"name":"Worm"},{"id":221,"name":"Wren"},{"id":222,"name":"Yak"},{"id":223,"name":"Zebra"}]}
Enter fullscreen mode Exit fullscreen mode

Navigate to the directory where db.json is placed and run the below command:

json-server -p 4000 db.json --delay 3000
Enter fullscreen mode Exit fullscreen mode
  • We are specifying a port here since json-server will use 3000 by default, which we will need for React!
  • A delay of 3 seconds to simulate a slow API, so that we can cancel!

Open the below URL in a browser and you should be able to see the response:

http://localhost:4000/animals
Enter fullscreen mode Exit fullscreen mode

Lets Go the Client Side!

Create a new React Project using CRA:

create-react-app axios-cancel --use-npm
Enter fullscreen mode Exit fullscreen mode

Now open the project in your favorite editor and install Axios using the following command:

npm i axios
Enter fullscreen mode Exit fullscreen mode

Update the App.js with the following code:

import axios from "axios";
import React from "react";
import "./App.css";

function App() {
  const handleSearchChange = async (e) => {
    const searchTerm = e.target.value;
    const results = await axios.get(
      `http://localhost:4000/animals?q=${searchTerm}`
    );
    console.log("Results for " + searchTerm + ": " + results.data);
  };

  return (
    <div style={{ marginTop: "3em", textAlign: "center" }}>
      <input
        style={{ width: "60%", height: "1.5rem" }}
        type="text"
        placeholder="Search"
        onChange={handleSearchChange}
      />
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

In the above code, we added

  • A search bar using an input element
  • An onChange handler for the input element called handleSearchChange which will be triggered every time we enter a text on the search bar.
  • And finally, we are calling the API using Axios by passing the search term.

Now if we try searching for cat we will see that 3 different calls made and all 3 responses are logged.

Search Screenshot

We really don't need the previous 2 responses, which we can cancel when the next request is made.

The fix!

Update the handleSearchChange as below:

  let cancelToken;
  const handleSearchChange = async (e) => {
    const searchTerm = e.target.value;

    //Check if there are any previous pending requests
    if (typeof cancelToken != typeof undefined) {
      cancelToken.cancel("Operation canceled due to new request.");
    }

    //Save the cancel token for the current request
    cancelToken = axios.CancelToken.source();

    try {
      const results = await axios.get(
        `http://localhost:4000/animals?q=${searchTerm}`,
        { cancelToken: cancelToken.token } //Pass the cancel token to the current request
      );
      console.log("Results for " + searchTerm + ": ", results.data);
    } catch (error) {
      console.log(error);
    }
  };
Enter fullscreen mode Exit fullscreen mode

Note that cancelToken is declared outside the function so that the previous token is retained.

Now if we try to search, we could see that previous requests are canceled and the result is logged only once. That's the one for the latest term. Exactly what we wanted!

Search screenshot

Here is the final code:

import axios from "axios";
import React from "react";
import "./App.css";

function App() {
  let cancelToken;
  const handleSearchChange = async (e) => {
    const searchTerm = e.target.value;

    //Check if there are any previous pending requests
    if (typeof cancelToken != typeof undefined) {
      cancelToken.cancel("Operation canceled due to new request.");
    }

    //Save the cancel token for the current request
    cancelToken = axios.CancelToken.source();

    try {
      const results = await axios.get(
        `http://localhost:4000/animals?q=${searchTerm}`,
        { cancelToken: cancelToken.token } //Pass the cancel token to the current request
      );
      console.log("Results for " + searchTerm + ": ", results.data);
    } catch (error) {
      console.log(error);
    }
  };

  return (
    <div style={{ marginTop: "3em", textAlign: "center" }}>
      <input
        style={{ width: "60%", height: "1.5rem" }}
        type="text"
        placeholder="Search"
        onChange={handleSearchChange}
      />
    </div>
  );
}

export default App;

Enter fullscreen mode Exit fullscreen mode

Full source code can be downloaded from github

Top comments (3)

Collapse
 
darijavan profile image
RANDRIANANDRAINA Hajanirina Ridjvan

Thanks for the trick.

I think you'd better use reference instead of normal plain variable when it's about storing the cancelToken otherwise the desired value will be lost on each re-render (I know, in this example there won't be any re-render at all since there is no state involved in there, but if it was the case, I think it won't work as expected)

Collapse
 
asadabdulhameed profile image
Asad Jakhar

Thanks for the helpful article

Collapse
 
mohsinalisoomro profile image
MOHSIN ALI SOOMRO

Thanks helpfully article to learn axios