E aí, programador! o/
Nessa aula iremos praticar tudo o que aprendemos até agora de React através da criação de um projeto: um aplicativo para gerenciar uma coleção de jogos que persiste os dados no local storage, evitando que eles se percam ao sair da página.
Comece criando o projeto:
Com o projeto criado, exclua os arquivos desnecessários: a pasta “assets”, o arquivo “App.css” e o conteúdo do arquivo “App.jsx”.
Inclua estilos extras no arquivo “index.css”:
body {
place-items: start;
padding: 0 4rem;
}
form {
display: flex;
align-items: end;
gap: 2rem;
}
input {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
margin: 1rem 0;
display: block;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
transition: border-color 0.25s;
}
.games {
display: flex;
flex-wrap: wrap;
}
.games > div {
display: flex;
align-items: start;
gap: 2rem;
height: 8rem;
margin-top: 2rem;
width: 28rem;
}
.games img {
height: 100%;
width: 10rem;
object-fit: cover;
}
.games h2 {
margin: 0;
}
Adicione a estrutura inicial do componente “App.jsx”:
function App() {
return (
<div className="app">
<h1>Biblioteca de Jogos</h1>
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="title">Título:</label>
<input type="text" id="title" />
</div>
<div>
<label htmlFor="cover">Capa:</label>
<input type="text" id="cover" />
</div>
<button>Adicionar</button>
</form>
<div className="games">
</div>
</div>
)
}
export default App
Crie os estados que estarão controlando os inputs e a função de submit:
import { useState } from "react"
function App() {
const [title, setTitle] = useState("")
const [cover, setCover] = useState("")
const handleSubmit = (ev) => {
ev.preventDefault()
console.log({ title, cover })
setTitle("")
setCover("")
}
return (
<div className="app">
<h1>Biblioteca de Jogos</h1>
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="title">Título:</label>
<input type="text" id="title" value={title} onChange={(ev) => setTitle(ev.target.value)} />
</div>
<div>
<label htmlFor="cover">Capa:</label>
<input type="text" id="cover" value={cover} onChange={(ev) => setCover(ev.target.value)} />
</div>
<button>Adicionar</button>
</form>
<div className="games">
</div>
</div>
)
}
export default App
Crie um estado para armazenar os jogos e uma função que adiciona um novo jogo:
import { useState } from "react"
function App() {
const [games, setGames] = useState([])
const [title, setTitle] = useState("")
const [cover, setCover] = useState("")
const addGame = ({ title, cover }) => {
const id = Math.floor(Math.random() * 1000000)
const game = { id, title, cover }
setGames(state => [...state, game])
}
const handleSubmit = (ev) => {
ev.preventDefault()
addGame({ title, cover })
setTitle("")
setCover("")
}
return (
<div className="app">
<h1>Biblioteca de Jogos</h1>
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="title">Título:</label>
<input type="text" id="title" value={title} onChange={(ev) => setTitle(ev.target.value)} />
</div>
<div>
<label htmlFor="cover">Capa:</label>
<input type="text" id="cover" value={cover} onChange={(ev) => setCover(ev.target.value)} />
</div>
<button>Adicionar</button>
</form>
<div className="games">
{games.map((game) => (
<div key={game.id}>
<img src={game.cover} alt="Capa do jogo" />
<div>
<h2>{game.title}</h2>
<button>
Remover
</button>
</div>
</div>))}
</div>
</div>
)
}
export default App
Crie uma função para remover os jogos:
// ...
const addGame = ({ title, cover }) => {
const id = Math.floor(Math.random() * 1000000)
const game = { id, title, cover }
setGames(state => [...state, game])
}
const removeGame = (id) => {
setGames(state => state.filter(game => game.id !== id))
}
// ...
<div className="games">
{games.map((game) => (
<div key={game.id}>
<img src={game.cover} alt="Capa do jogo" />
<div>
<h2>{game.title}</h2>
<button onClick={() => removeGame(game.id)}>
Remover
</button>
</div>
</div>))}
</div>
</div>
)
}
export default App
Por fim, adicione a funcionalidade de persistência dos dados no localStorage e modifique o valor inicial do estado “games” para ler os dados armazenados no localStorage:
import { useState } from "react"
function App() {
const [games, setGames] = useState(() => {
const storedGames = localStorage.getItem("obc-game-lib")
if (!storedGames) return []
return JSON.parse(storedGames)
})
const [title, setTitle] = useState("")
const [cover, setCover] = useState("")
const addGame = ({ title, cover }) => {
const id = Math.floor(Math.random() * 1000000)
const game = { id, title, cover }
setGames(state => {
const newState = [...state, game]
localStorage.setItem("obc-game-lib", JSON.stringify(newState))
return newState
})
}
const removeGame = (id) => {
setGames(state => {
const newState = state.filter(game => game.id !== id)
localStorage.setItem("obc-game-lib", JSON.stringify(newState))
return newState
})
}
const handleSubmit = (ev) => {
ev.preventDefault()
addGame({ title, cover })
setTitle("")
setCover("")
}
return (
<div className="app">
<h1>Biblioteca de Jogos</h1>
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="title">Título:</label>
<input type="text" id="title" value={title} onChange={(ev) => setTitle(ev.target.value)} />
</div>
<div>
<label htmlFor="cover">Capa:</label>
<input type="text" id="cover" value={cover} onChange={(ev) => setCover(ev.target.value)} />
</div>
<button>Adicionar</button>
</form>
<div className="games">
{games.map((game) => (
<div key={game.id}>
<img src={game.cover} alt="Capa do jogo" />
<div>
<h2>{game.title}</h2>
<button onClick={() => removeGame(game.id)}>
Remover
</button>
</div>
</div>))}
</div>
</div>
)
}
export default App