Table of Contents
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!
1npm install -g json-server
Create a db.json
file in the place of your choice with the following JSON data:
1{"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"}]}
Navigate to the directory where db.json
is placed and run the below command:
1json-server -p 4000 db.json --delay 3000
- 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:
1http://localhost:4000/animals
Lets Go the Client Side!
Create a new React Project using CRA:
1create-react-app axios-cancel --use-npm
Now open the project in your favorite editor and install Axios using the following command:
1npm i axios
Update the App.js with the following code:
1import axios from "axios"2import React from "react"3import "./App.css"45function App() {6 const handleSearchChange = async e => {7 const searchTerm = e.target.value8 const results = await axios.get(9 `http://localhost:4000/animals?q=${searchTerm}`10 )11 console.log("Results for " + searchTerm + ": " + results.data)12 }1314 return (15 <div style={{ marginTop: "3em", textAlign: "center" }}>16 <input17 style={{ width: "60%", height: "1.5rem" }}18 type="text"19 placeholder="Search"20 onChange={handleSearchChange}21 />22 </div>23 )24}2526export default App
In the above code, we added
- A search bar using an input element
-
An
onChange
handler for the input element calledhandleSearchChange
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.
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:
1let cancelToken2const handleSearchChange = async e => {3 const searchTerm = e.target.value45 //Check if there are any previous pending requests6 if (typeof cancelToken != typeof undefined) {7 cancelToken.cancel("Operation canceled due to new request.")8 }910 //Save the cancel token for the current request11 cancelToken = axios.CancelToken.source()1213 try {14 const results = await axios.get(15 `http://localhost:4000/animals?q=${searchTerm}`,16 { cancelToken: cancelToken.token } //Pass the cancel token to the current request17 )18 console.log("Results for " + searchTerm + ": ", results.data)19 } catch (error) {20 console.log(error)21 }22}
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!
Here is the final code:
1import axios from "axios"2import React from "react"3import "./App.css"45function App() {6 let cancelToken7 const handleSearchChange = async e => {8 const searchTerm = e.target.value910 //Check if there are any previous pending requests11 if (typeof cancelToken != typeof undefined) {12 cancelToken.cancel("Operation canceled due to new request.")13 }1415 //Save the cancel token for the current request16 cancelToken = axios.CancelToken.source()1718 try {19 const results = await axios.get(20 `http://localhost:4000/animals?q=${searchTerm}`,21 { cancelToken: cancelToken.token } //Pass the cancel token to the current request22 )23 console.log("Results for " + searchTerm + ": ", results.data)24 } catch (error) {25 console.log(error)26 }27 }2829 return (30 <div style={{ marginTop: "3em", textAlign: "center" }}>31 <input32 style={{ width: "60%", height: "1.5rem" }}33 type="text"34 placeholder="Search"35 onChange={handleSearchChange}36 />37 </div>38 )39}4041export default App
Full source code can be downloaded from github.
Do follow me on twitter where I post developer insights more often!
Comments