DEV Community

André Gtz
André Gtz

Posted on

Cómo configuro un proyecto simple en React

Cookie Clicker App con React

Instalación

Para crear la aplicación se requiere instalar create-react-app.

$ yarn global add create-react-app $ yarn create react-app cookie-clicker $ cd cookie-clicker 

Instalar eslint

eslint es la forma en la cual el IDE para desarrollar con javascript verifica si hay errores de sintaxis y se refuerza el uso de estilos populares ya aceptados.

yarn busca en los módulos instalados del proyecto eslint y lo ejecuta. No hay necesidad de instalarlo ya que create-react-app lo instala por su cuenta.

$ yarn eslint --init yarn run v1.15.2 $ /.../cookie-clicker/node_modules/.bin/eslint --init ? How would you like to use ESLint? (Use arrow keys) To check syntax only To check syntax and find problems > To check syntax, find problems, and enforce code style 

Seleccionar To check syntax, find problems, and enforce code style

? What type of modules does your project use? (Use arrow keys) > JavaScript modules (import/export) CommonJS (require/exports) None of these 

Seleccionar JavaScript modules (import/export)

? Which framework does your project use? (Use arrow keys) > React Vue.js None of these 

Seleccionar React

? Where does your code run? (Press <space> to select, <a> to toggle all, <i> to invert selection) >◉ Browser ◉ Node 

Seleccionar ambos <a> <enter>

? How would you like to define a style for your project? (Use arrow keys) > Use a popular style guide Answer questions about your style Inspect your JavaScript file(s) 

Seleccionar Use a popular style guide

