En esta parte de la traducción del curso de capacitación React, se le pide que cree un generador de memes.

→
Parte 1: descripción general del curso, razones de la popularidad de React, ReactDOM y JSX→
Parte 2: componentes funcionales→
Parte 3: archivos de componentes, estructura del proyecto→
Parte 4: componentes principales y secundarios→
Parte 5: inicio del trabajo en una aplicación TODO, los fundamentos del estilo→
Parte 6: sobre algunas características del curso, JSX y JavaScript→
Parte 7: estilos en línea→
Parte 8: trabajo continuo en la aplicación TODO, familiaridad con las propiedades de los componentes→
Parte 9: propiedades del componente→
Parte 10: Taller sobre trabajo con propiedades de componentes y estilo→
Parte 11: generación de marcado dinámico y método de matrices de mapas→
Parte 12: taller, tercera etapa de trabajo en una aplicación TODO→
Parte 13: componentes basados en clases→
Parte 14: taller sobre componentes basados en la clase, estado de los componentes.→
Parte 15: talleres de componentes de salud→
Parte 16: la cuarta etapa de trabajo en una aplicación TODO, manejo de eventos→
Parte 17: quinta etapa de trabajo en una aplicación TODO, modificando el estado de los componentes→
Parte 18: la sexta etapa de trabajo en una aplicación TODO→
Parte 19: métodos del ciclo de vida de los componentes.Parte 20: la primera lección de representación condicional→
Parte 21: segunda lección y taller sobre representación condicional→
Parte 22: la séptima etapa de trabajo en una aplicación TODO, descargando datos de fuentes externas→
Parte 23: primera lección sobre trabajar con formularios→
Parte 24: Segunda lección de formularios→
Parte 25: Taller sobre trabajo con formularios→
Parte 26: arquitectura de la aplicación, patrón de contenedor / componente→
Parte 27: proyecto del cursoLección 45. Proyecto del curso. Generador de memes
→
OriginalEntonces llegamos al proyecto del curso. Creemos una aplicación que genere memes. Comencemos con el proyecto estándar create-react-app creado con este comando:
npx create-react-app meme-generator
Aquí puede encontrar información sobre las características de su uso.
En el curso del trabajo en este proyecto, se le pedirá que implemente algunas de sus partes usted mismo y luego lea las explicaciones sobre ellas. El proyecto estándar ya tiene un código repetitivo ubicado, en particular, en los
App.js
index.js
y
App.js
Puede eliminar completamente este código e intentar escribirlo usted mismo para probarse en la implementación de los mecanismos estándar de las aplicaciones React.
En este proyecto, está invitado a usar los siguientes 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 }
Estos estilos pueden incluirse en el archivo
index.css
que ya se encuentra en el proyecto e
index.css
en el archivo
index.js
.
Entonces, en base a la suposición de que los archivos
index.js
y
App.js
ahora están vacíos, usted, como primera tarea, está invitado a escribir el código
index.js
, crear el componente más simple en
App.js
y
App.js
a
index.js
.
Esto es lo que debería aparecer en
index.js
:
import React from "react" import ReactDOM from "react-dom" import './index.css' import App from "./App" ReactDOM.render(<App />, document.getElementById("root"))
Aquí importamos
React
y
ReactDOM
, importamos estilos desde
index.css
y el componente de la
App
. Después de eso, utilizando el método
ReactDOM.render()
,
ReactDOM.render()
lo que forma el componente de la
App
en el elemento de página
index.html
con el identificador de
root
(
<div id="root"></div>
).
Así es como se
App.js
archivo
App.js
:
import React from "react" function App() { return ( <h1>Hello world!</h1> ) } export default App
Aquí se presenta ahora el componente funcional más simple.
En esta etapa, el proyecto se parece al que se muestra a continuación.
Aplicación en navegadorAhora cree dos componentes nuevos, en dos archivos cuyos nombres corresponden a los nombres de los componentes:
- El componente
Header
que se usará para mostrar el encabezado de la aplicación. - El componente
MemeGenerator
, en el que se resolverán las tareas principales asignadas a la aplicación. A saber, las llamadas a la API se realizarán aquí. Los datos de la aplicación se almacenarán aquí.
Teniendo en cuenta qué funciones se asignan a estos componentes, piense en lo que deberían ser.
Aquí está el contenido del archivo
Header.js
:
import React from "react" function Header() { return ( <h1>HEADER</h1> ) } export default Header
Dado que este componente solo se usará para mostrar el encabezado de la aplicación, lo diseñamos como un componente funcional.
Aquí está el código para el archivo
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
Aquí, teniendo en cuenta las tareas que se supone que deben resolverse mediante el componente
MemeGenerator
, utilizaremos un componente basado en la clase. Aquí hay un constructor en el que inicializamos el estado con un objeto vacío.
Una vez creados estos archivos, los importamos a
App.js
y devolvemos el marcado desde el componente funcional de la
App
, que utiliza instancias de estos componentes, sin olvidar que si el componente funcional devuelve varios elementos, deben estar envueltos en algo. En nuestro caso, esta es la
<div>
. Aquí está el código actualizado de
App.js
:
import React from "react" import Header from "./Header" import MemeGenerator from "./MemeGenerator" function App() { return ( <div> <Header /> <MemeGenerator /> </div> ) } export default App
Verifique la apariencia de la aplicación.
Aplicación en navegadorAhora trabajemos en el componente
Header
. Aquí usaremos el elemento semántico HTML5
<header>
. Esta etiqueta contendrá la imagen y el texto. Ahora el código del archivo
Header.js
se verá así:
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
Así es como cambiará el aspecto de la aplicación.
Aplicación en navegadorEl título de la aplicación está diseñado de acuerdo con los estilos previamente
index.js
en
index.js
. El trabajo en el componente
Header
ahora está completo.
Continuaremos
MemeGenerator
con el componente
MemeGenerator
. Ahora está invitado a inicializar independientemente el estado de este componente escribiendo los siguientes datos:
- El texto que se muestra en la parte superior del meme (propiedad
topText
). - El texto que se muestra en la parte inferior del meme (propiedad
bottomText
). - Imagen aleatoria (propiedad
randomImage
que debe inicializarse con el enlace http://i.imgflip.com/1bij.jpg ).
Esto es lo que
MemeGenerator.js
código
MemeGenerator.js
después de inicializar el 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
Ahora la apariencia de la aplicación no se verá afectada.
Utilizaremos llamadas a la API, que devuelve una matriz de objetos que contienen enlaces a imágenes, en función de los memes que se pueden crear. En esta etapa del trabajo en el proyecto, está invitado a implementar la siguiente funcionalidad en el componente
MemeGenerator
:
- Llame a la API https://api.imgflip.com/get_memes/ .
- Guarde los datos disponibles en la respuesta como una matriz de
response.data.memes
en la nueva propiedad de estado ( allMemeImgs
).
Aquí, para que quede más claro, un fragmento de los datos JSON devueltos al acceder a 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 }, …. ] } }
Para resolver el problema planteado anteriormente, es necesario tener en cuenta el hecho de que estamos hablando de datos que el componente necesita al comienzo de la aplicación.
Por lo tanto, para cargarlos, recurriremos al método del ciclo de vida del
componentDidMount()
. Aquí, utilizando el método
fetch()
estándar, llamaremos a la API. Devuelve una promesa. Después de cargar los datos, el objeto de respuesta estará disponible para nosotros, extraemos la matriz de
memes
y lo
allMemeImgs
en la nueva propiedad de estado
allMemeImgs
, inicializado con una matriz vacía. Dado que estos datos aún no se utilizan para formar algo que se muestra en la pantalla, imprimiremos el primer elemento de la matriz en la consola para verificar el funcionamiento correcto del mecanismo de carga de datos.
Aquí está el código del componente
MemeGenerator
en esta etapa del trabajo:
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
Esto es lo que llega a la consola después de cargar con éxito los datos.
Aplicación en el navegador, salida a la consola del primer elemento de la matriz cargadaTenga en cuenta que la imagen se describe utilizando muchas propiedades. Utilizaremos solo la propiedad
url
, que da acceso al enlace para descargar la imagen.
Al comienzo del curso, hablamos sobre cómo se verá esta aplicación.
Generador de memesEn particular, su interfaz tiene un par de campos para ingresar texto, que se mostrarán en las partes superior e inferior de la imagen. Ahora está invitado a tomar, según el código actualizado del componente
MemeGenerator
que se muestra a continuación, que difiere del código anterior de este componente en que se agrega un formulario en blanco aquí, para crear un par de campos de texto, texto
topText
e texto
bottomText
. Tenga en cuenta que estos deben ser componentes administrados. Agregue los atributos necesarios para ellos. Cree un
onChange
eventos
onChange
estos campos en el que necesita actualizar las propiedades de estado correspondientes a medida que ingresa texto en ellos.
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
Por cierto, preste atención al hecho de que para incluir un comentario en el código devuelto por el método
render()
, lo encerramos entre llaves para indicar al sistema que este fragmento debe interpretarse como código JavaScript.
Esto es lo que debe obtener en esta etapa del trabajo en la aplicación:
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
Ahora la página de la aplicación se verá como la que se muestra a continuación.
Aplicación en navegadorSi bien solo se muestran los campos con texto de ayuda, ingresar datos en ellos no conduce a cambios en la interfaz. Para verificar el funcionamiento correcto de los mecanismos implementados aquí, puede usar el comando
console.log()
.
Ahora trabajaremos en la parte de la aplicación responsable de mostrar el meme de la imagen. Recuerde que ahora tenemos una matriz que contiene información sobre imágenes que se planean utilizar como base de memes. La aplicación debe, al presionar el botón
Gen
, seleccionar aleatoriamente una imagen de esta matriz y formar un meme.
Aquí está el código actualizado para el componente
MemeGenerator
. Aquí, en el método
render()
, debajo del código de descripción del formulario, hay un elemento
<div>
, que incluye un elemento
<img>
que muestra una imagen y un par de elementos
<h2>
que muestran etiquetas. Los elementos
<div>
y
<h2>
están diseñados utilizando estilos que agregamos al proyecto al comienzo del trabajo en él.
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
Así es como se ve la aplicación ahora.
Aplicación en navegadorPreste atención al hecho de que la imagen que inicializó el estado se muestra aquí. Todavía no usamos imágenes almacenadas en la propiedad estatal
allMemeImgs
. Intentemos ingresar algo en los campos de texto.
Aplicación en navegadorComo puede ver, los subsistemas de aplicación responsables de trabajar con texto funcionan como se esperaba. Ahora solo queda asegurarse de que al presionar el botón
Gen
se seleccione una imagen aleatoria de la matriz con datos de imagen y se cargue en el elemento
<img>
, que está presente en la página debajo de los campos de entrada de texto.
Para equipar la aplicación con esta función, realice la siguiente tarea. Cree un método que se active cuando haga clic en el botón
Gen
Este método debe seleccionar una de las imágenes, cuya información se almacena en la propiedad de estado
allMemeImgs
, y luego realizar acciones que le permitan mostrar esta imagen en el elemento
<img>
ubicado debajo de los campos de entrada de texto.
allMemeImgs
en
allMemeImgs
que
allMemeImgs
almacena una matriz de objetos que describen las imágenes, y que cada objeto de esta matriz tiene una propiedad de
url
.
Aquí está el código que proporciona una solución a este 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
Al botón
Gen
se le puede asignar un controlador de eventos que ocurre cuando hace clic en él, como es el caso con cualquier otro botón. Sin embargo, dado el hecho de que este botón se usa para enviar el formulario, sería mejor usar el
onSubmit
eventos
onSubmit
formulario. En este controlador,
handleSubmit()
, llamamos al método del evento
event.preventDefault()
entra en él para cancelar el procedimiento estándar de
event.preventDefault()
formularios durante el cual se recarga la página. Luego, obtenemos un número aleatorio en el rango de 0 al valor correspondiente al índice del último elemento de la matriz
allMemeImgs
y usamos este número para referirnos al elemento con el índice correspondiente. Volviendo al elemento que es el objeto, obtenemos la propiedad de esta
url
objeto y la escribimos en la propiedad de estado
randomImg
. Después de eso, el componente se vuelve a representar y la apariencia de la página cambia.
Página de aplicación en el navegadorProyecto de curso completado.
Resumen
En esta lección, creó una aplicación que usa lo que aprendió mientras dominaba React. La próxima vez hablaremos sobre el desarrollo de aplicaciones modernas de React y discutiremos ideas de proyectos, implementando lo que puede practicar trabajando con React.
Estimados lectores! ¿Ha encontrado alguna dificultad al completar este proyecto del curso?