Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Neste tutorial, você criará um front-end do React para um aplicativo Web de lista de to-do usando o JavaScript e o Visual Studio 2022. O código para este aplicativo pode ser encontrado em ToDoJSWebApp.
Pré-requisitos
Instale o seguinte:
- Visual Studio 2022 ou posterior. Acesse a página Downloads do Visual Studio para instalá-lo gratuitamente.
- npm (
https://www.npmjs.com/
), que está incluído com o Node.js.
Criar o aplicativo React ToDo List
No Visual Studio, selecione Arquivo > Novo > Projeto para abrir a caixa de diálogo Criar um Novo Projeto, selecione o modelo React App JavaScript e escolha Avançar.
Nomeie o projeto
TodoWebApp
e selecione Criar.Isso cria o projeto JavaScript usando a ferramenta de linha de comando vite.
No Gerenciador de Soluções, clique com o botão direito do mouse na pasta
src
e escolha Adicionar > Nova Pasta. e crie uma nova pasta chamadacomponents
.É uma convenção comum colocar componentes em uma pasta de componentes, mas isso não é necessário.
Clique com o botão direito do mouse na nova pasta, selecione Adicionar > Arquivo de Componente React JSX, nomeie-o
TodoList
e clique em Adicionar.Isso cria um novo arquivo JSX na pasta de componentes.
Abra o componente
TodoList
e substitua o conteúdo padrão pelo seguinte:function TodoList() { return ( <h2>TODO app contents</h2> ); }
Esse componente exibe um cabeçalho, que você substituirá posteriormente.
Em seguida, conecte esse componente no aplicativo.
App.jsx
é o componente principal que é carregado e que representa o aplicativo de lista de tarefas pendentes. Esse componente é usado no arquivomain.jsx
.No Gerenciador de Soluções, abra
App.jsx
, remova todas as importações da parte superior e limpe o conteúdo da instrução de retorno. O arquivo deve ser semelhante ao seguinte.function App() { return ( <> <TodoList /> </> ); } export default App;
Para adicionar o componente TodoList, coloque o cursor dentro do fragmento e digite
<TodoL RETURN
. Isso adiciona o componente e a instrução de importação.Em seguida, desmarque os arquivos CSS.
Abra
App.css
e exclua todo o conteúdo.Abra
Index.css
e remova todos os conteúdos, exceto os estilos de: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; }
Executar o aplicativo
Selecione o botão Iniciar Depuração na barra de ferramentas ou pressione o atalho de teclado F5.
O aplicativo é aberto em uma janela do navegador.
Adicionar funções à lista de tarefas pendentes do aplicativo
Você pode deixar o aplicativo em execução. À medida que você faz alterações, o aplicativo é atualizado automaticamente com o conteúdo mais recente usando o suporte de substituição de módulo de acesso frequente do Vite. Algumas ações, como adicionar pastas ou renomear arquivos, exigem que você interrompa a depuração e reinicie o aplicativo, mas, em geral, você pode deixá-lo em execução em segundo plano à medida que desenvolve seu aplicativo. Abra o componente TodoList.jsx
para que possamos começar a defini-lo.
No Gerenciador de Soluções, abra
TodoList.jsx
e adicione a interface do usuário necessária para mostrar e gerenciar as entradas na lista de to-do. Substitua o conteúdo pelo seguinte código: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;
O código anterior adiciona uma caixa de entrada para a nova tarefa to-do e um botão para enviar a entrada. Em seguida, você conecta o botão Adicionar. Use o useState react hook para adicionar duas variáveis de estado, uma para a tarefa que está sendo adicionada e outra para armazenar as tarefas existentes. Para este tutorial, as tarefas são armazenadas na memória e não no armazenamento persistente.
Adicione a instrução de importação a seguir em
TodoList.jsx
para importaruseState
.import { useState } from 'react'
Em seguida, use esse gancho para criar as variáveis de estado. Adicione o código a seguir na função
TodoList
acima da instrução return.const [tasks, setTasks] = useState(["Drink some coffee", "Create a TODO app", "Drink some more coffee"]); const [newTaskText, setNewTaskText] = useState("");
Isso configura duas variáveis,
tasks
enewTaskText
, para os dados e duas funções que você pode chamar para atualizar essas variáveis,setTasks
esetNewTasks
. Quando um valor para uma variável de estado é alterado, o React renderiza automaticamente o componente.Você está quase pronto para atualizar TodoList.jsx para mostrar os itens to-do como uma lista, mas há um conceito de React importante para aprender primeiro.
No React, quando você exibe uma lista de itens, precisa adicionar uma chave para identificar exclusivamente cada item na lista. Esse recurso é explicado detalhadamente na documentação do React em Renderização de Listas, mas aqui você aprenderá as noções básicas. Você tem uma lista de to-do itens a serem exibidos e precisa associar uma chave exclusiva a cada item. A chave de cada item não deve ser alterada e, por esse motivo, você não pode usar o índice do item na matriz como a chave. Você precisa de uma ID que não mude ao longo do tempo de vida desses valores. Você usará randomUUID() para criar uma ID exclusiva para cada item to-do.
Crie TodoList.jsx usando uma UUID como a chave para cada item to-do. Atualize TodoList.jsx com o código a seguir.
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;
Como os valores de ID são atribuídos fora da função TodoList, você pode ter certeza de que os valores não serão alterados se a página for renderizada novamente. Ao experimentar o aplicativo nesse estado, você observará que não é possível digitar no campo de entrada do to-do. Isso ocorre porque o elemento de entrada está associado a
newTaskText
, que foi inicializado para uma cadeia de caracteres em branco. Para permitir que os usuários adicionem novas tarefas, você precisa lidar com o eventoonChange
nesse controle. Você também precisa implementar o suporte ao botão Adicionar.Adicione as funções necessárias imediatamente antes da instrução de retorno na função 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(); }
Na função
handleInputChanged
, o novo valor do campo de entrada é passado porevent.target.value
e esse valor é usado para atualizar o valor da variávelnewTaskText
comsetNewTaskText
. Na funçãoaddTask
, adicione a nova tarefa à lista de tarefas existentes usandosetTasks
e defina a ID do item como um novo valor UUID. Atualize o elemento de entrada para incluironChange={handleInputChange}
e atualizar o botão Adicionar para incluironClick={addTask}
. Esse código conecta o evento à função que manipula esse evento. Depois disso, você deve ser capaz de adicionar uma nova tarefa à lista de tarefas. Novas tarefas são adicionadas à parte inferior da lista. Para tornar esse aplicativo mais útil, você precisa adicionar suporte para excluir tarefas e mover uma tarefa para cima ou para baixo.Adicione as funções para dar suporte à exclusão, mova para cima e mova para baixo e atualize a marcação para mostrar um botão para cada ação. Adicione o código a seguir na função TodoList acima da instrução 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); } }
A função delete usa a ID da tarefa e exclui essa da lista e usa o método Array filter() para criar uma nova matriz, excluindo o item selecionado e, em seguida, chama
setTasks()
. As outras duas funções recebem o índice do item porque esse trabalho é específico para a ordem dos itens.moveTaskUp()
emoveTaskDown()
usam a atribuição de desestruturação de matriz para trocar a tarefa selecionada com o respectivo vizinho.Em seguida, atualize o modo de exibição para incluir esses três botões. Atualize a instrução return para conter o seguinte.
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> );
Você adicionou os botões necessários para executar as tarefas discutidas anteriormente. Você está usando caracteres Unicode como ícones nos botões. Na marcação, há alguns atributos adicionados para dar suporte à adição de alguns CSS posteriormente. Você também pode observar o uso de atributos aria para melhorar a acessibilidade, os quais são opcionais, mas altamente recomendados. Se você executar o aplicativo, ele deverá ser semelhante à ilustração a seguir.
Agora você deve ser capaz de executar o seguinte no aplicativo Web TODO.
- Adicionar tarefa
- Excluir tarefa
- Mover a tarefa para cima
- Mover a tarefa para baixo
Essas funções funcionam, mas você pode refatorar para criar um componente reutilizável para exibir os itens de to-do. A marcação do item to-do entra em um novo componente, TodoItem. Como o gerenciamento da lista permanece no componente de tarefas pendentes, você pode passar as funções de retorno para os botões Excluir e Mover.
Para começar, clique com o botão direito do mouse na pasta componentes no Gerenciador de Soluções e selecione Adicionar > Novo Item.
Na caixa de diálogo que é aberta, selecione o arquivo de componente React JSX, dê o nome TodoItem e selecione Adicionar.
Adicione o código a seguir ao TodoItem.
Nesse código, você passa a tarefa e os callbacks como propriedades para este novo 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;
O código anterior contém a marcação do componente Todo e, no final, você declara o
PropTypes
. Os Props são usados para passar dados de um componente pai para componentes filho. Para obter mais informações sobre Props, confira Passar Props para um componente – React. Como as funções excluir e mover estão sendo passadas como funções de retorno de chamada, o manipulador de eventosonClick
precisa estar atualizado para acionar esses retornos de chamada.Adicione o código necessário. O código completo para TodoList que usa o componente TodoItem é mostrado aqui.
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;
Agora, o componente TodoItem é usado para renderizar cada item to-do. Observe que a chave está definida como
task.id
, que contém o valor UUID dessa tarefa. Ao executar o aplicativo, você não deverá ver nenhuma alteração na aparência ou no comportamento do aplicativo porque o refatorou para usar TodoItem.Agora que você tem todas as funções básicas com suporte, é hora de começar a adicionar alguns estilos a isso para torná-lo agradável. Comece adicionando um link em Index.html para uma família de fontes, o Inter, que você usará para este aplicativo. Em Index.html, há alguns outros itens que precisam ser limpos. Especificamente, o título deve ser atualizado e você deseja substituir o arquivo vite.svg que atualmente é usado como o ícone.
Atualize Index.html com o conteúdo a seguir.
<!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>
Edite arquivo main.jsx para substituir
root
pormain
ao chamarcreateRoot
.O código completo para main.jsx é mostrado aqui.
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>, )
Além dessas alterações, o arquivo checkmark-square.svg foi adicionado à pasta pública. Este é um SVG da imagem quadrada de marca de seleção FluentUI, que você pode baixar diretamente. (Há um pacote que você pode usar para uma experiência mais integrada, mas que está fora do escopo deste artigo.)
Em seguida, atualize os estilos do componente TodoList.
Na pasta de componentes, adicione um novo arquivo CSS chamado TodoList.css. Clique com o botão direito do mouse no projeto e selecione Adicionar > Novo Item e selecione Folha de Estilos. Dê ao arquivo o nome TodoList.css.
Adicione o código a seguir ao 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; }
Em seguida, edite TodoList.jsx para adicionar a seguinte importação na parte superior do arquivo.
import './TodoList.css';
Atualize o navegador depois de salvar as alterações. Isso deve melhorar o estilo do aplicativo. O aplicativo deve ser semelhante ao seguinte.
Agora você criou um aplicativo de lista to-do funcionando que armazena os itens to-do na memória. A partir desse ponto, você pode atualizar o aplicativo para armazenar os itens to-do em localStorage/IndexedDb, ou integrá-lo a um banco de dados do lado do servidor ou outro back-end, para armazenamento mais permanente.
Resumo
Neste tutorial, você criou um novo aplicativo React usando o Visual Studio. O aplicativo consiste em uma lista de to-do, que inclui suporte para adicionar tarefas, excluir tarefas e reordená-las. Você criou dois novos componentes do React e os usou ao longo deste tutorial.
Recursos
- Código para este exemplo no ToDoJSWebApp
- Projetos JavaScript e TypeScript do Visual Studio