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?