Aprenda a reaccionar creando un generador de memes

Los memes son geniales, son una forma muy divertida de describir ideas y opiniones. Así que no es casualidad que elegí una aplicación de generador de memes como proyecto final en mi curso gratuito React en Scrimba. La aplicación funciona extrayendo una imagen aleatoria de meme de una API y colocando su texto sobre ella para crear su propio meme personalizado.
Entonces, en este artículo, te daré una guía paso a paso para crear la aplicación. Si alguna vez se confunde, también puede seguir estos pasos en el curso Scrimba, comenzando en esta conferencia.
Y luego, si le gusta mi estilo de enseñanza y está de humor para un desafío más difícil después de completar este tutorial, consulte mi próximo curso avanzado sobre Scrimba.
Nota: ya debe estar bastante familiarizado con algunos de los conceptos fundamentales de React, como componentes, estado, accesorios y métodos de ciclo de vida. Además, este tutorial no usa Hooks, pero en mi próximo curso cubriremos los Hooks en profundidad y practicaremos muchísimo su uso.

1. Crear el repetitivo y representar un componente de la aplicación

Crear la tarea repetitiva
Lo primero que debemos hacer es crear el código repetitivo para la aplicación. Para hacer esto, importamos ReactReactDOMutilizamos ReactDOMpara representar un componente llamado App, que crearemos más adelante. Luego colocamos el Appcomponente en la 'raíz'. También importamos Appdesde su archivo "./App", que crearemos en breve.
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));
Luego creamos nuestro App.jsarchivo. En él, creamos un componente funcional llamado Appque, por ahora, devuelve un simple <h1>Luego lo exportamos. El <h1>nos permite verificar que la aplicación se muestre correctamente en la pantalla.
import React from 'react';
function App() {
  return <h1>Hello world!</h1>;
}
export default App;
El resultado resultante es este:
Rendido Hola Mundo

2. Crear los componentes de encabezado y MemeGenerator

Crear la tarea Encabezado y MemeGenerator
A continuación, creamos los componentes Header y MemeGenerator. El encabezado solo mostrará elementos, mientras que MemeGenerator llamará a la API y retendrá los datos en estado.
Comencemos creando el Header.jsarchivo. Dado que Header es un componente que solo se usa para mostrar, debe ser un componente funcional. Por ahora, el componente debería devolver un simple <h1>Después de crearlo, exportamos el encabezado.
import React from 'react';
function Header() {
  return <h1>HEADER</h1>;
}
export default Header;
A continuación, creamos el MemeGenerator.jsarchivo. Como el MemeGeneratorcomponente retendrá datos y realizará llamadas a una API, debe ser un componente de clase. Todavía necesitamos importar React, y dado que va a ser un componente de clase, también importaremos Component(que es una importación con nombre ).
MemeGenerator necesita un estado constructor()que llame super()y ya que estará en estado de espera, le agregamos un estado vacío ahora. Al igual que en el componente Encabezado, mostramos un sencillo <h1>para comenzar. Luego exportamos MemeGenerator.
import React, { Component } from 'react';
class MemeGenerator extends Component {
  constructor() {
    super();
    this.state = {}; //empty state
  }
  render() {
    return <h1>MEME GENERATOR SECTION</h1>;
  }
}
export default MemeGenerator;
Ahora, importamos encabezado y MemeGenerator App.jsy creamos una instancia de cada uno en nuestro componente de aplicación. Para mostrar los componentes correctamente, los envolvemos en a <div>.
import React from 'react';
import Header from './Header';
import MemeGenerator from './MemeGenerator';
function App() {
  return (
    <div>
      <Header />
      <MemeGenerator />
    </div>
  );
}
export default App;

3. Completando el componente Encabezado.

Para completar el <Header>componente, agregamos una imagen trollface insertando una <img>etiqueta y configurando el src en la URL de la imagen. Luego agregamos una <p>etiqueta con el nombre de nuestra aplicación y las envolvemos en la <header>etiqueta semántica HTML5 .
function Header() {
  return (
    <header>
      <img
        src='http://www.pngall.com/wp-content/uploads/2016/05/Trollface.png'
        alt='Problem?'
      />
      <p>Meme Generator</p>
    </header>
  );
}
Como el estilo está fuera del alcance de este curso, los estilos CSS ya se han creado y aplicado a la <header>etiqueta. El resultado es este:
Encabezado procesado
Dicho esto, los alumnos siempre pueden jugar con el estilo y perfeccionar sus habilidades de CSS por sí mismos. Con el <Header/>ahora completo, el resto del desafío tendrá lugar en<MemeGenerator/>

4. Estado de inicialización

