Guest User

react

a guest
Feb 23rd, 2019
95
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 19.30 KB | None | 0 0
  1. # React Timer
  2.  
  3. ## Introducción
  4.  
  5. ### Objetivos
  6.  
  7. Vamos a codear un Timer y un CountDown. En el primero, va a haber un reloj que empiece en cero, cuando el usuario haga click en Empezar, el reloj va a empezar a contar segundos, el Botón se deberá cambiar a Pausar, y si el usuario lo clickque se deberá pausar el cronómetro. Siempre al lado habrá un botón de reiniciar, que volverá el reloj a cero.
  8.  
  9. ![Timer](./img/Timer.png)
  10.  
  11. Para CountDown, vamos a pedir un input al usuario: un número entero que respresente una cantidad de segundos a contar. El reloj iniciará en la cantidad de segundos que introdujo el usuario y empezará a decrecer hasta llegar a cero, en ese caso se mostrará una alerta. De nuevo va a aparecer el botón de pausar cuando el reloj esté en funcionamiento, y el de Reiniciar para volver a comenzar.
  12.  
  13. ![CountDown](./img/CountDown.png)
  14.  
  15. En las imagenes de arriba podemos ver los mockups de lo que tenemos que codear. Podemos distinguir los siguientes componentes y containers (que podremos reutilizar):
  16.  
  17. ### Organización de la App
  18.  
  19. Para lograr esto vamos a crear componentes reutilizables que nos permitan usar un mismo componente tanto para el Timer como para el CountDown
  20.  
  21. - **Componentes Presentacionales**: Estos componentes no tienen estado simplemente se van a renderizar mostrando lo que le pasemos por props:
  22. + **Timer** (línea negra): Nuestro reloj recibe por props un tiempo en milisegundos y lo formatea con el formato mm:ss.
  23. + **Dashboard** (línea verde): Va a recibir un status, que indica si el contador esta contando o está parado. Y va a recibir una función que es la que se ejecutará cuando se presionen los botones.
  24. + **Button**: Vamos a tener un componente Boton el cual va recibir su contenido de texto, su color y la función que va a ejecutar cuando se clickeá
  25. + **Input** (línea violeta): Recibe una función que modifica la cantidad de segundos que va a contar el contados.
  26. + **Navbar** (linea roja): Va a tener una función que diga si estamos en modo `timer` o `countDown`.
  27. - **Container Principal** (línea celeste): Este container va a contener (invocar) a los demás Componentes. Además va a mantener el estado del reloj, y es donde vamos a definir las funciones que pasaremos a los demás Componentes.
  28.  
  29.  
  30. ## Largada
  31.  
  32. ### Punto de Arranque
  33.  
  34. Para comenzar este workshop vamos a utilizar este repo que nos sirve como punto de partida.
  35.  
  36. - Hace un fork de este repo
  37. - Clonalo en tu computadora
  38. - Corre `npm i` para instalar dependencias
  39. - Una vez que termina de instalarse `npm start` para levantar el server y correr webpack
  40. - Entra al puerto `3000` de tu *localhost*
  41.  
  42.  
  43. ### Hello React
  44.  
  45. Es momento de agregar React a nuestra aplicación. El primer paso para crear nuestro componente de React va ser instalar `react` como dependencia.
  46.  
  47. ```sh
  48. $ npm i react
  49. ```
  50.  
  51.  
  52. Ahora si, agreguemos React. ¿Pero en dónde? Si entramos en nuestro `webpack.config.js` podemos ver que nuestro entry point se encuentra en `./src/App.jsx` por lo tanto ahí es dónde nuestra aplicación comienza. Dirigite a ese archivo e importa React como dependencia.
  53.  
  54. ```js
  55. import React from 'react';
  56. ```
  57.  
  58. Ahora creemos nuestra primer contenedor de React que vamos a llamar `App`. Esto va a ser una clase que se extienda de `React.Component` y creemos una función render que simplemente muestre un Hello World.
  59.  
  60. Para crear una clase de componente hacemos lo siguiente:
  61.  
  62. ```js
  63. class App extends React.Component {
  64.  
  65. }
  66. ```
  67.  
  68. Ahora a esto le agregamos el método `render` el cual React va a ejecutar cuando tenga que renderizar este componente:
  69.  
  70.  
  71.  
  72. ```JSX
  73. class App extends React.Component {
  74. render() {
  75. return <div>Hello World</div>
  76. }
  77. }
  78. ```
  79.  
  80.  
  81. ### Agregando al DOM
  82.  
  83. Ahora que tenemos nuestro componente creado es momento de anexarlo al DOM. Para eso vamos a necesitar otra librería, `react-dom`. Así que primero instalala como dependencia:
  84.  
  85. ```sh
  86. $ npm i react-dom
  87. ```
  88.  
  89. Ahora vamos a requerir un método específico de esta librería, `render`.
  90.  
  91. ```js
  92. import { render } from 'react-dom';
  93. ```
  94.  
  95. Una vez que hacemos eso, vamos a decirle que nos renderize nuestro componente `App` al `div#app` que podés ver que ya esta creado en nuestro `index.html`.
  96.  
  97. ```js
  98. render(<App />, document.getElementById('app'))
  99. ```
  100.  
  101. Ahora solo falta agregar el script que pida el **bundle** en nuestro HTML
  102. ```
  103. <script src="/bundle.js"></script>
  104. ```
  105.  
  106.  
  107. Genial si todo esta bien, y seguís corriendo `webpack` en una pestaña de tu consola, al refrescar la página deberías ver un *Hello World* en tu browser.
  108.  
  109. ## Timer.jsx
  110.  
  111. ### Creando el Reloj
  112.  
  113. Ahora vamos a crear un nuevo componente el cual va a renderizar el tiempo.
  114. Lo importante de este componente es que va a ser presentacional, esto significa que no va a tener un estado propio, simplemente va a tomar unos props que le van a decir como se tiene que presentar.
  115.  
  116. Cuando escribimos componentes presentacionales en React los escribimos como funciones puras.
  117.  
  118. ```js
  119. function Timer() {
  120.  
  121. }
  122. ```
  123.  
  124. Esta función va a tomar props como parámetros y va a devolver el JSX que queres que renderize.
  125.  
  126. En este caso nuestro Timer va tomar el tiempo en milisegundos y lo va a mostrar en formato mm:ss. Pero antes de hacer eso intentemos ver el Timer en la página con un tiempo harcodeado.
  127.  
  128. Hace el componente Timer en el archivo `Timer.jsx` dentro del directorio `src`y hace una función que devuelva esto:
  129.  
  130. ```html
  131. <div>00:00</div>
  132. ```
  133.  
  134. Ahora exporta el componente e importalo en tu `App.jsx` y agregalo a tu Contendor:
  135.  
  136. ```JSX
  137. import Timer from './Timer.jsx';
  138.  
  139. class App extends React.Component {
  140. render() {
  141. return (
  142. <div>
  143. <Timer />
  144. </div>
  145. )
  146. }
  147. }
  148. ```
  149.  
  150. Fijate que lo importamos sin llaves así que es una exportación por default.
  151.  
  152. Si hiciste todos estos pasos correctamente deberías ver en tu página el reloj renderizado.
  153.  
  154. ### Agregando un Estado
  155.  
  156. Ahora que ya tenemos nuestro Timer renderizado es momento de hacerlo más dinámico. Vamos a darle un estado a nuestro contenedor `App` con la cantidad de mili-segundos el cual va a pasarlo como prop al componente `Timer`.
  157.  
  158. El estado de un Contenedor se define dentro de el constructor del mismo. Así que vamos a crear una propiedad `time` al estado. El cual va a empezar en `0`, y vamos a pasar el mismo como prop de Timer.
  159.  
  160.  
  161. ```JSX
  162. class App extends React.Component {
  163. constructor() {
  164. this.state = {
  165. time: 0,
  166. };
  167. }
  168. render() {
  169. return (
  170. <div>
  171. <Timer time={this.state.time} />
  172. </div>
  173. )
  174. }
  175. }
  176. ```
  177.  
  178. Ahora que el estado esta definido, es momento de verlo en Timer. Toma el parámetro `props` dentro de la función de `Timer` y haz un console.log para verlo.
  179.  
  180. Ahora en vez de renderear el "00:00" hardcodeado, haz que se vea el tiempo del prop. Recuerda de formatear el numero de mili-segundos al formato mm:ss.
  181.  
  182. También recordá que cuando queremos mostrar el resultado de una expresión en JSX lo tenemos que poner entre llaves por ejemplo `{2 + 5}` o `{valorGuardado}`.
  183.  
  184. Si querés ver que tu forma de resolver el formateo funciona, no dudes en cambiar el valor del estado a distintos números para ver como se actualiza.
  185.  
  186. Si todo sale bien, la vista no debería cambiar, pero ahora en vez de ver el valor harcodeado de "00:00" lo estamos generando dinámicamente.
  187.  
  188. ## Button.jsx
  189.  
  190. ### Componentes Reusables
  191.  
  192. Una de las grandes ventajas de React es poder hacer los componentes híper-customizados de tal forma que dinámicamente se ajusten a mis necesidades. Por ejemplo, podemos tener un Componente navbar, que le pasemos un arreglo de links y nombres y genere dinámicamente un navbar con todos sus links.
  193.  
  194. Más cosas decidamos de forma dinámica y no esten hard-codeados más fácil se vuelve re-usar ese componente en otras partes de nuestra aplicación y hasta en otras aplicaciones! Imaginate las horas ganadas en desarrollo si solo tenemos que importar un componente decidir sus parámetros y no tener que crearlo desde cero!
  195.  
  196. En este caso vamos a generar un componente `Button` el cual va tomar un texto a mostrar, un color de fondo y una acción que tiene que ejecutar cuando es clickeado.
  197.  
  198. ### Children del Componente
  199.  
  200. Para el texto del botón vamos a usar el `children` del componente. `children` es un tipo de prop especial de JSX que entra de manera distinta a las otras propiedades. Por ejemplo si usamos un componente de esta forma:
  201.  
  202. ```JSX
  203. <MyComponent>Hello World</MyComponent>
  204. ```
  205.  
  206. Eso que esta dentro del componente se agrega automáticamente a la propiedad `children` del `props`.
  207.  
  208. ```JSX
  209. function MyComponent(props) {
  210. return <div>{props.children}</div>
  211. }
  212. ```
  213.  
  214. Esto renderezaría un div con un "Hello World".
  215.  
  216. De esta misma manera vamos a pasar el texto a nuestro Botón. Esto significa que tenemos una forma súper-intuitiva de decidir que va a mostrar nuestro botón imaginate esto:
  217.  
  218. ```JSX
  219. // nuestra aplicación
  220.  
  221. <Button>Reiniciar</Button>
  222. <Button>Pausar</Button>
  223.  
  224. // otra aplicación
  225.  
  226. <Button>Login</Button>
  227. <Button>Register</Button>
  228. ```
  229.  
  230. Ahora si es momento de crear el archivo `Button.jsx` y crear nuestro componente presentacional `Button`. Recuerda que el componente es presentacional porque no tiene un estado propio, solo recibe props del contenedor. Muestra el texto usando `props.children`. A continuación veremos como agregarle estilo y un click handler. Podes utilizar el `<button>` nativo de html.
  231.  
  232. Fijate si agregás el botón a tu container podes verlo, agrega varios con textos distintos para saber si todo esta como corresponde.
  233.  
  234. ### Inline Style
  235.  
  236. Una practica común cuando queremos crear estilos dinámicos en un componente es agregarlo un inline style. Esto significa un atributo style dentro del tag del elemento html. Lo bueno es que con React podemos agregar un objeto Javascript al style y este lo va a traducir a CSS.
  237.  
  238. Por ejemplo:
  239.  
  240. ```JSX
  241. <div style={{
  242. fontSize: '38px',
  243. backgroundColor: '#fff444'
  244. }}>
  245. Soy un texto
  246. </div>
  247. ```
  248.  
  249. Si ves, como son propiedades Javascript, todas las propiedades CSS que tenían un guión (background-color) se cambian por la letra mayúscula (backgroundColor).
  250.  
  251. Ahora que sabes esto, toma un prop `color` en tu componente `Button` y asignáselo al color de fondo del `<button>`.
  252.  
  253. Prueba agregar varios `Button` con distintos colores para ver si funciona.
  254.  
  255. ### onClick
  256.  
  257. Agregar un eventListener a un elemento es bastante distinto a como estábamos acostumbrados. Para agregar un elemento lo que vamos a tener que hacer es darle como propiedad del elemento html el nombre del evento y la función callback que queremos que ejecute.
  258.  
  259. Por ejemplo si querríamos agregar un evento onChange a un input tendríamos que hacer
  260.  
  261.  
  262. ```JSX
  263. <input type="text" onChange={callbackHandler} />
  264. ```
  265.  
  266. Lo mismo podemos hacer con muchos eventos del DOM. [Si queres saber mas acerca de eventos en React entra aca](https://reactjs.org/docs/events.html). En este caso vamos a estar usando `onClick`.
  267.  
  268. Lo que tenés que hacer ahora es definir un onClick dentro del `<button>` que ejecute una función que le pasamos por prop al `Button`.
  269.  
  270. Proba pasando distintas funciones dentro de nuestro `App` al componente `<Button>` para ver si lo ejecuta cuando clickeamos el botón.
  271.  
  272. ### Haciendo Funcionar al Timer
  273.  
  274. Bueno ahora si tenemos botones y un reloj, hagamos correr nuestro timer. Lo que queremos hacer ahora es crear una función que le vamos a pasar a `Button`
  275. para que ejecute cuando es clickeado, y esta función va a actualizar el estado, para que nuestra vista de los mili-segundos cambie
  276.  
  277. #### Cambiar el estado
  278.  
  279. Para cambiar el estado tenemos que definir la función dentro del contenedor, y hacer que esa función ejecute `this.setState`, esta función toma un objeto con el nuevo estado a actualizar. Cada vez que cambiamos el estado, la vista es actualizada, al ejecutar otra vez la función render del contenedor con el nuevo estado.
  280.  
  281. #### Mantener el Contexto
  282.  
  283. Pensemos lo siguiente. Definimos una función dentro de nuestra clase. Esa clase tiene un método que utiliza `this`, por lo tanto esta función se tiene que ejecutar en el contexto de este contenedor. Pero, cuando pasamos la función para que un eventHandler lo ejecute sabemos que eso nos va a hacer perder el contexto. Por lo tanto tenemos que bindear la función a nuestro componente para que no pierda el contexto.
  284.  
  285. Esto es algo muy común cuando empezamos a escribir funciones en React. Una buena práctica siempre que definimos una función que utiliza `this` y nosotros no somos los encargados de ejecutarla, es en el constructor hacer lo siguiente:
  286.  
  287. ```js
  288. constructor(){
  289. // otras cosas del constructor...
  290.  
  291. this.estaFnUsaThis = this.estaFnUsaThis.bind(this);
  292. }
  293. ```
  294.  
  295. > **OJO!** Muy probablemente necesites crear un `setInterval` que actualice el estado constantemente para hacer funcionar el reloj. Recordá que `setInterval` también ejecuta nuestra función en otro contexto, y también vas a tener que encontrar una solución para esto. Puede ser con `bind` o con _arrow functions_.
  296.  
  297. ---
  298.  
  299. Genial, ahora deberías ser capaz de crear la función que necesitamos pasar a nuestro botón para arrancar el Timer. Pero solo estamos haciendo eso, corriendo el reloj, empecemos a darle mayor funcionalidad a nuestro reloj.
  300.  
  301. ## Dashboard.jsx
  302.  
  303. ### Render Condicional
  304.  
  305. Algo que nos permite hacer a React tan customizable es la posibilidad de decidir de forma condicional que queremos renderear. Hay muchas formas distintas de lograr esto, pero la forma más común y en la mayoría de los casos más prolija de lograrlo es con nuestro operador ternario. El operador ternario tiene la ventaja que nuestra condición va a devolver un valor, por lo tanto podemos decirle: "si esta condición es verdad renderizá esto, sino renderizá esto otro".
  306.  
  307. Por ejemplo, imaginense que tenemos un componente que si el usuario esta logueado queremos mostrar el boton de logout pero si no hay una sesión queremos mostrar el botón de login:
  308.  
  309. ```JSX
  310. function LoginOrLogoutButton(props) {
  311. return (<button>
  312. {props.currentUser ? 'Log Out' : 'Login'}
  313. </button>);
  314. }
  315. ```
  316.  
  317. Este tipo de condiciones se puede hacer para absolutamente todo, no solo texto. Lo podemos usar para decidir clases, estilos, componentes, etc.
  318.  
  319. Veamos un ejemplo más, yo quiero ver, dependiendo un item si lo quiero borrar o agregar y para cada acción tengo un componente distinto que se encarga de eso:
  320.  
  321. ```JSX
  322. function Item(props) {
  323. return (<div>
  324. <p>{props.name}</p>
  325. {props.hasItem ? <DeleteItem /> : <AddItem />}
  326. </div>);
  327. }
  328. ```
  329.  
  330. Esta herramienta es súper útil en la mayoría de los casos, otras veces si nuestras condiciones o devoluciones son muy complejas por ahí nos conviene generar funciones aparte que se encarguen de devolver el JSX correspondiente.
  331.  
  332. ### Funcionalidad del Dashboard
  333.  
  334. Sabemos que nuestro Dashboard va a tener que comenzar el reloj, pausarlo, y reiniciarlo. Así que antes de continuar creemos esta funcionalidad.
  335.  
  336. #### Pausando el Intervalo
  337.  
  338. Recuerda que el `setInterval` retorna un identificador que después podemos usar en `clearInterval` para detener el intervalo. Guarda este identificador en el estado del contenedor, y asegurate que cuando no haya un intervalo el valor sea `null`.
  339.  
  340.  
  341. #### Reiniciando el Reloj
  342.  
  343. El reiniciado es muy parecido al pausado, pero ademas tiene que poner el tiempo otra vez en cero.
  344.  
  345. ---
  346.  
  347. Prueba creando botones de pausa y reinicio que ejecute estas funciones a ver si todo está funcionando correctamente. Una vez que todo funcione continuá.
  348.  
  349.  
  350. ### Creando el Dashboard
  351.  
  352. Ahora si llego el momento. Vamos a crear nuestro archivo `Dashboard.jsx`. Este va a siempre mostrar el botón de reiniciar, y va a decidir de forma condicional si mostrar el botón de Pausa, con color de fondo amarillo, o de Comenzar, color de fondo verde. ¿Qué podemos utilizar, que ya se encuentra en el estado de nuestro contenedor, para saber si el reloj esta corriendo o no?
  353.  
  354. Agregalo al `App` y borra los botones que habíamos puesto ahí originalmente. No te olvides de que el `<Dashboard />` va a tener que recibir las tres funciones que le pasamos y dárselas al botón que corresponde.
  355.  
  356. Si hiciste todo correctamente, apretar el botón de comenzar debería arrancar el reloj y mostrar el botón de pausa. Clickear el de pausa detiene el reloj y muestra otra vez el botón de Comenzar, y lo mismo el de Reiniciar solo que vuelve el reloj a cero.
  357.  
  358. ## Navbar.jsx
  359.  
  360. ### Agregando el Navbar
  361.  
  362. Ya tenemos nuestro Timer hecho, pero no nos olvidemos el objetivo principal que teníamos tener un Timer y un CountDown. El navbar nos va a ayudar a elegir en que modo queremos estar.
  363.  
  364. Crea el componente `Navbar` el cual va a tener dos botones, uno va a ser "Timer" y el otro "CountDown". Generá una referencia en nuestro estado, que diga en que modo estamos, y que estos botones se encarguen de cambiarlo.
  365.  
  366. Recuerda el workflow de esto:
  367.  
  368. - Generá las funciones en nuestro contenedor (no te olvides del contexto!)
  369. - Pasalo al componente `<Navbar />`
  370. - Asegurate que Navbar llame a estas funciones cuando hacemos click
  371.  
  372. ### Clase .active
  373.  
  374. Si ves tenemos un archivo `styles.css` en el cual tenemos definido una clase `.active`, esta clase que hasta ahora no la estuvimos usando es para que dependiendo de cual es el modo que estamos el botón correspondiente tenga esa clase.
  375.  
  376. Por ejemplo: si estamos en modo `"timer"` el botón de timer tendría la clase `.active` y el botón de CountDown no lo tendría, y viceversa si estuviésemos en modo `"countDown"`.
  377.  
  378. > **IMPORTANTE: CLASES EN REACT**: Dado que la palabra `class` es un keyword reservado en Javascript, cuando agregamos una clase a un tag tenemos que usar la propiedad `className`, e.g `<div className="active">`.
  379.  
  380.  
  381. ### Cambiando Funcionalidad
  382.  
  383. Ahora queremos que dependiendo el Modo nuestro timer vaya para arriba o para abajo, así que haz que tu función dependiendo del estado haga una acción o la otra.
  384.  
  385. Ademas en el modo CountDown vamos a querer que se agregue un input en el cual podamos entrar un valor al apretar enter y actualice el estado del Reloj. Para esto usa el evento onKeypress. Este recibe información del evento, acordate que en la propiedad `target` del evento podemos acceder al input y también podemos acceder al `keyCode` para saber si es un enter.
  386.  
  387.  
  388. ## Credito Extra
  389.  
  390. ### Laps
  391.  
  392. Agregá un botón "Laps" al Dashboard en el modo Timer que vaya agregando a un arreglo en nuestro estado el tiempo de esa lap.
  393.  
  394. Mostrá una lista de laps con su respectivos tiempos en la vista. Acordate que cuando agregamos un elemento al arreglo no tenemos que mutar el arreglo sino que tenemos que mantener el estado inmutable, osea que no podes cambiar el arreglo, sino devolver un arreglo nuevo con el elemento agregado, hay varias formas de hacer esto, por ejemplo con `.concat` o con el spread operator.
  395.  
  396. ### Error Handling
  397.  
  398. Muestra un mensaje de error debajo del input si se ingresa un valor que no es un numero positivo.
  399.  
  400. ### Detener el Reloj
  401.  
  402. Cuando estamos en modo CountoDown asegurate que el reloj deje de contar una vez que llega a cero, y envia una alerta al usuario diciendo que countdown terminó.
  403.  
  404. ### Mejora el estilo
  405.  
  406. Nuestro reloj se ve medio choto, agrega Bootstrap y dale un poco de forma, podes basarte en las imágenes de los mock ups.
Add Comment
Please, Sign In to add comment