Esempi di hooks: richieste di rete di tipo comando | Aulab

GUIDE PER ASPIRANTI PROGRAMMATORI

Esempi di hooks: richieste di rete di tipo comando

Nelle sezioni precedenti, abbiamo gestito in modo dichiarativo la richiesta di rete per creare un elemento della lista di cose da fare:   // File: src/components/TodoItemForm.jsx // ... const NetworkRequestType = { idle: "idle", loading: "loading", success: "success", failure: "failure", }; function makeIdleNetworkRequest() { return { type: NetworkRequestType.idle }; } function makeLoadingNetworkRequest() { return {…

Lezione 26 / 41
Enza Neri
Immagine di copertina

Vuoi avviare una nuova carriera o fare un upgrade?

Trova il corso Digital & Tech più adatto a te nel nostro catalogo!

Nelle sezioni precedenti, abbiamo gestito in modo dichiarativo la richiesta di rete per creare un elemento della lista di cose da fare:

// File: src/components/TodoItemForm.jsx

// ...

const NetworkRequestType = {
  idle: "idle",
  loading: "loading",
  success: "success",
  failure: "failure",
};

function makeIdleNetworkRequest() {
  return { type: NetworkRequestType.idle };
}

function makeLoadingNetworkRequest() {
  return { type: NetworkRequestType.loading };
}

function makeSuccessfulNetworkRequest(response) {
  return { type: NetworkRequestType.success, response };
}

function makeFailedNetworkRequest(error) {
  return { type: NetworkRequestType.failure, error };
}

export default function TodoItemForm({ onSubmit }) {
  // ...
  const [networkState, setNetworkState] = useState(makeIdleNetworkRequest());

  // ...
  const onFormSubmit = (event) => {
    event.preventDefault();
    setNetworkState(makeLoadingNetworkRequest());

    createTodoItem(todoItem).then(
      (todoItem) => {
        setNetworkState(makeSuccessfulNetworkRequest(todoItem));
        onSubmit(todoItem);
        setTodoItem({ description: "", isDone: false });
      },
      (error) => {
        setNetworkState(makeFailedNetworkRequest(error.message));
      }
    );
  };

  // ...
}

Questo tipo di funzionamento non vale solo per la richiesta di rete che crea l’elemento della lista di cose da fare, ma per tutte le richieste di rete di tipo “comando”, cioè quelle richieste che partono all’occorrenza, come al click di un bottone (a differenza, per esempio, di una richiesta per ottenere gli elementi già esistenti, che partirebbe nel momento in cui viene visualizzata la pagina).

Il concetto di richiesta di rete di tipo “comando” è un ottimo argomento per una funzione hook. Possiamo creare una funzione hook, che potremmo chiamare useCommand. – ricordiamoci che le funzioni hook sono funzioni che hanno un nome che inizia per use e che possono, al loro interno, fare uso di altre funzioni hook, come useState – .

Avendo a che fare con una funzione, dobbiamo prima di tutto decidere quali sono gli argomenti che riceve (il suo input) e qual è il risultato che restituisce (il suo output).

Per quanto riguarda l’output, ci servono sicuramente lo stato della richiesta di rete (idle, loading, success o failure) e una funzione da chiamare per far partire la richiesta, per esempio al click del bottone che dice “submit”.

La funzione per far partire la richiesta prenderà come argomenti i dati dell’elemento della lista, che abbiamo a disposizione in un momento del tempo diverso. Come nell’esempio dei validatori del form, ci sono alcuni dati che abbiamo a disposizione nel momento in cui scriviamo il codice, come l’URL a cui inviare la richiesta o il metodo HTTP che vogliamo usare. Altri dati invece – come la descrizione dell’elemento – li avremo a disposizione nel momento in cui l’utente li inserirà nel form.

Non avendo a disposizione un back-end, modifichiamo la funzione che finge di inviare una richiesta di rete per accettare un percorso e un metodo HTTP, come se fosse la funzione fetch del browser:

// File: src/mock/sendNetworkRequest.js
export function sendNetworkRequest({ path, method, data, shouldFail = false }) {
  console.log(`Would send a ${method} request to ${path}`);

  return new Promise((resolve, reject) => {
    window.setTimeout(() => {
      if (shouldFail) {
        reject(new Error("Filed!"));
      } else {
        resolve(data);
      }
    }, 1000);
  });
}

In un caso realistico, l’URL del server a cui inviamo la richiesta sarebbe salvato in un file env, che conterrebbe le variabili d’ambiente. La funzione per inviare le richieste accetterebbe come argomento, come nel nostro caso, il percorso (path) per completare l’URL. Ad esempio, la variabile d’ambiente potrebbe contenere il valore https://my-api.com e il path potrebbe essere /todo, così che la richiesta venga inviata a https://my-api.com/todo.

