Nesta parte da tradução do curso de treinamento do React, você é solicitado a criar um gerador de memes.

→
Parte 1: visão geral do curso, razões para a popularidade do React, ReactDOM e JSX→
Parte 2: componentes funcionais→
Parte 3: arquivos de componentes, estrutura do projeto→
Parte 4: componentes pai e filho→
Parte 5: início do trabalho em um aplicativo TODO, noções básicas de estilo→
Parte 6: sobre alguns recursos do curso, JSX e JavaScript→
Parte 7: estilos embutidos→
Parte 8: trabalho contínuo na aplicação TODO, familiaridade com as propriedades dos componentes→
Parte 9: propriedades do componente→
Parte 10: Workshop sobre como trabalhar com propriedades e estilo de componentes→
Parte 11: geração dinâmica de marcação e método de matrizes de mapas→
Parte 12: workshop, terceira etapa do trabalho em uma aplicação TODO→
Parte 13: componentes baseados em classe→
Parte 14: workshop sobre componentes baseados em classe, status dos componentes→
Parte 15: oficinas de saúde componentes→
Parte 16: a quarta etapa do trabalho em um aplicativo TODO, manipulação de eventos→
Parte 17: quinta etapa do trabalho em um aplicativo TODO, modificando o estado dos componentes→
Parte 18: a sexta etapa do trabalho em um aplicativo TODO→
Parte 19: métodos do ciclo de vida dos componentesParte 20: a primeira lição sobre renderização condicional→
Parte 21: segunda lição e workshop sobre renderização condicional→
Parte 22: sétima etapa do trabalho em um aplicativo TODO, baixando dados de fontes externas→
Parte 23: primeira lição sobre como trabalhar com formulários→
Parte 24: Segunda lição sobre formulários→
Parte 25: Workshop sobre como trabalhar com formulários→
Parte 26: arquitetura do aplicativo, padrão Container / Component→
Parte 27: projeto do cursoLição 45. Projeto do curso. Meme generator
→
OriginalEntão chegamos ao projeto do curso. Vamos criar um aplicativo que irá gerar memes. Vamos começar com o projeto create-react-app padrão criado usando este comando:
npx create-react-app meme-generator
Aqui você pode encontrar informações sobre os recursos de seu uso.
No decorrer do trabalho neste projeto, você será solicitado a implementar algumas de suas partes e ler as explicações sobre elas. O projeto padrão já possui código padrão, localizado principalmente nos
App.js
index.js
e
App.js
Você pode remover completamente esse código e tentar escrevê-lo por conta própria para testar a implementação dos mecanismos padrão dos aplicativos React.
Neste projeto, você está convidado a usar os seguintes estilos:
* { box-sizing: border-box; } body { margin: 0; background-color: whitesmoke; } header { height: 100px; display: flex; align-items: center; background: #6441A5; background: -webkit-linear-gradient(to right, #2a0845, #6441A5); background: linear-gradient(to right, #2a0845, #6441A5); } header > img { height: 80%; margin-left: 10%; } header > p { font-family: VT323, monospace; color: whitesmoke; font-size: 50px; margin-left: 60px; } .meme { position: relative; width: 90%; margin: auto; } .meme > img { width: 100%; } .meme > h2 { position: absolute; width: 80%; text-align: center; left: 50%; transform: translateX(-50%); margin: 15px 0; padding: 0 5px; font-family: impact, sans-serif; font-size: 2em; text-transform: uppercase; color: white; letter-spacing: 1px; text-shadow: 2px 2px 0 #000, -2px -2px 0 #000, 2px -2px 0 #000, -2px 2px 0 #000, 0 2px 0 #000, 2px 0 0 #000, 0 -2px 0 #000, -2px 0 0 #000, 2px 2px 5px #000; } .meme > .bottom { bottom: 0; } .meme > .top { top: 0; } .meme-form { width: 90%; margin: 20px auto; display: flex; justify-content: space-between; } .meme-form > input { width: 45%; height: 40px; } .meme-form > button { border: none; font-family: VT323, monospace; font-size: 25px; letter-spacing: 1.5px; color: white; background: #6441A5; } .meme-form > input::-webkit-input-placeholder { font-family: VT323, monospace; font-size: 25px; text-align: cen } .meme-form > input::-moz-placeholder { font-family: VT323, monospace; font-size: 25px; text-align: cen } .meme-form > input:-ms-input-placeholder { font-family: VT323, monospace; font-size: 25px; text-align: cen } .meme-form > input:-moz-placeholder { font-family: VT323, monospace; font-size: 25px; text-align: cen }
Esses estilos podem ser incluídos no arquivo
index.css
já no projeto e
index.css
no arquivo
index.js
.
Portanto, com base na suposição de que os arquivos
index.js
e
App.js
estão vazios agora, você, como primeira tarefa, é convidado a escrever o código
index.js
, criar o componente mais simples no
App.js
e
App.js
-lo para
index.js
.
Aqui está o que deve aparecer no
index.js
:
import React from "react" import ReactDOM from "react-dom" import './index.css' import App from "./App" ReactDOM.render(<App />, document.getElementById("root"))
Aqui importamos
React
e
ReactDOM
, estilos de
index.css
e o componente
App
. Depois disso, usando o método
ReactDOM.render()
,
ReactDOM.render()
que o componente
App
forma no elemento da página
index.html
com o identificador
root
(
<div id="root"></div>
).
Aqui está a
App.js
arquivo
App.js
:
import React from "react" function App() { return ( <h1>Hello world!</h1> ) } export default App
Aqui é apresentado agora o componente funcional mais simples.
Nesta fase, o projeto se parece com o mostrado abaixo.
Aplicativo no navegadorAgora crie dois novos componentes, em dois arquivos cujos nomes correspondem aos nomes dos componentes:
- O componente
Header
que será usado para exibir o cabeçalho do aplicativo. - O componente
MemeGenerator
, no qual as principais tarefas atribuídas ao aplicativo serão resolvidas. Nomeadamente, as chamadas para a API serão realizadas aqui. Os dados do aplicativo serão armazenados aqui.
Considerando as funções atribuídas a esses componentes, pense sobre o que deveriam ser.
Aqui está o conteúdo do arquivo
Header.js
:
import React from "react" function Header() { return ( <h1>HEADER</h1> ) } export default Header
Como esse componente será usado apenas para exibir o cabeçalho do aplicativo, nós o projetamos como um componente funcional.
Aqui está o código para o arquivo
MemeGenerator.js
:
import React, {Component} from "react" class MemeGenerator extends Component { constructor() { super() this.state ={} } render() { return ( <h1>MEME GENERATOR SECTION</h1> ) } } export default MemeGenerator
Aqui, levando em consideração as tarefas que devem ser resolvidas por meio do componente
MemeGenerator
, usaremos um componente baseado na classe. Há um construtor aqui no qual inicializamos o estado com um objeto vazio.
Após a criação desses arquivos, importamos-os para o
App.js
e retornamos a marcação do componente funcional do
App
, que usa instâncias desses componentes, sem esquecer que, se o componente funcional retornar vários elementos, eles precisarão ser envolvidos em algo. No nosso caso, esta é a
<div>
. Aqui está o código
App.js
atualizado:
import React from "react" import Header from "./Header" import MemeGenerator from "./MemeGenerator" function App() { return ( <div> <Header /> <MemeGenerator /> </div> ) } export default App
Verifique a aparência do aplicativo.
Aplicativo no navegadorAgora vamos trabalhar no componente
Header
. Aqui vamos usar o elemento semântico HTML5
<header>
. Essa tag conterá a imagem e o texto. Agora o código do arquivo
Header.js
ficará assim:
import React from "react" function Header() { return ( <header> <img src="http://www.pngall.com/wp-content/uploads/2016/05/Trollface.png" alt="Problem?" /> <p>Meme Generator</p> </header> ) } export default Header
Veja como a aparência do aplicativo mudará.
Aplicativo no navegadorO título do aplicativo foi criado de acordo com os estilos
index.js
anteriormente no
index.js
. O trabalho no componente
Header
está concluído.
Continuaremos a lidar com o componente
MemeGenerator
. Agora você está convidado a inicializar independentemente o estado desse componente gravando os seguintes dados nele:
- O texto exibido na parte superior do meme (propriedade
topText
). - O texto exibido na parte inferior do meme (propriedade
bottomText
). - Imagem aleatória (propriedade
randomImage
que precisa ser inicializada com o link http://i.imgflip.com/1bij.jpg ).
É assim que o código
MemeGenerator.js
será após a inicialização do estado:
import React, {Component} from "react" class MemeGenerator extends Component { constructor() { super() this.state = { topText: "", bottomText: "", randomImg: "http://i.imgflip.com/1bij.jpg" } } render() { return ( <h1>MEME GENERATOR SECTION</h1> ) } } export default MemeGenerator
Agora a aparência do aplicativo não é afetada.
Usaremos chamadas para a API, que retorna uma matriz de objetos contendo links para imagens, com base nos quais memes podem ser criados. Nesta fase do trabalho no projeto, você está convidado a implementar a seguinte funcionalidade no componente
MemeGenerator
:
- Ligue para a API https://api.imgflip.com/get_memes/ .
- Salve os dados disponíveis na resposta como uma matriz de
response.data.memes
na nova propriedade state ( allMemeImgs
).
Aqui, para tornar mais claro, um fragmento dos dados JSON retornados ao acessar esta API:
{ "success":true, "data":{ "memes":[ { "id":"112126428", "name":"Distracted Boyfriend", "url":"https:\/\/i.imgflip.com\/1ur9b0.jpg", "width":1200, "height":800, "box_count":3 }, { "id":"87743020", "name":"Two Buttons", "url":"https:\/\/i.imgflip.com\/1g8my4.jpg", "width":600, "height":908, "box_count":2 }, { "id":"129242436", "name":"Change My Mind", "url":"https:\/\/i.imgflip.com\/24y43o.jpg", "width":482, "height":361, "box_count":2 }, …. ] } }
Resolvendo o problema exposto acima, é necessário levar em consideração o fato de estarmos falando de dados que o componente precisa no início do aplicativo.
Portanto, para carregá-los, recorreremos ao método do ciclo de vida do
componentDidMount()
. Aqui nós, usando o método
fetch()
padrão, chamaremos a API. Isso retorna uma promessa. Após carregar os dados, o objeto de resposta estará disponível para nós, extraímos a matriz de
memes
e a colocamos na nova propriedade de estado
allMemeImgs
, inicializada com uma matriz vazia. Como esses dados ainda não são usados para formar algo exibido na tela, imprimiremos o primeiro elemento da matriz no console para verificar a operação correta do mecanismo de carregamento de dados.
Aqui está o código do componente
MemeGenerator
nesta fase do trabalho:
import React, {Component} from "react" class MemeGenerator extends Component { constructor() { super() this.state = { topText: "", bottomText: "", randomImg: "http://i.imgflip.com/1bij.jpg", allMemeImgs: [] } } componentDidMount() { fetch("https://api.imgflip.com/get_memes") .then(response => response.json()) .then(response => { const {memes} = response.data console.log(memes[0]) this.setState({ allMemeImgs: memes }) }) } render() { return ( <h1>MEME GENERATOR SECTION</h1> ) } } export default MemeGenerator
É isso que chega ao console depois de carregar os dados com êxito.
Aplicativo no navegador, saída para o console do primeiro elemento da matriz carregadaObserve que a imagem é descrita usando muitas propriedades. Usaremos apenas a propriedade
url
, que dá acesso ao link para baixar a imagem.
No início do curso, conversamos sobre como essa aplicação será exibida.
Meme generatorEm particular, sua interface possui alguns campos para inserir texto, que serão exibidos nas partes superior e inferior da imagem. Agora você está convidado a participar, com base no código atualizado do componente
MemeGenerator
mostrado abaixo, que difere do código acima desse componente, pois um formulário em branco é adicionado aqui, para criar alguns campos de texto,
topText
e
bottomText
. Lembre-se de que esses componentes devem ser gerenciados. Adicione os atributos necessários a eles. Crie um
onChange
eventos
onChange
esses campos nos quais você precisa atualizar as propriedades de estado correspondentes à medida que insere texto neles.
import React, {Component} from "react" class MemeGenerator extends Component { constructor() { super() this.state = { topText: "", bottomText: "", randomImg: "http://i.imgflip.com/1bij.jpg", allMemeImgs: [] } } componentDidMount() { fetch("https://api.imgflip.com/get_memes") .then(response => response.json()) .then(response => { const {memes} = response.data this.setState({ allMemeImgs: memes }) }) } render() { return ( <div> <form className="meme-form"> { // } <button>Gen</button> </form> </div> ) } } export default MemeGenerator
A propósito, preste atenção ao fato de que, para incluir um comentário no código retornado pelo método
render()
, o colocamos entre colchetes para indicar ao sistema que esse fragmento deve ser interpretado como código JavaScript.
Aqui está o que você deve obter nesta fase do trabalho no aplicativo:
import React, {Component} from "react" class MemeGenerator extends Component { constructor() { super() this.state = { topText: "", bottomText: "", randomImg: "http://i.imgflip.com/1bij.jpg", allMemeImgs: [] } this.handleChange = this.handleChange.bind(this) } componentDidMount() { fetch("https://api.imgflip.com/get_memes") .then(response => response.json()) .then(response => { const {memes} = response.data this.setState({ allMemeImgs: memes }) }) } handleChange(event) { const {name, value} = event.target this.setState({ [name]: value }) } render() { return ( <div> <form className="meme-form"> <input type="text" name="topText" placeholder="Top Text" value={this.state.topText} onChange={this.handleChange} /> <input type="text" name="bottomText" placeholder="Bottom Text" value={this.state.bottomText} onChange={this.handleChange} /> <button>Gen</button> </form> </div> ) } } export default MemeGenerator
Agora, a página do aplicativo será semelhante à mostrada abaixo.
Aplicativo no navegadorEmbora apenas os campos com texto de ajuda sejam exibidos, a inserção de dados neles não leva a alterações na interface. Para verificar a operação correta dos mecanismos implementados aqui, você pode usar o comando
console.log()
.
Agora, trabalharemos por parte do aplicativo responsável por exibir o meme da imagem. Lembre-se de que agora temos uma matriz contendo informações sobre imagens que são planejadas para serem usadas como base de memes. O aplicativo deve, pressionando o botão
Gen
, selecionar aleatoriamente uma imagem dessa matriz e formar um meme.
Aqui está o código atualizado para o componente
MemeGenerator
. Aqui, no método
render()
, abaixo do código de descrição do formulário, existe um elemento
<div>
que inclui um elemento
<img>
que exibe uma imagem e alguns elementos
<h2>
que exibem rótulos. Os elementos
<div>
e
<h2>
são projetados usando os estilos que adicionamos ao projeto no início do trabalho nele.
import React, {Component} from "react" class MemeGenerator extends Component { constructor() { super() this.state = { topText: "", bottomText: "", randomImg: "http://i.imgflip.com/1bij.jpg", allMemeImgs: [] } this.handleChange = this.handleChange.bind(this) } componentDidMount() { fetch("https://api.imgflip.com/get_memes") .then(response => response.json()) .then(response => { const {memes} = response.data this.setState({ allMemeImgs: memes }) }) } handleChange(event) { const {name, value} = event.target this.setState({ [name]: value }) } render() { return ( <div> <form className="meme-form"> <input type="text" name="topText" placeholder="Top Text" value={this.state.topText} onChange={this.handleChange} /> <input type="text" name="bottomText" placeholder="Bottom Text" value={this.state.bottomText} onChange={this.handleChange} /> <button>Gen</button> </form> <div className="meme"> <img align="center" src={this.state.randomImg} alt="" /> <h2 className="top">{this.state.topText}</h2> <h2 className="bottom">{this.state.bottomText}</h2> </div> </div> ) } } export default MemeGenerator
Aqui está a aparência do aplicativo agora.
Aplicativo no navegadorPreste atenção ao fato de que a imagem que inicializou o estado é exibida aqui. Ainda não usamos imagens armazenadas na propriedade de estado
allMemeImgs
. Vamos tentar inserir algo nos campos de texto.
Aplicativo no navegadorComo você pode ver, os subsistemas de aplicativos responsáveis por trabalhar com texto estão funcionando conforme o esperado. Agora resta apenas garantir que, ao pressionar o botão
Gen
, uma imagem aleatória seja selecionada da matriz com dados da imagem e carregada no elemento
<img>
, presente na página abaixo dos campos de entrada de texto.
Para equipar o aplicativo com esse recurso - execute a tarefa a seguir. Crie um método que é acionado quando você clica no botão
Gen
Esse método deve selecionar uma das imagens, informações sobre as quais são armazenadas na propriedade de estado
allMemeImgs
, e executar ações que permitem exibir essa imagem no elemento
<img>
localizado nos campos de entrada de texto.
allMemeImgs
que
allMemeImgs
armazena uma matriz de objetos que descrevem as imagens e que cada objeto dessa matriz possui uma propriedade
url
.
Aqui está o código que fornece uma solução para esse problema:
import React, {Component} from "react" class MemeGenerator extends Component { constructor() { super() this.state = { topText: "", bottomText: "", randomImg: "http://i.imgflip.com/1bij.jpg", allMemeImgs: [] } this.handleChange = this.handleChange.bind(this) this.handleSubmit = this.handleSubmit.bind(this) } componentDidMount() { fetch("https://api.imgflip.com/get_memes") .then(response => response.json()) .then(response => { const {memes} = response.data this.setState({ allMemeImgs: memes }) }) } handleChange(event) { const {name, value} = event.target this.setState({ [name]: value }) } handleSubmit(event) { event.preventDefault() const randNum = Math.floor(Math.random() * this.state.allMemeImgs.length) const randMemeImg = this.state.allMemeImgs[randNum].url this.setState({ randomImg: randMemeImg }) } render() { return ( <div> <form className="meme-form" onSubmit={this.handleSubmit}> <input type="text" name="topText" placeholder="Top Text" value={this.state.topText} onChange={this.handleChange} /> <input type="text" name="bottomText" placeholder="Bottom Text" value={this.state.bottomText} onChange={this.handleChange} /> <button>Gen</button> </form> <div className="meme"> <img align="center" src={this.state.randomImg} alt="" /> <h2 className="top">{this.state.topText}</h2> <h2 className="bottom">{this.state.bottomText}</h2> </div> </div> ) } } export default MemeGenerator
O botão
Gen
pode ser atribuído a um manipulador de eventos que ocorre quando você clica nele, como é o caso de qualquer outro botão. No entanto, como esse botão é usado para enviar o formulário, seria melhor usar o
onSubmit
eventos
onSubmit
formulário. Nesse manipulador,
handleSubmit()
, chamamos o método do evento
event.preventDefault()
entra nele para cancelar o procedimento padrão de
event.preventDefault()
formulário durante o qual a página é recarregada. Em seguida, obtemos um número aleatório no intervalo de 0 ao valor correspondente ao índice do último elemento da matriz
allMemeImgs
e usamos esse número para se referir ao elemento com o índice correspondente. Passando para o elemento que é o objeto, obtemos a propriedade desse
url
e a
randomImg
propriedade de estado
randomImg
. Depois disso, o componente é renderizado novamente e a aparência da página é alterada.
Página do aplicativo no navegadorProjeto do curso concluído.
Sumário
Nesta lição, você criou um aplicativo que usa o que aprendeu ao dominar o React. Da próxima vez, falaremos sobre o desenvolvimento de aplicativos modernos do React e discutiremos idéias de projetos, implementando as quais você pode praticar trabalhando com o React.
Caros leitores! Você encontrou alguma dificuldade ao concluir este projeto de curso?