Inicializando tarea de estado
Ahora tenemos que inicializar el estado para que guarde un texto superior, un texto inferior y una imagen aleatoria, que ya se suministra.
Para hacer esto, construimos el objeto vacío que colocamos en <MemeGenerator/>cuando lo construimos originalmente. Inicializamos topTextbottomTextcomo cadenas vacías y randomImgcomo la URL proporcionada.
class MemeGenerator extends Component {
  constructor() {
    super();
    this.state = {
      topText: '',
      bottomText: '',
      randomImg: 'http://i.imgflip.com/1bij.jpg'
    };
  }
}

5. Realizar la llamada API

Realizar la tarea de llamada API
A continuación, realizamos una llamada API a la URL proporcionada y guardamos los datos devueltos (que es una matriz que se encuentra response.data.memes) en una nueva propiedad de estado llamada allMemeImgs.
Cuando necesitamos cargar datos desde un punto final para usar en nuestro componente, un buen lugar para hacer la solicitud es el componentDidMount()método del ciclo de vida. Tan pronto como se monte el componente, usamos la fetch()función nativa para llamar a la URL proporcionada.
componentDidMount() {
  fetch("https://api.imgflip.com/get_memes")
}
Esto devuelve una promesa que convertimos en un objeto Javascript con el .json()método.
componentDidMount() {
  fetch("https://api.imgflip.com/get_memes")
    .then(response => response.json())
}
Luego obtenemos la respuesta que nos es útil al extraer la matriz de memes response.data.
componentDidMount() {
fetch("https://api.imgflip.com/get_memes")
  .then(response => response.json())
  .then(response => {
  const { memes } = response.data
  })
}
Ahora, guardamos los resultados en una nueva propiedad estatal llamada allMemeImgsPara hacer esto, inicializamos allMemeImgscomo una matriz vacía.
this.state = {
  topText: '',
  bottomText: '',
  randomImg: 'http://i.imgflip.com/1bij.jpg',
  allMemeImgs: []
};
Ahora, de vuelta componentDidMount(), establecemos el estado. Como no estamos interesados ​​en cuál era el estado anterior, nos pusimos allMemeImgsen memes.
componentDidMount() {
  fetch("https://api.imgflip.com/get_memes")
    .then(response => response.json())
    .then(response => {
  const { memes } = response.data
  this.setState({ allMemeImgs: memes })
  })
}
Para asegurarnos de que funciona, tenemos console.logel primer elemento, que se parece a esto:
salida de console.log
Aquí hay una descripción general de toda la componentDidMount()función.
componentDidMount() { //ensure that data is fetched at the beginning
  fetch("https://api.imgflip.com/get_memes") //call to URL
    .then(response => response.json()) //turn promise into JS object
    .then(response => {
  const { memes } = response.data //pull memes array from response.data
  console.log(memes[0]) // check data is present
  this.setState({ allMemeImgs: memes }) // set allMemeImgs state
})
}

6. Crear el formulario de entrada

Ahora queremos crear un formulario que eventualmente permita al usuario ingresar los textos superior e inferior. Hacemos esto con una <form>etiqueta HTML y un simple <button>que dice 'Gen'. Lo diseñamos con el CSS proporcionado previamente.
render() {
  return (
    <div>
      <form className="meme-form">
        <button>Gen</button>
      </form>
    </div>
  )
}
Botón Gen generado

7. Agregar campos de entrada al formulario

Agregar tarea de campos de entrada
A continuación, depende de nosotros agregar los dos campos de entrada (uno para el texto superior y otro para el texto inferior). El formulario debe ser controlado, por lo que necesitaremos agregar todos los atributos necesarios para que eso funcione. Crearemos el onChangecontrolador más tarde.
Creamos dos campos de entrada que tienen el tipo texty los atributos de nombre apropiados ( topTextbottomText). En lugar de usar etiquetas, usamos marcadores de posición: 'Texto superior' y 'Texto inferior'.
Por último, para que esto sea una forma controlada , establecemos el valor como igual al valor actual en statecon {this.state.topText}{this.state.bottomText}.
render() {
  return (
    <div>
      <form className="meme-form">
        <input
          type="text"
          name="topText"
          placeholder="Top Text"
          value={this.state.topText}
        />
        <input
          type="text"
          name="bottomText"
          placeholder="Bottom Text"
          value={this.state.bottomText}
        />
        <button>Gen</button>
      </form>
    </div>
  )
}

8. Crear el controlador onChange.

