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.

  1. Comece criando o projeto:

  2. Com o projeto criado, exclua os arquivos desnecessários: a pasta “assets”, o arquivo “App.css” e o conteúdo do arquivo “App.jsx”.

  3. 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;
    }
    
  4. 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
    
  5. 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
    
  6. 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
    
  7. 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
    
  8. 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