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
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.
Assegnare al progetto il nome
TodoWebApp
e selezionare Crea.Verrà creato il progetto JavaScript usando lo strumento da riga di comando vite.
In Esplora soluzioni fare clic con il pulsante destro del mouse sulla cartella
src
e scegliere Aggiungi > Nuova cartella. e creare una nuova cartella denominatacomponents
.Si tratta di una convenzione comune per inserire i componenti in una cartella dei componenti, ma questo non è obbligatorio.
Fare clic con il pulsante destro del mouse sulla nuova cartella, selezionare Aggiungi > File componente JSX React, dargli un nome
TodoList
e fare clic su Aggiungi.Verrà creato un nuovo file JSX nella cartella components.
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 filemain.jsx
.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;
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.Cancellare quindi i file CSS.
Aprire
App.css
ed eliminare tutto il contenuto.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.
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.
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.
Aggiungere l'istruzione import seguente a
TodoList.jsx
per importareuseState
.import { useState } from 'react'
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
enewTaskText
, per i dati e due funzioni che è possibile chiamare per aggiornare tali variabili,setTasks
esetNewTasks
. 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.
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'eventoonChange
su tale controllo. È anche necessario implementare il supporto per il pulsante Aggiungi.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 attraversoevent.target.value
e tale valore viene usato per aggiornare il valore per la variabilenewTaskText
consetNewTaskText
. Nella funzioneaddTask
aggiungere la nuova attività all'elenco delle attività esistenti usandosetTasks
e impostare l'ID dell'elemento come nuovo valore UUID. Aggiornare l'elemento di input per includereonChange={handleInputChange}
e aggiornare il pulsante Aggiungi per includereonClick={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.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. SiamoveTaskUp()
chemoveTaskDown()
usano assegnazione di struttura della matrice per scambiare l'attività selezionata con il relativo vicino.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.
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.
Per iniziare, fare clic con il pulsante destro del mouse sulla cartella componenti in Esplora soluzioni e selezionare Aggiungi > Nuovo Elemento.
Nella finestra di dialogo visualizzata selezionare il File componente JSX React, assegnargli il nome TodoItem e selezionare Aggiungi.
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 gestoreonClick
deve essere aggiornato per invocare queste callback.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.
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>
Modificare file main.jsx per sostituire
root
conmain
quando si chiamacreateRoot
.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.
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.
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; }
Modificare quindi TodoList.jsx per aggiungere l'importazione seguente all'inizio del file.
import './TodoList.css';
Aggiornare il browser dopo aver salvato le modifiche. Questo dovrebbe migliorare lo stile dell'app. L'app dovrebbe essere simile alla seguente.
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
- Codice per questo esempio in ToDoJSWebApp
- progetti JavaScript e TypeScript di Visual Studio