Crear la tarea del controlador onChange
Ahora, creamos el controlador onChange, que actualizará el estado correspondiente en cada cambio del campo de entrada.
Primero, creamos una handleChange()función que recibe un evento.
handleChange(event) {

}
Ahora, establecemos los onChangedos campos de entrada en igual handleChange.
<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>
Debemos recordar vincular el método en el constructor, un problema común para los desarrolladores de React.
constructor() {
  super()
  this.state = {
    topText: "",
    bottomText: "",
    randomImg: "http://i.imgflip.com/1bij.jpg",
    allMemeImgs: []
  }
  this.handleChange = this.handleChange.bind(this)
}
Para probar la nueva handleChange()función, agregamos un simple console.log:
handleChange(event) {
  console.log("Working!")
}
Si está disparando correctamente, verá algo como esto:
Renderizado console.log ("¡Funcionando!")
Ahora para completar la handleChange()función. Para hacer esto, queremos extraer las propiedades de nombre y valor de event.target para que podamos obtener el nombre del estado que debemos actualizar ( topTextbottomText) y el valor que se escribe en el cuadro.
handleChange(event) {
  const { name, value } = event.target
}
Ahora los utilizaremos para actualizar el estado. Como no estamos interesados ​​en cuál era el estado anterior, solo podemos proporcionar un objeto en el que establezcamos [name]el valor escrito en el campo de entrada.
handleChange(event) {
const {name, value} = event.target
this.setState({ [name]: value })
}

9. Mostrar una imagen de meme junto al texto superior e inferior

Ahora queremos que la aplicación muestre una imagen de meme junto con el texto superior e inferior. Insertamos una <img>etiqueta debajo de <form>y establecemos la randomImgque inicializamos como fuente mediante el uso src={this.state.randomImg}Luego agregamos dos <h2>etiquetas que muestran el texto correspondiente que también se guarda en estado. Todo esto está envuelto en un divestilo con la memeclase proporcionada previamente .
<div className='meme'>
  <img src={this.state.randomImg} alt='' />
  <h2 className='top'>{this.state.topText}</h2>
  <h2 className='bottom'>{this.state.bottomText}</h2>
</div>
Ahora podemos probar la aplicación escribiendo en los cuadros de texto. Como el estado se establece correctamente en cada pulsación de tecla, el texto que se muestra en la imagen cambia cada vez que escribimos.
Ejemplo presentado de progreso hasta ahora

10. Mostrar una imagen aleatoria de meme junto al texto superior e inferior

Mostrar una tarea aleatoria de imagen de meme
Ahora, necesitamos crear un método que muestre una imagen de meme que elija aleatoriamente de nuestra allMemeImgsmatriz cuando Gense haga clic en el botón. La propiedad de la imagen elegida en la matriz es .url.
Podemos dividir esta tarea en partes más pequeñas.
En primer lugar, configuramos los formularios onSubmitpara que sean iguales al nombre de nuestro nuevo método, al que llamaremos handleSubmit().
<form className="meme-form" onSubmit={this.handleSubmit}>
Ahora creamos la handleSubmit()función encima de la render()función. Necesitamos evitar DefaultDefault en el evento, de lo contrario, el método intentará actualizar la página.
handleSubmit(event) {
  event.preventDefault()
}
También necesitamos unirnos handleSubmit()a nuestro constructor().
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)
}
Ahora, necesitamos obtener un número aleatorio, obtener el meme de ese índice y establecerlo randomImgen el .urlelemento aleatorio.
handleSubmit(event) {
  event.preventDefault()
  // get a random int (index in the array)
  // get the meme from that index
  // set `randomImg` to the `.url` of the random item I grabbed
}
Para obtener un número aleatorio, usamos Math.floor(Math.random)Para asegurarnos de que sea uno de los índices en nuestra allMemeImgsmatriz, multiplicamos por la longitud de la matriz.
const randNum = Math.floor(Math.random() * this.state.allMemeImgs.length);
Ahora nos ponemos randMemeImga igual allMemeImgs, con el índice de allMemeImgscomo el randNumque acabamos de obtener. Luego agregamos .urlal final de la misma.
const randMemeImg = this.state.allMemeImgs[randNum].url;
Ahora, todo lo que tenemos que hacer es actualizar el estado actualizando la propiedad randomImg con randMemeImg.
this.setState({ randomImg: randMemeImg });
Nuestra handleSubmit()función completa se ve así:
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 })
}

Generador de memes completado

Aplicación de trabajo
Ahora hemos completado la aplicación del generador de memes y obtenemos una imagen diferente cada vez que presionamos el Genbotón, que luego se superpone con el texto que ingresamos.
Para avanzar en nuestro aprendizaje, podríamos jugar con código y ver si podemos mejorarlo o tratar de obtener imágenes de una API diferente. Para algunas prácticas realmente pesadas, incluso podríamos eliminar todo el código e intentar construirlo nuevamente desde cero.
Felicitaciones por seguir el tutorial y aprender todas las habilidades utilizadas en este proyecto.
Y si estás listo para ello, ¡mira mi próximo curso avanzado , ya que te llevará a un nivel profesional en React!