CONTATTACI

Guide per aspiranti programmatori

Programmatore che esamina react al microscopio
Lezione 22 / 41

Stati con liste in React

Pronto a mettere le mani in pasta? Mettiamo in pratica quello che abbiamo appena imparato!

Modifichiamo la nostra applicazione per mantenere uno stato della lista di cose da fare. Alla creazione di un nuovo elemento, dopo aver mandato la nostra finta richiesta di rete, aggiungiamo il nuovo elemento in cima alla lista.

Alla fine di questa modifica, la nostra applicazione avrà due stati: lo stato della lista di cose da fare, che possiamo chiamare todoList e lo stato dell’elemento che sta venendo creato, che coincide con lo stato todoItem che abbiamo in questo momento.

Ricordi quando dicevamo che avere lo stato todoItem all’interno di App poteva non essere la soluzione migliore? Bene, questo è il momento di riportarlo in TodoItemForm. Lo stato todoItem è legato solamente all’aspetto del form (in altre parole, l’aspetto del form dipende dallo stato todoItem), mentre App non si occupa di quella parte del sistema. App avrà invece il compito di occuparsi della lista intera, cioè il nuovo stato todoList.

 

// File: src/components/TodoItemForm.jsx

import { createTodoItem } from "../mock/createTodoItem";
import { useState } from "react";
import "./TodoItemForm.css";

/* ... */

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

  const [networkState, setNetworkState] = useState(makeIdleNetworkRequest());

  const onDescriptionChange = (description) => {
    setTodoItem((todoItem) => {
      return {
        ...todoItem,
        description,
      };
    });
  };

  const onIsDoneChange = (isDone) => {
    setTodoItem((todoItem) => {
      return {
        ...todoItem,
        isDone,
      };
    });
  };

  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));
      }
    );
  };

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

  return (
    <form className="TodoItemForm" onSubmit={onFormSubmit}>
      <input
        type="checkbox"
        checked={todoItem.isDone}
        onChange={(event) => {
          onIsDoneChange(event.currentTarget.checked);
        }}
        disabled={isLoading}
      />
      <input
        type="text"
        placeholder="Description"
        value={todoItem.description}
        onInput={(event) => {
          onDescriptionChange(event.currentTarget.value);
        }}
        disabled={isLoading}
      />
      <input type="submit" value="Create" disabled={isLoading} />
      {networkState.type === NetworkRequestType.failure ? (
        <p style={{ color: "red" }}>{networkState.error}</p>
      ) : null}
    </form>
  );
}

Abbiamo riportato lo stato todoItem all’interno di TodoItemForm. Al submit, inviamo la richiesta di rete per il salvataggio dei dati direttamente da qui. Se il salvataggio avviene correttamente, allora notifichiamo App dell’avvenuta creazione dell’elemento, passando alla prop onSubmit i relativi dati.

Con questo cambiamento, il componente App non avrà altre interazioni con TodoItemForm se non nel caso in cui un nuovo elemento è stato creato. Tutta la fase di creazione dell’elemento è gestita internamente dal componente TodoItemForm, creando una separazione dei compiti ben definita.

 

// File: src/App.jsx

import { useState } from "react";
import "./App.css";
import Panel from "./components/Panel";
import TodoItemForm from "./components/TodoItemForm";

export default function App() {
  const [todoList, setTodoList] = useState([]);

  const onTodoItemFormSubmit = (todoItem) => {
    return setTodoList((todoList) => [todoItem, ...todoList]);
  };

  return (
    <div className="App">
      <Panel>
        <TodoItemForm onSubmit={onTodoItemFormSubmit} />
      </Panel>
    </div>
  );
}

La quantità di logica gestita dal componente App è diminuita sostanzialmente e si vede dalle dimensioni del componente. Ora App ha due compiti: mantenere lo stato della lista di cose da fare e aggiungere nuovi elementi che arrivano da TodoItemForm, sapendo che tutta la fase di validazione e salvataggio è gestita internamente dal componente figlio.

Se hai ESLint acceso e/o stai usando Create React App, noterai un avvertimento: non stiamo utilizzando todoList! Per adesso, questa cosa non ha importanza ma l’avvertimento ci aiuterà a tenerlo presente.

 

Le liste in JSX

Quando abbiamo uno stato che contiene un array, in React, utilizziamo tipicamente il metodo map di Array per trasformarlo in un array di elementi JSX.

 

// File: src/App.tsx

// ...

export default function App() {
  const [todoList, setTodoList] = useState([]);

  // ...

  return (
    <div className="App">
      <div className="TodoList">
        {todoList.map((todoItem, index) => {
          return (
            <div className="TodoItem" key={index}>
              <input type="checkbox" value={todoItem.isDone} disabled />
              <span>{todoItem.description}</span>
            </div>
          );
        })}
      </div>
      {/* ... */}
    </div>
  );
}

