Primeros Pasos

ReactJS 101

¿Por qué ReactJS?

  • Angular VS React
  • ¿Esta de moda?
  • Metodología de componentes

Michelle Torres

Lead Software Engineer at TiempoDevelopment • Professor at UdG • Founder & CTO at @CORBmx • Speaker • Free software advocate • Crazy
  • michelle.torres@corb.mx
  • fb.com/michelletorres.mx
  • nmicht
  • nmicht
Vamos a trabajar este taller haciendo un Pomodorito https://github.com/nmicht/pomodorito

Instalar NPM

Para iniciar, tenemos que instalar NPM. NPM (node package manager) es el gestor de paquetes javascript de NODE.JS, y te recomiendo instalarlo usando. NVM NVM es el Gestor de Versiones de Node.

Instalación

Instalar create-react-app
npm install -g create-react-app

-g lo utilizamos para indicar que será una instalación global.

Crea tu primer App

  1. Crear tu app
    create-react-app pomodorito
  2. Arrancar el servidor de pruebas en una consolado por aparte
    
    cd pomodorito/
    npm start
              
  3. Para arrancar el servidor, puedes usar también
    yarn start

Estructura de tu App

Todo tu código fuente incluídos assets va en src, es la que se compila.
public tiene todo lo público al servidor web estático.
Entendiendo el archivo App.js
Manos a la obra

Componente del contador

  1. Crear carpeta de componentes
    
    cd src
    mkdir components
                
  2. Crear carpeta para el componente de contador
    
    cd components
    mkdir Counter
                

Nuestro componente por estándar se define en CamelCase.

  1. Crear archivo del componente como tal
    
    cd Counter
    touch Counter.js
                
  2. Crear archivo para manejar la carpeta como módulo
    touch index.js

ReactJS cuando hace un import de un folder, busca un index.js

En el archivo Counter.js vamos a crear un componente representacional. También se le conoce como stateless component. Este componente será el contador de nuestro pomodorito.

import React from 'react';

const Counter = () => {
   return (
     00:00
   );
}

export default Counter;
        
Ahora a nuestro index.js le diremos que utilice nuestro componente Counter

import Counter from './Counter';
export default Counter;
        
Rendereamos nuestro componente Counter
  1. Importamos nuestro counter
    import Counter from './components/Counter';
  2. En el render agregamos el componente

import Counter from './components/Counter';
...
render() {
    return (
      
...
); } ...

Props

  1. Son las propiedades de los componentes. Estos se le pasan al componente desde el componente padre.
  2. En el renderizado del componente se definen como si fueran atributos de html.
  3. En la declaración del componente se usan a través del objeto props.
    
    const Counter = (props) => {
       return (
         {props.minutes}:{props.seconds}
       );
    }
                

PropTypes

PropTypes es la definición de las propiedades que tendrá tu componente, para hacer claro/documentable tu código.
Antes eran parte de React, pero ahora es decisión del developer, por lo cual tienes que instalarlo como una dependencia.
npm install prop-types --save
Importamos el módulo
import PropTypes from 'prop-types';
Describimos nuestros props

Counter.propTypes = {
  time: PropTypes.number.isRequired,
}
        

Destructurización

Para simplificar el uso de los props usamos la destructurización, que extrae los valores del objeto.

const Counter = ({minutes, seconds}) => {
   return (
     {minutes}:{seconds}
   );
}
        

Componente de acciones Pomodoro

Crear carpeta para el componente de contador

cd components
mkdir Pomodoro
        

Nuestro componente por estándar se define en CamelCase.

  1. Crear archivo del componente como tal
    
    cd Pomodoro
    touch Pomodoro.js
              
  2. Crear archivo para manejar la carpeta como módulo
    touch index.js

ReactJS cuando hace un import de un folder, busca un index.js

En el archivo Pomodoro.js vamos a renderear todo nuestro Pomodorito

import React from 'react';
import Pomodoro from '../Pomodoro';

const Pomodoro = () => {
   return (
     
{ /*...botones...*/ }
); } export default Pomodoro;
Ahora a nuestro index.js le diremos que utilice nuestro componente Pomodoro

import Pomodoro from './Pomodoro';
export default Pomodoro;
      
Modificamos nuestro archivo App.js para renderizar el Pomodoro y ya no el Counter.

import React, { Component } from 'react';
import Pomodoro from './components/Pomodoro';
...

class App extends Component {
  render() {
    return (
        ...
        
        ...
    );
  }
}

export default App;
      

La biblioteca moment.js

moment se usa para manejo de fechas en JS.
  1. Dentro del folder de Pomodorito instalamos la biblioteca
  2. npm install moment --save

--save es para que se guarde como dependencia en nuestro proyecto.

Agregamos moment al Counter


import React from 'react';
import moment from 'moment';

