react-logo

in Informatica, react

React onChange – Eseguire solo una chiamata

Reading Time: 3 minutes

Uno dei problemi in cui mi sono imbattuto recentemente è la necessità di annullare chiamate a REST API. Il caso tipico è quello di una inputbox html in cui l’utente digita del testo e ad ogni variazione, viene eseguita una chiamata REST.

Questo tipo di comportamento è piuttosto comune nelle searchbar di ricerca.

Il primo motivo per dover annullare una richiesta è legato all’utilizzo delle risorse: ad ogni modifica viene richiamata l’Api corrispondente, andando ad utilizzare risorse lato backend (per le ricerche) e lato frontend per il render del risultato.

Il secondo motivo sono le chiamate asincrone: le risposte dal server potrebbero arrivare in maniera del tutto random rendendo i dati inconsistenti.

Si rende quindi necessario un sistema che consenta di gestire in maniera sincrona ogni singola chiamata, cambiando di fatto il comportamento da asincrono a sincrono. Un’altra soluzione potrebbe essere quella di utilizzare un timeout, in modo da far scattare la chiamata remota solo dopo un intervallo prestabilito. In questo caso,però, entra in gioco il tempo di digitazione tra ogni singolo carattere.

Ipotizzando di poter accodare in qualche modo le chiamate alla REST Api, si potrebbe provare ad annullare tutte le chiamate precedenti e mantenere attiva sempre e solo l’ultima.

Utilizzando il client http Axios disponibile su github , è possibile memorizzare un riferimento ad ogni singola chiamata in una variabile ed annullarla ogni volta che ne viene generata una nuova.

Partendo da una semplice applicazione react :

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>
      <input type="text" placeholder="Search" onChange={handleSearchChange} />
    </div>
  )
}
export default App

L’applicazione è molto semplice, si tratta di una casella di testo che ad ogni modifica del testo richiama la funzione handleSearchChange che a sua volta esegue una chiamata GET tramite axios.

La REST API che viene richiamata ritorna un risultato in base al termine passato come parametro. Analizzando la console log è possibile visualizzare l’esecuzione asincrona delle singole chiamate, con le risposte asincrone (e quindi potenzialmente non sequenziali).

console log react

Introduzione di un cancelToken

Ci si trova quindi nella situazione in cui ad ogni variazione del testo della inputbox si verfica una chiamata alla REST API.

Ottimizziamo il codice e l’uso delle risorse, introducendo il concetto di cancelToken che consente di annullare una chiamata axios in fase di esecuzione. Nel codice precedente introduciamo una variabile chiamata cancelToken esterna alla chiamata handleSearchChange. In questo modo questa variabile è valorizzata con il token dell’ultima chiamata che è stata eseguita. In questo modo dalla nuova chiamata sarà possibile annullare la chiamata precedente.

let cancelToken
const handleSearchChange = async e => {
  
  const searchTerm = e.target.value
  
  if (typeof cancelToken != typeof undefined) {
    cancelToken.cancel("Operazione precedente annullata.")
  }
  //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)
  }

In pratica quello che stiamo facendo è la memorizzazione all’interno della variabile cancelToken del puntatore dell’ultima chiamata eseguita. La nuova chiamata effettuerà l’annullamento della chiamata precedente mandando in esecuzione quella corrente.

In questo modo ci sarà sempre una sola chiamata attiva, risparmiando di fatto risorse.