Condividi tramite


Creare un'app React in Visual Studio

In questa esercitazione viene creato un front-end React per un'app Web to-do list usando JavaScript e Visual Studio 2022. Il codice per questa app è disponibile in ToDoJSWebApp.

Prerequisiti

Assicurarsi di installare quanto segue:

  • Visual Studio 2022 o versione successiva. Vai alla pagina dei download di Visual Studio per installarlo gratuitamente.
  • npm (https://www.npmjs.com/), incluso in Node.js.

Creare l'app React ToDo List

  1. In Visual Studio selezionare File > Nuovo > Progetto per aprire la finestra di dialogo Crea un nuovo progetto, selezionare il modello App React JavaScript e quindi scegliere Avanti.

    Screenshot che mostra la scelta di un modello.

  2. Assegnare al progetto il nome TodoWebApp e selezionare Crea.

    Verrà creato il progetto JavaScript usando lo strumento da riga di comando vite.

  3. In Esplora soluzioni fare clic con il pulsante destro del mouse sulla cartella src e scegliere Aggiungi > Nuova cartella. e creare una nuova cartella denominata components.

    Si tratta di una convenzione comune per inserire i componenti in una cartella dei componenti, ma questo non è obbligatorio.

  4. Fare clic con il pulsante destro del mouse sulla nuova cartella, selezionare Aggiungi > File componente JSX React, dargli un nome TodoListe fare clic su Aggiungi.

    Screenshot che mostra l'aggiunta di un componente JSX.

    Verrà creato un nuovo file JSX nella cartella components.

  5. Aprire il componente TodoList e sostituire il contenuto predefinito con quanto segue:

    function TodoList() {
      return (
        <h2>TODO app contents</h2>
      );
    }
    

    Questo componente visualizza un'intestazione che verrà sostituita in un secondo momento.

    Collegare quindi questo componente nell'app. App.jsx è il componente principale che viene caricato, rappresentando l'applicazione della lista to-do. Questo componente viene usato nel file main.jsx.

  6. Nell'Esplora soluzioni, apri App.jsx, rimuovi tutte le importazioni dall'inizio e cancella il contenuto dell'istruzione return. Il file dovrebbe essere simile al seguente.

    function App() {
      return (
        <>
          <TodoList />
        </>
      );
    }
    export default App;
    
  7. Per aggiungere il componente TodoList, posizionare il cursore all'interno del frammento e quindi digitare <TodoL RETURN. In questo modo vengono aggiunti il componente e l'istruzione import.

    Screenshot che mostra l'aggiunta di un componente JSX all'app.

    Cancellare quindi i file CSS.

  8. Aprire App.css ed eliminare tutto il contenuto.

  9. Aprire Index.css e rimuovere tutto il contenuto, ad eccezione degli stili per :root:

    :root {
      font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
      line-height: 1.5;
      font-weight: 400;
      color-scheme: light dark;
      color: rgba(255, 255, 255, 0.87);
      background-color: #242424;
    }
    

Eseguire l'app

Selezionare il pulsante Avvia debug sulla barra degli strumenti o premere il tasto di scelta rapida F5.

L'app viene aperta in una finestra del browser.

Screenshot che mostra l'app in esecuzione nel browser.

Aggiungere to-do funzioni elenco all'app

È possibile lasciare l'app in esecuzione. Quando si apportano modifiche, l'app viene aggiornata automaticamente con il contenuto più recente usando il supporto per la sostituzione dei moduli a caldo di Vite. Alcune azioni, ad esempio l'aggiunta di cartelle o la ridenominazione dei file, richiedono di interrompere il debug e quindi riavviare l'app, ma in generale è possibile lasciarla in esecuzione in background durante lo sviluppo dell'app. Aprire il componente TodoList.jsx in modo che sia possibile iniziare a definirlo.

  1. In Esplora soluzioni, apri TodoList.jsx e aggiungi l'interfaccia utente necessaria per visualizzare e gestire le voci dell'elenco to-do. Sostituire il contenuto con il codice seguente:

    function TodoList() {
      return (
        <div>
          <h1>TODO</h1>
          <div>
            <input type="text" placeholder="Enter a task" required aria-label="Task text" />
            <button className="add-button" aria-label="Add task">Add</button>
          </div>
          <ol id="todo-list">
            <p>existing tasks will be shown here</p>
          </ol>
        </div>
      );
    }
    export default TodoList;
    

    Il codice precedente aggiunge una casella di input per la nuova attività to-do e un pulsante per inviare l'input. Successivamente, si collega il pulsante Aggiungi. Usare il useState hook React per aggiungere due variabili di stato, una per l'attività che viene aggiunta e un'altra per archiviare le attività esistenti. Per questa esercitazione, le attività vengono archiviate in memoria e non nell'archiviazione permanente.

  2. Aggiungere l'istruzione import seguente a TodoList.jsx per importare useState.

    import { useState } from 'react'
    
  3. Usare quindi tale hook per creare le variabili di stato. Aggiungere il codice seguente nella funzione TodoList sopra l'istruzione return.

    const [tasks, setTasks] = useState(["Drink some coffee", "Create a TODO app", "Drink some more coffee"]);
    const [newTaskText, setNewTaskText] = useState("");
    

    In questo modo vengono impostate due variabili, tasks e newTaskText, per i dati e due funzioni che è possibile chiamare per aggiornare tali variabili, setTasks e setNewTasks. Quando viene modificato un valore per una variabile di stato, React esegue automaticamente il rendering del componente.

    Sei quasi pronto per aggiornare TodoList.jsx per visualizzare gli elementi to-do come elenco, ma c'è un concetto importante di React per imparare prima.

    In React, quando si visualizza un elenco di elementi è necessario aggiungere una chiave per identificare in modo univoco ogni elemento nell'elenco. Questa funzionalità è illustrata in modo approfondito nella documentazione di React in Elenchi di rendering, ma qui verranno illustrate le nozioni di base. È disponibile un elenco di to-do elementi da visualizzare ed è necessario associare una chiave univoca per ogni elemento. La chiave per ogni elemento non deve essere modificata e per questo motivo non è possibile usare l'indice dell'elemento nella matrice come chiave. È necessario un ID che non cambierà per tutta la durata di tali valori. Si userà randomUUID() per creare un ID univoco per ogni elemento to-do.

  4. Creare TodoList.jsx usando un UUID come chiave per ogni elemento to-do. Aggiornare TodoList.jsx con il codice seguente.

    import React, { useState } from 'react';
    
    const initialTasks = [
        { id: self.crypto.randomUUID(), text: 'Drink some coffee' },
        { id: self.crypto.randomUUID(), text: 'Create a TODO app' },
        { id: self.crypto.randomUUID(), text: 'Drink some more coffee' }
    ];
    
    function TodoList() {
        const [tasks, setTasks] = useState(initialTasks);
        const [newTaskText, setNewTaskText] = useState("");
    
        return (
            <article
                className="todo-list"
                aria-label="task list manager">
                <header>
                    <h1>TODO</h1>
                    <form className="todo-input" aria-controls="todo-list">
                        <input
                            type="text"
                            placeholder="Enter a task"
                            value={newTaskText} />
                        <button
                            className="add-button">
                            Add
                        </button>
                    </form>
                </header>
                <ol id="todo-list" aria-live="polite" aria-label="task list">
                    {tasks.map((task, index) =>
                        <li key={task.id}>
                            <span className="text">{task.text}</span>
                        </li>
                    )}
                </ol>
            </article>
        );
    }
    export default TodoList;
    

    Poiché i valori ID vengono assegnati all'esterno della funzione TodoList, è possibile assicurarsi che i valori non cambieranno se viene eseguito di nuovo il rendering della pagina. Quando provi l'app in questo stato, noterai che non puoi digitare nell'elemento di input todo. Questo perché l'elemento di input è associato a newTaskText, che è stato inizializzato in una stringa vuota. Per consentire agli utenti di aggiungere nuove attività, è necessario gestire l'evento onChange su tale controllo. È anche necessario implementare il supporto per il pulsante Aggiungi.

  5. Aggiungere le funzioni necessarie immediatamente prima dell'istruzione return nella funzione TodoList.

    function handleInputChange(event) {
        setNewTaskText(event.target.value);
    }
    
    function addTask() {
        if (newTaskText.trim() !== "") {
            setTasks(t => [...t, { id: self.crypto.randomUUID(), text: newTaskText }]);
            setNewTaskText("");
        }
        event.preventDefault();
    }
    

    Nella funzione handleInputChanged il nuovo valore del campo di input viene passato attraverso event.target.valuee tale valore viene usato per aggiornare il valore per la variabile newTaskText con setNewTaskText. Nella funzione addTask aggiungere la nuova attività all'elenco delle attività esistenti usando setTasks e impostare l'ID dell'elemento come nuovo valore UUID. Aggiornare l'elemento di input per includere onChange={handleInputChange} e aggiornare il pulsante Aggiungi per includere onClick={addTask}. Questo codice collega l'evento alla funzione che gestisce l'evento. A questo scopo, si dovrebbe essere in grado di aggiungere una nuova attività all'elenco di attività. Le nuove attività vengono aggiunte alla fine dell'elenco. Per rendere l'app più utile, è necessario aggiungere il supporto per eliminare le attività e spostare un'attività verso l'alto o verso il basso.

  6. Aggiungere le funzioni per supportare l'eliminazione, spostarsi verso l'alto e spostarsi verso il basso e quindi aggiornare il markup per visualizzare un pulsante per ogni azione. Aggiungere il codice seguente nella funzione TodoList sopra l'istruzione return.

    function deleteTask(id) {
        const updatedTasks = tasks.filter(task => task.id != id);
        setTasks(updatedTasks);
    }
    
    function moveTaskUp(index) {
        if (index > 0) {
            const updatedTasks = [...tasks];
            [updatedTasks[index], updatedTasks[index - 1]] = [updatedTasks[index - 1], updatedTasks[index]];
            setTasks(updatedTasks);
        }
    }
    
    function moveTaskDown(index) {
        if (index < tasks.length) {
            const updatedTasks = [...tasks];
            [updatedTasks[index], updatedTasks[index + 1]] = [updatedTasks[index + 1], updatedTasks[index]];
            setTasks(updatedTasks);
        }
    }
    

    La funzione delete accetta l'ID attività ed elimina quella dall'elenco e usa il metodo filtro array per creare una nuova matrice esclusa l'elemento selezionato e quindi chiama setTasks(). Le altre due funzioni accettano l'indice dell'oggetto perché queste funzioni riguardano l'ordine degli oggetti. Sia moveTaskUp() che moveTaskDown() usano assegnazione di struttura della matrice per scambiare l'attività selezionata con il relativo vicino.

  7. Aggiornare quindi la visualizzazione per includere questi tre pulsanti. Aggiorna la dichiarazione di ritorno per includere quanto segue.

    return (
        <article
            className="todo-list"
            aria-label="task list manager">
            <header>
                <h1>TODO</h1>
                <form className="todo-input" onSubmit={addTask} aria-controls="todo-list">
                    <input
                        type="text"
                        required
                        autoFocus
                        placeholder="Enter a task"
                        value={newTaskText}
                        aria-label="Task text"
                        onChange={handleInputChange} />
                    <button
                        className="add-button"
                        aria-label="Add task">
                        Add
                    </button>
                </form>
            </header>
            <ol id="todo-list" aria-live="polite">
                {tasks.map((task, index) =>
                    <li key={task.id}>
                        <span className="text">{task.text}</span>
                        <button className="delete-button" onClick={() => deleteTask(task.id)}>
                            🗑️
                        </button>
                        <button className="up-button" onClick={() => moveTaskUp(index)}>
                            ⇧
                        </button>
                        <button className="down-button" onClick={() => moveTaskDown(index)}>
                            ⇩
                        </button>
                    </li>
                )}
            </ol>
        </article>
    );
    

    Sono stati aggiunti i pulsanti necessari per eseguire le attività descritte in precedenza. Si usano caratteri Unicode come icone nei pulsanti. Nel markup sono stati aggiunti alcuni attributi per supportare l'aggiunta di alcuni CSS in un secondo momento. È anche possibile notare l'uso degli attributi aria per migliorare l'accessibilità, che sono facoltativi ma altamente raccomandati. Se esegui l'app, dovrebbe apparire come nell'illustrazione seguente.

    Screenshot che mostra l'app in esecuzione e mostra un elenco

    A questo fine dovrebbe essere possibile eseguire le operazioni seguenti nell'app Web TODO.

    • Aggiungi attività
    • Elimina attività
    • Sposta attività verso l'alto
    • Sposta l'attività verso il basso

    Queste funzioni funzionano, ma è possibile effettuare il refactoring per compilare un componente riutilizzabile per visualizzare gli elementi to-do. Il markup per l'elemento to-do viene inserito in un nuovo componente, TodoItem. Poiché la gestione dell'elenco rimane nel componente Todo, è possibile passare i callback ai pulsanti Elimina e Sposta.

  8. Per iniziare, fare clic con il pulsante destro del mouse sulla cartella componenti in Esplora soluzioni e selezionare Aggiungi > Nuovo Elemento.

  9. Nella finestra di dialogo visualizzata selezionare il File componente JSX React, assegnargli il nome TodoItem e selezionare Aggiungi.

  10. Aggiungere il codice seguente a TodoItem.

    In questo codice, l'attività e i callback vengono passati come proprietà a questo nuovo componente.

    import PropTypes from 'prop-types';
    
    function TodoItem({ task, deleteTaskCallback, moveTaskUpCallback, moveTaskDownCallback }) {
        return (
            <li aria-label="task" >
                <span className="text">{task}</span>
                <button
                    type="button"
                    aria-label="Delete task"
                    className="delete-button"
                    onClick={() => deleteTaskCallback()}>
                    🗑️
                </button>
                <button
                    type="button"
                    aria-label="Move task up"
                    className="up-button"
                    onClick={() => moveTaskUpCallback()}>
                    ⇧
                </button>
                <button
                    type="button"
                    aria-label="Move task down"
                    className="down-button"
                    onClick={() => moveTaskDownCallback()}>
                    ⇩
                </button>
            </li>
        );
    }
    
    TodoItem.propTypes = {
        task: PropTypes.string.isRequired,
        deleteTaskCallback: PropTypes.func.isRequired,
        moveTaskUpCallback: PropTypes.func.isRequired,
        moveTaskDownCallback: PropTypes.func.isRequired,
    };
    
    export default TodoItem;
    

    Il codice precedente contiene il markup del componente Todo e alla fine di esso si dichiara il PropTypes. Le proprietà vengono usate per passare dati da un componente padre a componenti figlio. Per ulteriori informazioni sui Props, consulta Passaggio dei Props a un Componente: React. Poiché le funzioni di eliminazione e spostamento vengono passate come callback, il gestore onClick deve essere aggiornato per invocare queste callback.

  11. Aggiungere il codice richiesto. Di seguito è riportato il codice completo per TodoList che usa il componente TodoItem.

    import React, { useState } from 'react'
    import TodoItem from './TodoItem'
    
    const initialTasks = [
        { id: self.crypto.randomUUID(), text: 'Drink some coffee' },
        { id: self.crypto.randomUUID(), text: 'Create a TODO app' },
        { id: self.crypto.randomUUID(), text: 'Drink some more coffee' }
    ];
    
    function TodoList() {
        const [tasks, setTasks] = useState(initialTasks);
        const [newTaskText, setNewTaskText] = useState("");
        function handleInputChange(event) {
            setNewTaskText(event.target.value);
        }
        function addTask() {
            if (newTaskText.trim() !== "") {
                setTasks(t => [...t, { id: self.crypto.randomUUID(), text: newTaskText }]);
                setNewTaskText("");
            }
            event.preventDefault();
        }
        function deleteTask(id) {
            const updatedTasks = tasks.filter(task => task.id !== id);
            setTasks(updatedTasks);
        }
        function moveTaskUp(index) {
            if (index > 0) {
                const updatedTasks = [...tasks];
                [updatedTasks[index], updatedTasks[index - 1]] = [updatedTasks[index - 1], updatedTasks[index]];
                setTasks(updatedTasks);
            }
        }
        function moveTaskDown(index) {
            if (index < tasks.length) {
                const updatedTasks = [...tasks];
                [updatedTasks[index], updatedTasks[index + 1]] = [updatedTasks[index + 1], updatedTasks[index]];
                setTasks(updatedTasks);
            }
        }
        return (
            <article
                className="todo-list"
                aria-label="task list manager">
                <header>
                    <h1>TODO</h1>
                    <form onSubmit={addTask} aria-controls="todo-list">
                        <input
                            type="text"
                            required
                            placeholder="Enter a task"
                            value={newTaskText}
                            aria-label="Task text"
                            onChange={handleInputChange} />
                        <button
                            className="add-button"
                            aria-label="Add task">
                            Add
                        </button>
                    </form>
                </header>
                <ol id="todo-list" aria-live="polite">
                    {tasks.map((task, index) =>
                        <TodoItem
                            key={task.id}
                            task={task.text}
                            deleteTaskCallback={() => deleteTask(task.id)}
                            moveTaskUpCallback={() => moveTaskUp(index)}
                            moveTaskDownCallback={() => moveTaskDown(index)}
                        />
                    )}
                </ol>
            </article>
        );
    }
    
    export default TodoList;
    

    A questo punto, il componente TodoItem viene usato per eseguire il rendering di ogni elemento to-do. Si noti che la chiave è impostata su task.id, che contiene il valore UUID per tale attività. Quando si esegue l'app, non dovrebbero essere visualizzate modifiche all'aspetto o al comportamento dell'app perché è stato eseguito il refactoring per l'uso di TodoItem.

    Ora che sono supportate tutte le funzioni di base, è il momento di iniziare ad aggiungere alcuni stili a questo per renderlo bello. Per iniziare, aggiungere un collegamento in Index.html per una famiglia di caratteri, Inter, che verrà usata per questa app. In Index.htmlsono presenti altri elementi che devono essere puliti. In particolare, il titolo deve essere aggiornato e si vuole sostituire il file vite.svg attualmente usato come icona.

  12. Aggiornare Index.html con il contenuto seguente.

    <!doctype html>
    <html lang="en">
        <head>
            <meta charset="UTF-8" />
            <link rel="icon" type="image/svg+xml" href="/checkmark-square.svg" />
            <meta name="viewport" content="width=device-width, initial-scale=1.0" />
            <title>TODO app</title>
            <link href='https://fonts.googleapis.com/css?family=Inter' rel='stylesheet'>
            <script type="module" defer src="/src/main.jsx"></script>
        </head>
        <body>
        </body>
    </html>
    
  13. Modificare file main.jsx per sostituire root con main quando si chiama createRoot.

    Il codice completo per main.jsx è illustrato qui.

    import { StrictMode } from 'react'
    import { createRoot } from 'react-dom/client'
    import App from './App.jsx'
    import './index.css'
    
    createRoot(document.querySelector('main')).render(
        <StrictMode>
            <App />
        </StrictMode>,
    )
    

    Oltre a queste modifiche, il file checkmark-square.svg è stato aggiunto alla cartella pubblica. Si tratta di un SVG dall'immagine quadrata del segno di spunta FluentUI, che è possibile scaricare direttamente. È disponibile un pacchetto che è possibile usare per un'esperienza più integrata, ma non rientra nell'ambito di questo articolo.

    Aggiornare quindi gli stili del componente TodoList.

  14. Nella cartella components aggiungere un nuovo file CSS denominato TodoList.css. È possibile fare clic con il pulsante destro del mouse sul progetto e selezionare Aggiungi > nuovo elemento e quindi selezionare foglio di stile. Assegnare al file il nome TodoList.css.

  15. Aggiungere il codice seguente a TodoList.css.

    .todo-list {
        background-color: #1e1e1e;
        padding: 1.25rem;
        border-radius: 0.5rem;
        box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.3);
        width: 100%;
        max-width: 25rem;
    }
    
    .todo-list h1 {
        text-align: center;
        color: #e0e0e0;
    }
    
    .todo-input {
        display: flex;
        justify-content: space-between;
        margin-bottom: 1.25rem;
    }
    
    .todo-input input {
        flex: 1;
        padding: 0.625rem;
        border: 0.0625rem solid #333;
        border-radius: 0.25rem;
        margin-right: 0.625rem;
        background-color: #2c2c2c;
        color: #e0e0e0;
    }
    
    .todo-input .add-button {
        padding: 0.625rem 1.25rem;
        background-color: #007bff;
        color: #fff;
        border: none;
        border-radius: 0.25rem;
        cursor: pointer;
    }
    
    .todo-input .add-button:hover {
        background-color: #0056b3;
    }
    
    .todo-list ol {
        list-style-type: none;
        padding: 0;
    }
    
    .todo-list li {
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 0.625rem;
        border-bottom: 0.0625rem solid #333;
    }
    
    .todo-list li:last-child {
        border-bottom: none;
    }
    
    .todo-list .text {
        flex: 1;
    }
    
    .todo-list li button {
        background: none;
        border: none;
        cursor: pointer;
        font-size: 1rem;
        margin-left: 0.625rem;
        color: #e0e0e0;
    }
    
    .todo-list li button:hover {
        color: #007bff;
    }
    
    .todo-list li button.delete-button {
        color: #ff4d4d;
    }
    
    .todo-list li button.up-button,
    .todo-list li button.down-button {
        color: #4caf50;
    }
    
  16. Modificare quindi TodoList.jsx per aggiungere l'importazione seguente all'inizio del file.

    import './TodoList.css';
    
  17. Aggiornare il browser dopo aver salvato le modifiche. Questo dovrebbe migliorare lo stile dell'app. L'app dovrebbe essere simile alla seguente.

    Screenshot che mostra la versione finale dell'app in esecuzione.

    A questo punto è stata creata un'app di elenco di to-do funzionante che archivia gli elementi to-do in memoria. A questo punto, è possibile aggiornare l'app per archiviare gli elementi to-do in localStorage/IndexedDboppure integrarlo con un database lato server o un altro back-end per un'archiviazione più permanente.

Sommario

In questa esercitazione è stata creata una nuova app React con Visual Studio. L'app è costituita da un elenco di to-do, che include il supporto per aggiungere attività, eliminare attività e riordinarle. Sono stati creati due nuovi componenti React e sono stati usati in questa esercitazione.

Risorse