Per informazioni dettagliate sulle variabili d’ambiente in React, puoi fare riferimento alla guida ufficiale.

Ora che possiamo fingere di mandare una richiesta di rete, dividiamo in due fasi l’invio perché, come abbiamo detto, path e method sono disponibili sempre, mentre data sarà disponibile in futuro. Creiamo una nuova cartella hooks per raccogliere le nostre funzioni hook, in cui possiamo creare un nuovo file useCommand.js:

// File: src/hooks/useCommand.js

import { useState } from "react";
import { sendNetworkRequest } from "../mock/sendNetworkRequest";

export const NetworkRequestType = {
  idle: "idle",
  loading: "loading",
  success: "success",
  failure: "failure",
};

function makeIdleNetworkRequest() {
  return { type: NetworkRequestType.idle };
}

function makeLoadingNetworkRequest() {
  return { type: NetworkRequestType.loading };
}

function makeSuccessfulNetworkRequest(response) {
  return { type: NetworkRequestType.success, response };
}

function makeFailedNetworkRequest(error) {
  return { type: NetworkRequestType.failure, error };
}

export function useCommand({ path, method }) {
  const [networkRequest, setNetworkRequest] = useState(
    makeIdleNetworkRequest()
  );

  const sendCommand = (data) => {
    setNetworkRequest(makeLoadingNetworkRequest());

    return sendNetworkRequest({ path, method, data }).then(
      (response) => {
        setNetworkRequest(makeSuccessfulNetworkRequest(response));
        return Promise.resolve(response);
      },
      (error) => {
        setNetworkRequest(makeFailedNetworkRequest(error));
        return Promise.reject(error);
      }
    );
  };

  return [networkRequest, sendCommand];
}

Rispetto agli esempi precedenti, l’unica novità è la funzione useCommand, il resto è stato solo spostato da TodoItemForm (perché stiamo rendendo generica la gestione di una richiesta di rete di tipo “comando”).

Prima di capire perché useCommand è conveniente, utilizziamolo all’interno del componente TodoItemForm:

// File: src/components/TodoItemForm.jsx

import { useState } from "react";
import "./TodoItemForm.css";
import { NetworkRequestType, useCommand } from "../hooks/useCommand";

export default function TodoItemForm({ onSubmit }) {
  const [todoItem, setTodoItem] = useState({
    description: "",
    isDone: false,
  });

  const [createTodoItemNetworkRequest, createTodoItem] = useCommand({
    path: "/todo",
    method: "POST",
  });

  // onDescriptionChange e onIsDoneChange restano uguali

  const onFormSubmit = (event) => {
    event.preventDefault();

    createTodoItem(todoItem).then((response) => {
      onSubmit(response);
      setTodoItem({ description: "", isDone: false });
    });
  };

  const isLoading =
    createTodoItemNetworkRequest.type === NetworkRequestType.loading;

  return (
    <form className="TodoItemForm" onSubmit={onFormSubmit}>
      {/* anche il form rimane uguale */}
    </form>
  );
}

Abbiamo spostato tutta la gestione della richiesta di rete all’interno di useCommand.

La nuova funzione hook useCommand accetta come input il percorso e il metodo HTTP della chiamata di rete. L’argomento che accetta è uno solo, che racchiude entrambe le informazioni (path e method) per la stessa ragione per cui abbiamo fatto la stessa cosa con gli stati e per cui React la fa con le props: gli argomenti devono contenere tutte e sole le informazioni che descrivono l’argomento della funzione, in questo caso “la chiamata di rete quando non abbiamo ancora i dati a disposizione”.

La nuova funzione hook useCommand restituisce un array con due elementi: lo stato di rete – che utilizziamo nello stesso modo in cui l’abbiamo utilizzato precedentemente –  e la funzione per far partire la richiesta di rete. La funzione per far partire la richiesta di rete accetta come argomento i dati da inviare, sottolineando il fatto che i dati sono disponibili in un momento del tempo diverso dal percorso e dal metodo HTTP che descrivono la richiesta.

Nota: il fatto che useCommand restituisca un array con due elementi, come fa useState, è una coincidenza. Non c’è nessuna regola sul formato dei valori restituiti dalle funzioni hook, né sul formato o sulla quantità di argomenti che possono accettare.

Sei indeciso sul percorso? 💭

Parliamone! Scrivici su Whatsapp e risponderemo a tutte le tue domande per capire quale dei nostri corsi è il più adatto alle tue esigenze.

Oppure chiamaci al 800 128 626