Spoiler alert: nei prossimi paragrafi parleremo un sacco di componenti, elementi, proprietà e attributi. Per fare un rapidissimo ripasso: in JSX, parliamo di elementi e attributi in riferimento agli elementi HTML, riconoscibili per la prima lettera minuscola. Parliamo, invece, di componenti e proprietà (o props) in riferimento ai componenti di React, riconoscibili dalla prima lettera maiuscola. Quando si parla di JSX in generale, parlare di componenti o elementi è equivalente, perché JSX contiene un misto di entrambi.

Abbiamo trasformato una lista di elementi di cose da fare, con le loro proprietà description e isDone, in una lista di elementi JSX. Possiamo notare un paio di cose:

 

  • Per ogni concetto della nostra app, c’è un elemento HTML: l’intera app è circondata da un div, la lista è rappresentata da un div, c’è un div per ogni elemento della lista e un elemento HTML per ogni proprietà dell’elemento della lista (span per description, input[type=checkbox] per isDone). Creare una correlazione 1:1 tra parti della struttura dati ed elementi HTML può essere incredibilmente utile per mantenere un alto livello di confidenza sulla qualità del codice.
  • L’elemento div.TodoItem ha un attributo/proprietà key. key è una delle tre proprietà sempre valide in React (abbiamo visto children in precedenza) e definisce l’unicità di un elemento o componente in una lista. Merita una sezione a sé. Approfondiamola nel prossimo capitolo!

Contattaci senza impegno per informazioni sul corso

Pagamento rateale

Valore della rata: A PARTIRE DA 115 €/mese.

Esempio di finanziamento 

Importo finanziato: € 2440 in 24 rate da € 115 – TAN fisso 9,55% TAEG 12,57% – importo totale del credito € 2841.

Il costo totale del credito comprende: interessi calcolati al TAN indicato, oneri fiscali (imposta di bollo sul contratto 16,00 euro*) addebitati sulla prima rata, costo mensile di gestione pratica € 3,90, spesa di istruttoria € 0,00, spesa per invio rendicontazione periodica cartacea € 0,98 (o spesa per invio rendicontazione periodica cartacea € 0,00), imposta di bollo su rendicontazione periodica € 0,00. Modalità di rimborso obbligatoria: addebito diretto su c/c. La scadenza delle rate è determinata dal giorno della liquidazione del contratto; la data di scadenza delle rate è prevista il giorno 15 del mese. L’importo di ciascuna rata comprende una quota di capitale crescente e interessi decrescente secondo un piano di ammortamento “alla francese”. Offerta valida dal 01/01/2024 al 31/12/2024.

Messaggio pubblicitario con finalità promozionale. Per le informazioni precontrattuali richiedere sul punto vendita il documento “Informazioni europee di base sul credito ai consumatori” (SECCI) e copia del testo contrattuale. Salvo approvazione di Sella Personal Credit S.p.A. Aulab S.r.l. opera quale intermediario del credito NON in esclusiva.

*In fase di richiesta del finanziamento verrà proposta la facoltà di selezionare, in alternativa all’imposta di bollo sul contratto di 16,00 euro, l’imposta sostitutiva, pari allo 0,25% dell’importo finanziato.

Pagamento rateale

Valore della rata: A PARTIRE DA 210 €/mese.

Esempio di finanziamento  

Importo finanziato: € 4500 in 24 rate da € 210,03 – TAN fisso 9,68% TAEG 11,97% – importo totale del credito € 5146,55.

Il costo totale del credito comprende: interessi calcolati al TAN indicato, oneri fiscali (imposta di bollo sul contratto 16,00 euro*) addebitati sulla prima rata, costo mensile di gestione pratica € 3,90, spesa di istruttoria € 0,00, spesa per invio rendicontazione periodica cartacea € 0,98 (o spesa per invio rendicontazione periodica cartacea € 0,00), imposta di bollo su rendicontazione periodica € 0,00. Modalità di rimborso obbligatoria: addebito diretto su c/c. La scadenza delle rate è determinata dal giorno della liquidazione del contratto; la data di scadenza delle rate è prevista il giorno 15 del mese. L’importo di ciascuna rata comprende una quota di capitale crescente e interessi decrescente secondo un piano di ammortamento “alla francese”. Offerta valida dal 01/01/2024 al 31/12/2024.

Messaggio pubblicitario con finalità promozionale. Per le informazioni precontrattuali richiedere sul punto vendita il documento “Informazioni europee di base sul credito ai consumatori” (SECCI) e copia del testo contrattuale. Salvo approvazione di Sella Personal Credit S.p.A. Aulab S.r.l. opera quale intermediario del credito NON in esclusiva.

* In fase di richiesta del finanziamento verrà proposta la facoltà di selezionare, in alternativa all’imposta di bollo sul contratto di 16,00 euro, l’imposta sostitutiva, pari allo 0,25% dell’importo finanziato.

Contattaci senza impegno per informazioni sul corso

Scopriamo insieme se i nostri corsi fanno per te. Compila il form e aspetta la chiamata di uno dei nostri consulenti.