const Counter = ({time}) => {
   return (
     {moment(time).format('mm:ss')}
   );
}

export default Counter;
      
Indicamos al counter la cantidad de tiempo que queremos mostrar. Este cambio se hace en los props.
El tiempo es en milisegundos.

State Component

Convertimos nuestro componente stateless Pomodoro a un componente con estado.

import React from 'react';
import Counter from '../Counter';

class Pomodoro extends React.Component {
  render() {
    return (
      
{ /*botones*/ }
); } } export default Pomodoro;

Constructor de un componente


constructor(props) {
    super(props);

    this.state = {
      intervalId: null, //ID for the interval to update the time
      breaksCount: 0, //Count for each "time break"
      currentBreak: 'pomodoro', //Type of current "time break"
    }
}
      
Método para crear un "tiempo"

setDuration(minutes) {
  return moment.duration(minutes * 60, 'seconds');
}
      
La agregamos en nuestra clase Pomodoro.

No olvides importar el módulo de moment.

Definimos las propiedades del Pomodoro.

Esto en nuestra App.js que lo renderiza, le definimos los minutos para el timer, para el descanso corto y para el descanso largo.


      
Usando los props, agregamos al estado de nuestro componente el valor para mantener la duración del timer.
currentDuration: this.setDuration(props.pomodoro),

¿Y los botones?

Ahora, ya que tenemos el timer necesitamos un botón que lo active.

Hagamos que inicie el counter

A nuestro botón, le ponemos el evento
onClick={this.handleStartClick}
Y en nuestra clase agregamos el método

handleStartClick = () => {
   const intervalId = setInterval( () => {
     console.log('corriendo intervalo');
   }, 1000);
   this.setState({ intervalId });
}
      

Timer


timer = () => {
  this.setState((state) => {
      const seconds = state.currentDuration.asSeconds();
      if (seconds <= 1) {
          clearInterval(state.intervalId);
          // Debemos cambiar al nuevo break y aumentar el contador de breaks
      }
      return {
        currentDuration: moment.duration(seconds - 1, 'seconds')
      };
  });
}
      

Cuando retornas en la función setState el objeto se va a mergear con el state actual.

Al counter le mandamos el currentDuration para hacerlo dinamico
Modificamos el handle del click para ejecutar nuestro timer

handleStartClick = () => {
   const intervalId = setInterval( this.timer, 1000);
   this.setState({ intervalId });
}
      

Cambiar de break cuando termina el timer

Después de que nuestro contador llega a 0, necesitamos cambiar al siguiente "break"/bloque.

getNextTime = () => {
  if (this.state.currentBreak === 'pomodoro') {
      if (this.state.breaksCount === MAX_BREAKS) {
        return 'longBreak';
      }

      return 'shortBreak';
  }

  return 'pomodoro';
}
      

Cambiar de break cuando termina el timer

La lógica para cambiar de break debería estar dentro de un método para cada que finaliza el timer.

finishTime = () => {
  let breaksCount = this.state.breaksCount;

  if (this.state.currentBreak === 'shortBreak') {
    breaksCount += 1;
  } else if (breaksCount === MAX_BREAKS) {
    breaksCount = 0;
  }

  const nextTime = this.getNextTime();

  return {
      currentBreak: nextTime,
      currentDuration: this.setDuration(this.props[nextTime]),
      breaksCount,
  };
}
      
Ahora nuestro timer deberá ejecutar el finishTime
return this.finishTime();

Push Notifications

Para hacer que nuestro browser arroje notificaciones al terminar un break instalaremos push.js.
npm install push.js --save
Creamos un método para manejar las notificaciones dentro de nuestro App.js

handleTimerFinish = (finishedTime) => {
  Push.create(`Terminó tu ${finishedTime}`, {
    body: 'Da click aquí para ir al Pomodorito',
    icon: 'icon.png',
    timeout: 4000,
    onClick: function() {
      window.focus();
      this.close();
    }
  });
}
      
Mandamos como props al Pomodoro el callback


      
y no olvides agregarlo a tus PropTypes
onTimerFinish: PropTypes.func,
Cuando se termine nuestro tiempo, queremos ejecutar un callback que se encargue de lanzar la notificación.
Entonces agregaremos en el método finishTime la llamada.
this.props.onTimerFinish(this.state.currentBreak);
Ahora hagamos que se vea bonito
Existen muchos paquetes para dar estilo, sin embargo, para terminar el workshop implementaremos CSS de la manera sencilla y rápida.
Creamos un objeto de styles

const styles = {
  fontSize: "42px",
  backgroundColor: "rgb(225, 225, 225)",
  padding: "35px",
  display: "inline-block",
  margin: "40px",
}
      
Asignamos el style a través de CSS inline
{moment(time).format('mm:ss')}
GRACIAS