? Which style guide do you want to follow? (Use arrow keys) > Airbnb (https://github.com/airbnb/javascript) Standard (https://github.com/standard/standard) Google (https://github.com/google/eslint-config-google) 

Seleccionar Airbnb

? What format do you want your config file to be in? JavaScript

Checking peerDependencies of eslint-config-airbnb@latest Local ESLint installation not found. The config that you've selected requires the following dependencies: eslint-plugin-react@^7.11.0 eslint-config-airbnb@latest eslint@^4.19.1 || ^5.3.0 eslint-plugin-import@^2.14.0 eslint-plugin-jsx-a11y@^6.1.1 ? Would you like to install them now with npm? (Y/n) 

Como estamos usando yarn en lugar de npm le decimos que no, vamos a instalar estos paquetes manualmente usando yarn.

$ yarn add eslint-plugin-react@^7.11.0 eslint-config-airbnb@latest eslint-plugin-import@^2.14.0 eslint-plugin-jsx-a11y@^6.1.1 --dev 

Asegurarse de agregar --dev al final, ya que solo se necesita durante el desarrollo del proyecto.

Además se tiene que instalar @babel/plugin-transform-runtime

$ yarn add @babel/plugin-transform-runtime --dev 

Y se puede personalizar el archivo .eslintrc.js, para que se adecúe al estilo de cada equipo.

En este caso agregaremos:

{ . . . parser: 'babel-eslint', rules: { 'react/prop-types': [0,], }, } 

Nota: Según el IDE que se utilice habrá que habilitar que lea el .eslintrc.js

Editores como VS Code ya lo traen integrado.

Ahora si abres el archivo src/App.js debería marcar un error diciendo que los archivos con jsx deberían tener una extensión .jsx en lugar de .js.

Crear el Layout de la aplicación

Utilizaremos material-ui como soporte para varios componentes, iconos y los estilos.

$ yarn add @material-ui/core 

Modificar App.js por App.jsx.

Eliminar import App.css ya que no se utilizará de esta manera los estilos.

Crear 3 contentedores.

  1. Contenedor que contendrá la información de cuantas galletas tienes
  2. Contenedor con la imagen de la galleta
  3. Contenedor con lista de upgrades
import React, { Component } from 'react'; import Typography from '@material-ui/core/Typography'; import Card from '@material-ui/core/Card'; import CardContent from '@material-ui/core/CardContent'; import logo from './logo.svg'; class App extends Component { state = { }; render = () => ( <div className="App"> <div className="info"> <Typography variant="subtitle1"> Tienes X galletas. </Typography> </div> <div className="cookie"> <img src={logo} alt="" /> </div> <div className="upgrades"> <Card className="card"> <CardContent> <Typography className="" color="textSecondary" gutterBottom> +1 Cookie per click [30 cookies] </Typography> </CardContent> </Card> </div> </div> ); } export default App; 

Ahí utilizamos los componentes de material-ui Typography, Card y CardContent. Para más información sobre los componentes visitar la página de material-ui.

Si corres la aplicación usando

$ yarn start 

Se puede observar que aún no tiene estilos más que lo poco que trae el componente de material-ui.

Para agregar los estilos, necesitamos utilizar withStyles que viene incluido en el paquete de material-ui.

import React, { Component } from 'react'; import { withStyles } from '@material-ui/core/styles'; import Typography from '@material-ui/core/Typography'; import Card from '@material-ui/core/Card'; import CardContent from '@material-ui/core/CardContent'; import logo from './logo.svg'; const styles = { App: { height: '100%', width: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'flex-start', }, info: { display: 'flex', justifyContent: 'center', alignItems: 'center', }, cookie: { width: '100%', maxWidth: '500px', }, upgrades: { width: '90%', display: 'flex', flexDirection: 'column', justifyContent: 'flex-start', alignItems: 'center', }, card: { minWidth: '100%', }, }; class App extends Component { state = { }; render = () => { const { classes } = this.props; return ( <div className={classes.App}> <div className={classes.info}> <Typography variant="subtitle1"> Tienes X galletas. </Typography> </div> <div className={classes.cookie}> <img src={logo} alt="" /> </div> <div className={classes.upgrades}> <Card className={classes.card}> <CardContent> <Typography color="textSecondary" gutterBottom> +1 Cookie per click [30 cookies] </Typography> </CardContent> </Card> </div> </div> ); }; } export default withStyles(styles)(App); 

No es muy cómodo rellenar cada Upgrade manualmente, así que podemos hacer un archivo js para guardar y obtener los upgrades.

Creamos un archivo llamado upgrades.js

const upgrades = [ { mejora: 1, costo: 30, actived: false, }, { mejora: 2, costo: 100, actived: false, }, { mejora: 3, costo: 200, actived: false, }, { mejora: 4, costo: 300, actived: false, }, { mejora: 5, costo: 600, actived: false, }, { mejora: 6, costo: 800, actived: false, }, ]; export default upgrades; 

Y lo utilizamos dentro de App.js

import React, { Component } from 'react'; import { withStyles } from '@material-ui/core/styles'; import Typography from '@material-ui/core/Typography'; import Card from '@material-ui/core/Card'; import CardContent from '@material-ui/core/CardContent'; import logo from './logo.svg'; // Importamos los upgrades import UPGRADES from './upgrades'; const styles = { App: { height: '100%', width: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'flex-start', }, info: { display: 'flex', justifyContent: 'center', alignItems: 'center', }, cookie: { width: '100%', maxWidth: '500px', }, upgrades: { width: '90%', display: 'flex', flexDirection: 'column', justifyContent: 'flex-start', alignItems: 'center', }, card: { minWidth: '100%', }, // Nuevo estilo para mostrar si ya se activó un upgrade activedBg: { backgroundColor: 'greenyellow', }, }; class App extends Component { // Agregamos el estado de los upgrades, el cual vamos a modificar para // actualizar si ya se activó o aún no.  state = { upgrades: [], }; // Es importante utilizar componentDidMount para cargar todos los datos // que se van a utilizar al renderizar el componente. // Si se necesita cargar la información antes de renderizar, si utiliza // componentWillMount componentDidMount = () => { // Cargamos upgrades al estado this.setState({ upgrades: UPGRADES }); }; render = () => { // Es una buena práctica descomponer el estado y los props const { classes } = this.props; const { upgrades } = this.state; return ( <div className={classes.App}> <div className={classes.info}> <Typography variant="subtitle1"> Tienes X galletas. </Typography> </div> <div className={classes.cookie}> <img src={logo} alt="" /> </div> <div className={classes.upgrades}> {/* Mapeamos los upgrades para ponerlos en su Card*/} {upgrades.map(upgrade => ( <Card className={classes.card}> <CardContent> <Typography className={upgrade.actived ? classes.activedBg : ''} color="textSecondary" > {`+${upgrade.mejora} Cookie per click [${upgrade.costo} cookies]`} </Typography> </CardContent> </Card> ))} </div> </div> ); }; } export default withStyles(styles)(App); 

Implementando Estados

  • Cuando se haga click en la galleta, aumentar el total de galletas por la cantidad adecuada.
  • Cuando se haga click en una mejora, aumentar la cantidad de galletas por click
  • Cuando se haga click en una mejora y se tiene cantidad suficiente de galletas, restar las galletas del total y aumentar el costo de la mejora.

A partir de esas necesidades podemos determinar un estado:

state = { upgrades: [], cookiesPerClick: 1, totalCookies: 0, }; 

El handler del click a la galleta

cookieClick = (amount) => { const { totalCookies } = this.state; this.setState({ totalCookies: (amount + totalCookies) }); }; 

El handler del upgrade

clickMejora = (upgrade) => { const { totalCookies, cookiesPerClick, upgrades } = this.state; if (totalCookies >= upgrade.costo) { // findIndex es un método de los arreglos, si la condición es true, regresa el index const upgradeIndex = upgrades.findIndex(up => up.mejora === upgrade.mejora); const newCosto = Math.round(upgrade.costo * 1.15); // Probar que pasa si se hace:  // upgrades[upgradeIndex].costo = newCosto; upgrades[upgradeIndex] = { ...upgrades[upgradeIndex], costo: newCosto, }; this.setState({ totalCookies: (totalCookies - upgrade.costo), cookiesPerClick: (cookiesPerClick + upgrade.mejora), upgrades, }); } }; 

Se agregan los eventos onClick

render = () => { const { classes } = this.props; const { upgrades, totalCookies, cookiesPerClick } = this.state; return ( <div className={classes.App}> <div className={classes.info}> <Typography variant="subtitle1"> {`Tienes ${totalCookies} galletas. Ratio: ${cookiesPerClick}`} </Typography> </div> <div className={classes.cookie} onClick={() => this.cookieClick(cookiesPerClick)} onKeyPress={() => {}} role="button" tabIndex="0" > <img src={logo} alt="" /> </div> <div className={classes.upgrades}> {upgrades.map(upgrade => ( <Card className={classes.card} key={upgrade.mejora} onClick={() => this.clickMejora(upgrade)} > <CardContent> <Typography className={upgrade.actived ? classes.activedBg : ''} color="textSecondary" > {`+${upgrade.mejora} Cookie per click [${upgrade.costo} cookies]`} </Typography> </CardContent> </Card> ))} </div> </div> ); }; 

Top comments (0)