Transcripciones
1. Introducción: Hola, soy Hunor, y hoy te voy a
enseñar a construir un juego de Tic-Tac-Toe con React. Si no has usado React antes, no te preocupes, vamos a cubrir todos los conceptos básicos de React mientras estamos construyendo este juego. En primer lugar, voy a hablar de cómo descomponer este juego en piezas más pequeñas, en componentes, y cómo conectar estos componentes. Entonces vamos a cubrir cómo hacer que este juego sea interactivo, cuál es el estado, y cómo detectar al ganador al final del juego. El único que necesitas tener es algún conocimiento previo de HTML, CSS, y JavaScript, y después de este curso, vas a poder construir un juego simple o un sitio web con React.
2. Desglosar el juego en componentes: Vamos a crear nuestra aplicación en React. Reaccionar es como una biblioteca basada en componentes. Entonces primero, tenemos que descomponer nuestra aplicación en componentes. Pero, ¿qué es un componente y cómo descomponer una app? Un componente es como una función. Si lo piensas, principalmente escribes funciones por dos razones. Una es descomponer tu lógica de aplicación en bits significativos más pequeños. Una función es básicamente una pieza de código con un nombre. Si alguien lee ese nombre, ojalá, sabría o al menos tendría una buena idea respecto a lo que hace la función. El segundo motivo para escribir una función es evitar la repetición. Si te encuentras escribiendo las mismas líneas una y otra vez, entonces probablemente vas a escribir una función para eso y
llamarla en múltiples lugares en lugar de repetir las mismas líneas. Nosotros vamos a hacer lo mismo con los componentes. Pero antes de llegar ahí, primero, necesitamos un componente inicial el que se inyectará en HTML y que servirá como raíz para todo el resto de la aplicación. En nuestro caso, este va a ser el propio componente TicTacToe. Técnicamente, en realidad podemos detenernos aquí. No se requiere en absoluto tener múltiples componentes. Pero normalmente, si intentas tener toda tu lógica en un solo componente junto con toda la información de diseño, probablemente, va a ser demasiado larga e inmantenible. Entonces empecemos a descomponer nuestra aplicación evitando la repetición. esta app hay dos cosas muy obviamente repitiendo, las cruces y los círculos. También hay una tercera, que no es tan visual, por lo que es fácil perderse. Pero si miras un poco más de cerca, entonces ves que esta cuadrícula consta de nueve cuadrados. Y las plazas no tienen mucho que mostrar, pero vas a ver que su comportamiento es el mismo. Por lo que también van a ser un candidato realmente bueno para un componente. Una vez que terminamos con la repetición, podemos buscar partes de la aplicación que sean más o menos independientes del resto de la aplicación. En nuestro caso, si juegas a través de este juego, entonces terminas en una pantalla de resultados. Por supuesto, esta pantalla de resultados no es totalmente independiente del resto
del juego porque tiene que saber si el juego ha terminado, quién ganó el juego, y cómo restablecer el juego. Pero aparte de eso, no tiene que conocer cada detalle. No tiene por qué saber cuál es el estado de la segunda plaza en la tercera fila. Entonces de esta manera, es independiente. Podría interpretarse como una función con propiedades. Entonces estos van a ser nuestros componentes, pero claro, esta no es la única manera de descomponer una aplicación. Una forma de comprobar si hicimos un buen trabajo es comprobar los nombres de los componentes. Si los nombres de los componentes son significativos y puedes
adivinar más o menos qué hace el componente o cómo se ve, entonces hay una buena probabilidad de que nuestro desglose sea bueno.
3. Escribir nuestros primeros componentes: Empecemos a crear cosas. Para este tutorial, vamos a utilizar CodePen, que es un editor web y plataforma de redes sociales. Cuando creas algo en CodePen, va a ser público para que otras personas puedan verlo, puedan comentarlo, pueden gustarlo, y si es realmente bueno y los editores de los sitios lo vean, entonces tal vez lo resalten y puedas ve a ti mismo en la primera página. Si no has usado CodePen antes, entonces solo puedes registrarte rápidamente usando tu cuenta de Twitter, GitHub o Facebook. Una vez que estés dentro, en la esquina superior izquierda, vas a ver “Crear Pen”, y esto nos está llevando al editor web. Entonces este es tu editor aquí, puedes editar HTML, CSS, y JavaScript y lo genial de ello es que una vez edites algo, entonces el resultado aparecerá en la pantalla de resultados de inmediato. Si agregas algún encabezado y párrafo principal simple, entonces vas a ver el resultado en el panel de resultados. Vamos a usar React and React no es parte del JavaScript estándar. Se trata de una biblioteca externa. Por suerte para nosotros, en CodePen, puedes agregar fácilmente bibliotecas externas a un proyecto. Vaya a Configuración, seleccione el panel JavaScript y busque React. Una vez que hayas agregado React, también
tenemos que añadir algo llamado ReactDom y aquí tienes que tener cuidado que el número de versión de React y el número de versión de ReactDom tienen que coincidir. Si ese no es el caso por algunas razones, entonces echa un vistazo al comentario debajo de este video. Tenemos que poner una cosa más aquí. Tenemos que establecer un Preprocesador JavaScript y este va a ser Babel en nuestro caso. En cuanto a qué es Babel, ¿por qué lo necesitamos y qué es ReactDom. Se lo voy a explicar sólo en un poco pero por ahora, vamos a guardar esto y estamos listos para irnos. Escribamos algo de JavaScript y empecemos por crear nuestro primer componente. Y esto puede ser sorprendente, pero un componente es básicamente una función. Cuando estaba diciendo antes que la forma en que se desglosa una aplicación a un componente es la misma que descomponer una lógica de aplicación en funciones. Bueno, los componentes están más cerca de las funciones de lo que podrías haber pensado. Un componente es una función especial la cual está devolviendo algo que realmente se parece a HTML. Pero esto aún no es HTML, todavía
estamos dentro de JavaScript y tal vez te estés preguntando ¿cómo funciona esto? Es decir, esto no es JavaScript. No se pueden agregar etiquetas HTML en JavaScript, y la respuesta a esta pregunta es que de hecho, aquí no
estamos usando JavaScript. Esto no es JavaScript. Al menos no JavaScript puro. Recuerda configuramos a Babel como preprocesador para JavaScript. Babel está transpilando este código en JavaScript adecuado. En el fondo estamos teniendo JavaScript adecuado, pero estamos editando una versión ligeramente modificada de JavaScript, lo que nos permite escribir etiquetas HTML. O al menos algo que realmente se parece a las etiquetas HTML. Tenemos un componente React, pero en la pantalla de resultados, todavía no lo vemos. Esto es como tener una función a la que nunca se ha llamado. Estamos teniendo una función que está generando una pieza de HTML, pero no decimos a dónde debería
pertenecer este HTML generado y no se agrega automáticamente a la raíz del HTML. Tenemos que definir un punto de entrada en HTML. Solo agreguemos un div simple, con una ID, y voy a usar “app” como ID, pero solo puedes escribir cualquier otro nombre si quieres. En JavaScript vamos a utilizar ReactDom. ReactDom es una biblioteca de utilidades realmente simple y su único propósito es buscar el HTML, encontrar este ID, y reemplazar su contenido por nuestro componente. Si has hecho todo bien, entonces finalmente, tenemos un componente React dentro de nuestro sitio web y ese es un comienzo realmente genial. Antes de pasar a la siguiente sección, vamos a añadir rápidamente el esqueleto de los demás componentes en los que acordamos. Vamos a añadir un componente Square, un componente Cross,
y el componente Circle más los componentes de la pantalla de resultados. Estos componentes por ahora van a ser componentes muy simples. Simplemente están devolviendo una pieza de texto y no se van a conectar con nuestros componentes raíz. Simplemente se están quedando en el espacio exterior y no hacen nada en absoluto, pero este es nuestro esqueleto de aplicación. Vamos a conectar estos componentes en nuestras próximas secciones.
4. La cuadrícula: En nuestras lecciones anteriores, escribimos nuestros primeros componentes React. Pero en nuestra pantalla de resultados, todavía sólo vemos “Hola de React” en la esquina superior izquierda, y ni siquiera estamos usando la mayoría de nuestros componentes React en absoluto. En esta lección, vamos a hablar mucho sobre CSS, cómo agregar estilo estos componentes,
y cómo conectarlos, cómo incrustar el componente Square dentro del componente TicTacToe. En primer lugar, vamos a centrar todo. Ahora mismo, por defecto, todo está en la esquina superior izquierda. Y esto es un juego, este no es un sitio web normal, queremos centrar todo. Para centrar todo vamos a nuestra etiqueta de más alto nivel y definimos algunas propiedades CSS para ello. El nivel más alto en este momento es nuestro div dentro del HTML, que es el punto de entrada de la aplicación. Ya tiene un ID, así que solo usemos eso dentro de CSS, y para centrar las cosas, vamos a usar CSS Flexbox. Si no has usado CSS Flexbox antes, es realmente bueno para centrar las cosas y alinear las cosas de manera uniforme. En este momento sólo lo vamos a usar para centrar, y tiene algunos nombres de propiedad realmente raros como justify-content y align-items. No es tan intuitivo que uno sea para alinear las cosas horizontalmente y uno para alinear las cosas verticalmente, pero funciona realmente bien. Una vez que lo agregamos, todo debería estar en medio de la pantalla. Bueno, todo ahora mismo es solo un texto que dice, “Hola de React”, porque eso es todo lo que está produciendo el componente TicTacToe. Cambiemos eso. Sustituyamos este texto inicial por nueve instancias del componente Cuadrado. Podría estar preguntándose cómo hacerlo porque, componente
TicTacToe está produciendo una pieza de HTML, componente
Square también está produciendo una pieza de HTML, pero el componente Square en sí es solo una función. Entonces, ¿hacemos llamadas a funciones, y llamamos a los componentes Square como una función? En React, se puede hacer referencia a componentes, como si fueran etiquetas HTML personalizadas. Lo cual es realmente útil porque se
necesita el componente Square dentro de la declaración de retorno de un componente TicTacToe. Ahora tenemos nueve cuadrados en el componente TicTacToe, pero no aparecen como cuadrícula. Para solucionar eso, vamos a asignar alguna clase CSS para el contenedor principal del componente TicTacToe. Y esto es algo que es ligeramente diferente al HTML. Como dije antes, esto no es HTML en realidad, sólo
se ve así. Esto se llama JSX, JavaScript XML, y tiene algunas diferencias menores en comparación con HTML. Una de ellas es que no se puede usar la palabra clave class, porque la palabra clave class está reservada en JavaScript y tiene algunos significados muy diferentes. En lugar de escribir clase, escribimos ClassName. Para alinear el contenido en una cuadrícula, vamos a usar la cuadrícula CSS. Si no has usado la grilla CSS antes o CSS Flexbox, hay un enlace debajo de este video el cual está explicando muy bien cómo funcionan. Entonces con la grilla CSS, estamos definiendo, que queremos tener tres columnas y tres filas, y queremos tener algún hueco entre ellas. Ahora se ve un poco mejor, pero las plazas no se ven en absoluto como plazas. Del mismo modo que adjuntamos una clase para el componente TicTacToe, adjuntemos una clase cuadrada para el componente Square, y añadamos algo de CSS. Aquí estamos arreglando el ancho, y la altura del componente, por lo que se verá como un cuadrado, y también estamos centrando el contenido del cuadrado de la misma manera centramos toda la aplicación, que CSS Flexbox. Ahora tenemos nuestras nueve plazas en una cuadrícula, pero queremos tener alguna frontera entre ellas. Desafortunadamente, con la cuadrícula CSS, no
podemos establecer el color del borde. vamos a usar un pequeño truco. En lugar de configurar el color del borde, estamos configurando el color de fondo de toda la cuadrícula, luego configurando el color de
fondo de los componentes Cuadrados en todo el color de fondo de la aplicación. De esta manera, parece que establecimos el color del borde de la cuadrícula, pesar de que en realidad no lo hicimos.
5. ¿Qué Cuadrado es qué?: Tenemos nuestra cuadrícula de aspecto bonito, pero todas las plazas dentro lucen igual. Sólo para aclarar, qué cuadrado es cuál, démosles una identificación única. En React, puede configurar instancias de componentes pasando los apoyos a ellos. A utilería es algo así como un atributo a una función. Vamos a decir que es exactamente un atributo para nuestra función componente. Vamos a establecer un atributo de posición para cada una de nuestras instancias de componentes. En React, puedes pasar una propiedad de la
misma manera que establecerías un atributo para una etiqueta HTML. Podemos escribir posición igual y pasamos “0". Aquí vamos a usar índice basado en cero, porque más adelante, cuando especifiquemos algún valor adecuado para los cuadrados, también
vamos a usar índice basado en cero. También podemos pasar un valor de una manera un poco diferente. En lugar de usar comillas dobles aquí, también
podemos usar corchetes rizados. El uso de corchetes rizados aquí, se llama encuadernación. Es una forma mucho más poderosa de pasar algunas propiedades para los componentes secundarios. Con corchetes rizados, básicamente estamos abriendo una Ventana para JavaScript. Inicialmente, estamos en tierra JavaScript, pero luego con JSX estamos empezando a escribir algo de HTML. Pero, ¿y si quieres volver a JavaScript dentro de HTML? Entonces usamos el enlace, con corchetes rizados estamos abriendo una Ventana a JavaScript, y lo que sea que esté dentro de estos corchetes rizados se evalúa como una expresión JavaScript y el resultado de eso se pasará como una propiedad. En este momento no estamos usando todo su poder porque sólo estamos pasando un número. Pero también podríamos usar un poco de cálculo matemático aquí, podríamos llamar a una función aquí, tener una expresión ternaria... Cualquier cosa que sea una expresión de JavaScript válida, podemos usar aquí. También hay una diferencia menor aquí entre pasar un valor entre corchetes rizados y pasar un valor entre comillas dobles. Si pasas un número entre comillas dobles, será un texto, se interpretará como una cadena. Por otro lado, si estás pasando un número entre corchetes rizados, se va a quedar un número. Vayamos al otro lado de las cosas y usemos esta propiedad pasada en el componente Square. Como dije, las propiedades en los componentes React se transmiten como un atributo para la función de componente. Pero en lugar de que el primer atributo de esta función simplemente se convierta en posición, el primer atributo seremos un objeto genérico para todas las propiedades del componente. Esto puede ser confuso para los componentes que tienen una sola propiedad. Ten en cuenta que el primer atributo todavía va a ser un objeto, cual tiene un atributo en este momento, la posición. Aquí podemos atrapar todo el objeto props y referirnos al atributo de posición del objeto props, o podemos descomponer este objeto props de inmediato en la firma de función. Tenemos este atributo de posición ahora, pasado. ¿ Cómo lo usamos? Básicamente lo que queremos lograr es en lugar de mostrar “Cuadrado”, queremos mostrar el número de posición dentro de este componente. ¿ Qué podemos hacer en este caso? Queremos utilizar una variable JavaScript dentro de JSX. Podemos hacer lo mismo otra vez, lo que acabamos de hacer antes con pasar una variable como propiedad. Podemos usar encuadernación. Nuevamente, estamos usando corchetes rizados y dentro de corchetes rizados sólo usamos la variable de posición. Después de hacer todo esto, deberíamos ver una identificación única dentro de cada cuadrado, dentro de nuestra cuadrícula.
6. Las partes de cambio: En nuestra lección anterior, logramos pasar un ID único por cada plaza pero dentro las plazas más bien queremos tener círculos sobre cruces basados en el progreso del juego. Esto es algo que va a ser dinámico. En un principio, cada plaza va a estar vacía y una vez que haga clic en ellos deben mostrar un círculo o una cruz. En React todo lo que es dinámico, todo lo que está cambiando con el tiempo debe definirse en un estado. El estado junto con los apoyos son los dos factores principales que dan forma a un componente, pero mientras los apoyos están siendo transmitidos por los padres por lo que salen fuera del componente, el estado es una cosa interna. ¿ Qué está cambiando aquí? dos cosas principales que están cambiando a lo largo del juego. En cada turno, un cuadrado obtiene un nuevo valor. Se pone un círculo o una cruz. El estado de las plazas es definitivamente algo de lo que necesitamos hacer un seguimiento. La otra cosa que está cambiando sobre el juego es el jugador. Después de cada giro está cambiando de círculo a cruz o de cruz a círculo. Estas van a ser las dos partes principales de nuestro estado. Ahora debemos discutir dónde definir el estado. En cuanto al jugador, tiene sentido definirlo en el nivel superior porque no tiene nada que ver con cuadrados o círculos o cruces. Es el estado de todo el juego. Pero en cuanto al estado de las plazas, podría sonar lógico definirlas en las casillas. Cada plaza sería responsable de su propio estado interno. Sabrían si deberían estar vacíos o si deberían contener una cruz o un círculo. Pero hay un tema arquitectónico importante con este. Las plazas conocerán su propio estado interno pero nadie conocería todo el estado de toda la grilla. Eso es por cómo funciona la comunicación entre componentes en React. Reaccionar funciona de arriba a abajo. Un componente de nivel superior, como el componente TicTacToe, puede transmitir propiedades a los componentes de nivel secundario como el componente Square pero no funciona al revés. El componente Cuadrado, los niños no pueden transmitir propiedades a sus padres. Desde la perspectiva secundaria, el componente Square puede tener acceso al estado del componente TicTacToe porque el componente TicTacToe puede pasarlo como propiedad pero no está funcionando al revés. El componente TicTacToe no puede tener acceso
al estado del componente Square porque el componente Square simplemente no puede pasarlo. Vamos a ver que aquí hay una solucion. Podemos usar callbacks para reportar de nuevo cierto progreso del niño al padre pero eso es un poco diferente. Con utilería, estás pasando por una propiedad. Estás exponiendo algunas de tus variables a tus hijos. Pero con callbacks, son llamadas de función. Son más bien como un evento. No es como compartir tus variables. Una vez que nos dimos cuenta de cómo funciona la comunicación entre componentes y cómo el estado y los apoyos funcionan juntos, probablemente
podemos estar de acuerdo en que si necesitas cierta lógica, que es detectar si el juego ha terminado y te dice qué jugador ganó el juego, entonces necesitamos tener el estado de cada plaza en un solo lugar común. Este va a ser un nivel más alto en el componente TicTacToe. Ahora estamos de acuerdo en que vamos a tener todo nuestro estado en el componente de alto nivel y este es un patrón bastante común. Por lo general, los componentes de primer nivel son los inteligentes. Por lo general tienen la lógica y el estado pero no son tan visuales mientras que por otro lado los componentes de nivel inferior suelen recibir todos sus valores verdaderos apoyos y son los que están teniendo que representaciones
visuales del aplicación pero el estado en general no siempre tiene que estar en el nivel superior. Tiene que ser de hecho lo más bajo posible pero aún lo más alto que sea necesario. Tengamos otro ejemplo para esto. Digamos que eres realmente bueno en el desarrollo de juegos y creaste un sitio web lleno de juegos. Contendrá una aplicación TicTacToe, contendrá un Tetris o Mine Sweeper etc. En este caso, si estás teniendo el componente de nivel superior, que es toda la página, no tiene que conocer el estado interno de la TicTacToe juego. Todavía podemos tener estados de nivel inferior individualmente para cada juego y en el nivel superior, tal vez no haya estado en absoluto.
7. Primer intento de añadir un estado: En la lección anterior, tuvimos mucha teoría respecto a cómo funciona el estado y cómo los componentes de la aplicación se pueden conectar con los apoyos. Empecemos a definir nuestro estado. Como acordamos. Pasemos al componente Tic Tac Toe y definamos un estado para el jugador y para las posiciones. Las posiciones por ahora, va a ser una matriz que contendrá el valor de cada cuadrado en la cuadrícula. Pero antes de definir los estados, vamos a tener sólo algunas constantes para los posibles valores de los cuadrados. Definamos los estados. Por ahora, definamos los estados como variables simples. Posteriormente, vamos a ver que los estados tienen que ser algo especial, algo más que una simple variable. Pero por ahora, vamos a mantenerlo simple. Una vez que definamos el estado de posición que está conteniendo dos valores de cada cuadrado, dejemos pasar este valor para cada cuadrado. Simplemente lo hacemos de la misma manera que estamos
pasando unos atributos de posición para el componente. Estamos agregando un nuevo prop aquí. Sólo llamémoslo valor, que puede contener círculo vacío o cruz. Tan solo para que este ejemplo sea un poco más interesante, en lugar de tener una cuadrícula vacía como estado inicial, solo tengamos algunas cruces y círculos aleatorios en este estado. De acuerdo, entonces ahora estamos pasando un valor por cada cuadrado, pero por supuesto, también tenemos que cambiar el componente Cuadrado para reflejar estos valores. El componente Cuadrado debe contener un círculo o un componente transversal dependiendo de este valor. Para lograrlo, vamos a utilizar una expresión JavaScript. Esto podría ser un poco confuso al principio, pero esto es simplemente una operación booleana. Si la primera parte antes del && es falsa, entonces no se evalúa la segunda parte de esta expresión. Se devuelve sencillamente falso. Pero si la primera parte es cierta, entonces la segunda parte será el resultado de esta expresión. En este caso eso sería círculo o cruz. Por suerte para nosotros, si una expresión es falsa, entonces reacciona con simplemente ignorarla. En esta configuración, sólo debe aparecer un círculo o una cruz. Nunca ambos y si el valor es inicial, entonces este cuadrado debe permanecer vacío. Ahora en nuestra cuadrícula, ya deberíamos ver algunos círculos y cruces
dependiendo de nuestro estado inicial o al menos el texto de posicionador para ello. Pero antes de que cambiemos el texto del Marcador de posición en círculos y cruces reales, detengámonos por un segundo aquí, y pensemos cómo funcionará esta aplicación.
8. Un estado que puede cambiar: Antes de seguir adelante, detengámonos un poco y veamos cómo va a funcionar esta aplicación. Por el momento, tenemos nuestra función que está generando nuestro estado inicial. Después del renderizado inicial, el estado del juego cambiará en base a nuestra interacción. Por supuesto, queremos reflejar estos cambios en el diseño. El modo en que React está lidiando con esto es volviendo a renderizar todo
el componente y volverá a renderizar todo el componente ejecutando la misma función. Esta es una situación graciosa aquí, porque estamos ejecutando la misma función dos veces y estamos esperando un resultado diferente. Me gusta resaltar aquí que esta es solo una función JavaScript normal aparte de devolver algo que se parece a HTML, aquí no
hay nada mágico. Si solo miras esta función, está bastante claro que si estás teniendo el estado en este formato, esta función va a generar el mismo resultado sin importar cuántas veces lo ejecutemos. ¿ Qué podemos hacer para arreglar eso? Necesitamos un estado que pueda devolver el último valor que le asignamos. Por lo tanto, necesitamos almacenarlo fuera de este componente. En React con el fin de resolver este problema introdujeron React Hooks. Uno de los Hooks se llama USEstate y es exactamente para este problema. Nosotros el gancho USEstate, podemos almacenar un estado fuera
del componente y siempre nos devolverá el último valor. La primera vez que estamos ejecutando esta función, podemos inicializar el estado con un valor que estamos pasando como atributo. Este atributo será, por supuesto, ignorado en todas las próximas llamadas una vez que el valor ya esté almacenado. El gancho USestate devuelve una matriz de dos artículos. El primer elemento es el propio estado. Si solo ejecutas la función por primera vez, entonces básicamente devuelve nuestro estado inicial. Pero si está ejecutando la función debido a un re-renderizado, entonces devolverá el valor más reciente del estado. El segundo ítem que se
nos devuelve el gancho USestate es una función y esta función se puede utilizar para cambiar el estado. En el futuro para cualquier interacción, si necesitas cambiar el estado, necesitamos llamar a esta función y no solo llamamos a esta función para cambiar el estado, sino que también está activando automáticamente el re-renderizado del componente. Entonces al cambiar el estado, también
volvemos a renderizar el componente, que por supuesto reflejamos el cambio de estado donde acabamos de tener.
9. Círculos y Crosses: Esta lección va a ser divertida, y es un huevo de Pascua porque no tiene que hacer nada que reaccione. En este, vamos a codificar imágenes. Vamos a dibujar círculos y cruces basados en vectores. Si alguna vez revisas el código fuente de una imagen, entonces probablemente te das cuenta de que son súper complicadas y simplemente no puedes sacar nada de ella. Pero vamos a usar SVG, y los SVG no son tan complicados, sobre todo si solo lo vas a usar para algo simple. Lo bonito de los SVG es que están basados en XML, y desde HTML5, son parte de la sintaxis HTML. Simplemente puedes inline un SVG dentro de HTML. No tienes que usar una imagen externa y no tienes que cargarla en el HTML. Simplemente puedes escribirlo como parte de tu HTML. ¿ Cómo se ven? En primer lugar, cada SVG se envuelve en una etiqueta SVG. En la etiqueta SVG, se puede definir el ancho y la altura del SVG junto con el ViewBox. El ViewBox está definiendo su sistema de coordenadas. En nuestro caso, vamos a definir que el SVG arranca en la esquina superior izquierda con la coordenada -50 -50, y el ancho y la altura del SVG Canvas serán de 100 cada uno. Por lo que el centro de la imagen será cero, cero. Se podría notar que la altura y el ancho que definimos en SVG y la altura y el ancho que definimos en el ViewBox son los mismos pero esto no es algo que sea obligatorio. El alto y el ancho definido como atributos en la etiqueta SVG van a ser el tamaño real del SVG. Si bien el ViewBox está definiendo el lienzo en el que podemos dibujar. Todas las coordenadas dentro de este SVG se relacionarán con este lienzo. El ViewBox está definiendo un tamaño interno desde la perspectiva de los elementos de la imagen. Si el tamaño real de la imagen y el tamaño definido por el ViewBox no coinciden, entonces la imagen simplemente se escalará hacia abajo o se escalará hacia arriba. Ahora tenemos un lienzo. Dejemos simplemente dibujar algo en él. Empecemos con un componente Círculo porque eso es sólo un círculo. Simplemente podemos agregar una etiqueta de círculo y decir que el centro de la misma estará en el centro del lienzo y el radio será de 40 píxeles. Te darás cuenta de que esto renderizará un círculo negro. Eso no es exactamente lo que queremos pero como los SVG son parte de HTML, también
podemos usar CSS para darle estilo. Antes del estilo, solo definamos también el componente Cross. Simplemente definimos dos líneas estableciendo sus coordenadas de inicio y sus coordenadas finales. Ahora si te preguntas, ¿qué hiciste mal porque eres cruces no apareciendo, eso es completamente normal. Por defecto, el ancho de línea es cero. Tenemos que cambiar eso en CSS. Definamos algún estilo para SVG en general. Aquí podemos establecer un ancho de línea, que se llama trazo ancho. También podemos cambiar el color estableciendo la propiedad de trazo. También eliminemos el relleno, que es automáticamente negro en caso del círculo. Digamos que llene: ninguno. Si quieres ser elegante, también
podemos cambiar la propiedad stroke-linecap, que hará que nuestras terminaciones de línea sean redondeadas. Si quieres tener diferentes colores para los círculos y las cruces, entonces como estamos en HTML ahora, simplemente
puedes adjuntar una clase CSS para la cruz o el círculo, luego establecer un color diferente para ellos basado en la clase CSS. Ahora tenemos estos círculos y cruces guapos. Para terminar este juego, sigamos adelante con una de las partes más esenciales, que es la interacción.
10. Interacción: Está bien, luciendo bien, pero hasta ahora todo sigue estático y este es un juego por lo que quieres agregar alguna interacción, por lo que deberíamos adjuntar un manejador de eventos para las plazas. Pero desafortunadamente en React, no
podemos adjuntar manejadores de eventos en niveles de componentes. Tenemos que adjuntar manejadores de eventos para las etiquetas HTML reales. En nuestro caso, lo más cercano que podemos conseguir es la etiqueta de contenedor dentro de un componente Square. Pero aquí hay un poco de problema, coincidimos en que vamos a tener el estado dentro de un componente TicTacToe, pero este manejador de eventos ahora está en el componente Square. Tenemos que encontrar una manera agradable de que el componente Cuadrado, pueda establecer el estado. Una idea podría ser que junto con pasar el estado al componente Square, también
pudiéramos pasar la función SetState, y de esta manera, el componente Square podría cambiar el estado en sí. Pero junto con cambiar el estado de la grilla, también
queremos cambiar el estado del jugador. Por supuesto, también podríamos pasar al jugador al componente Square
y el componente Square podría cambiar todo el estado porque lo sabrá todo. Pero esto va en contra de la idea de tener el componente Square lo más simple posible, y si lo piensas, el jugador no tiene nada que ver realmente con el componente Square. El jugador pertenece a la lógica principal la cual debe definirse en el componente TicTacToe. En lugar de pasarlo todo, lo que vamos a hacer es pasar una devolución de llamada. Una devolución de llamada es cuando estás pasando una función, entonces estás esperando que la función sea llamada en ciertos eventos. En nuestro caso, el componente TicTacToe pasará una función y el componente Cuadrado llamaremos a esta función una vez que se haya hecho clic. El bonito de esto es que la función que estamos pasando está definida en el componente TicTacToe, por lo que tiene acceso completo a todo dentro del componente TicTacToe junto con el estado, y el jugador y mientras un componente Cuadrado tiene acceso a ninguno de estos, todavía puede llamar a la función. En el componente TicTacToe, escribamos una función que reciba en qué cuadrado se ha hecho clic, y cambia el estado actualizando la cuadrícula y cambiando el jugador. Entonces pasemos esta función a cada una de las plazas, como propiedad. Es importante señalar aquí que aquí no estamos llamando a la función. Simplemente estamos pasando una referencia a la función, igual que podríamos hacer con cualquier otra variable. El único lugar donde vamos a llamar a esta función será dentro del componente Plaza, que va a tener acceso a ella porque simplemente lo pasamos como propiedad. Dentro del componente Square, podemos definir un manejador de eventos para el evento click, y este manejador de eventos requiere una función. Será tentador usar la función que acaba de pasar desde
el componente TicTacToe, pero no es tan fácil. La función transmitida por el componente TicTacToe requiere un atributo de posición. Lo que podemos hacer es definir un manejador de eventos intermedio en el componente Square, que se llama una vez que se hace clic en el cuadrado, luego se transferirá para llamar a nuestra devolución de llamada. En este manejador de clics intermedio, podemos pasar fácilmente el
atributo de posición porque el componente Cuadrado conoce la posición. También podemos agregar alguna lógica extra aquí, por ejemplo, si el cuadrado no está vacío, entonces vamos a ignorar el click. Vaya, así que hemos ido muy lejos, quiero decir, ahora en realidad puedes jugar este juego. Es interactivo, se ve bien. Todo está en su lugar excepto un último bit, anunciando al ganador.
11. Detección del ganador: Estamos realmente cerca de terminar este juego ahora, pero todavía no sabemos si el juego ha terminado, si alguien ganó el juego o es empate. Para hacer esto, escribamos una función de utilidad, que va a ser una función simple de JavaScript, y luego veamos cómo esta función de JavaScript simple está encajando en su lógica de aplicación. Para poder hacer este cálculo, esta función necesitaría el estado del juego, necesitaría el valor de cada cuadrado. Pero aparte de eso, sería una función simple de JavaScript, no
es nada Reaccionar específico, solo una función que recibe una matriz de nueve valores y devuelve un resultado. Por lo que detectar si alguien ganó el juego es comprobar si hay tres círculos o tres cruces en una fila, en una columna, o de manera diagonal. Básicamente, tenemos que revisar los ítems específicos en esta matriz de nueve ítems. Si quieres comprobar si la primera fila está llena de círculos, entonces vamos a comprobar si el primer, segundo y tercer elemento de esta matriz es un círculo. Si voy a esta la segunda línea, la segunda fila, entonces vamos a comprobar si el cuarto, quinto y sexto ítem de esta matriz es un círculo y así sucesivamente. Es un cheque súper simple. Es básicamente algunas condiciones si, si estos valores son todos círculos, entonces la función devuelve que el ganador es círculo. Si estos valores son todos cruces, entonces regresará ese ganador es una cruz. Si la función comprobó que nadie ganó el juego, entonces todavía hay dos opciones. El juego o ha terminado y es empate, o el juego sigue en marcha. Detectar un empate en este punto es simplemente comprobar si todos los cuadrados ya tienen un valor, por lo que no hay posible más movimiento. En este caso, devolvamos que “Es un empate”. La única opción que queda es que nadie ganó el juego, no
es empate por lo que el juego sigue en marcha. En este caso, simplemente no devolvemos nada en absoluto. OK, entonces tenemos esta función y esta es una función de utilidad, así que probablemente podamos tenerla en algún lugar fuera de los componentes React, pero también podemos tenerla en el componente TicTacToe porque el componente TicTacToe está manejando el principal lógica y es el único lugar donde se necesita esta función, para puedas decidir eso. ¿ Cómo encaja esto en nuestra aplicación? El ciclo de vida de estas aplicaciones Tic-Tac-Toe hasta ahora es que estamos renderizando todo como un estado inicial, luego una vez que estás haciendo clic en una de las casillas, entonces todo se vuelve a renderizar. Esto continúa en un bucle hasta que sigues haciendo clic en cuadrados. Detectar al ganador podría venir después de cada clic, por lo que podría ser parte del manejador de eventos después de hacer clic en un cuadrado. En este caso, podríamos tener un estado extra para el ganador. Podríamos almacenar al ganador en un estado, luego en base a eso, podríamos renderizar una pantalla de resultados. La otra opción viene al darse cuenta de que el ganador es un estado derivado. Viene en base a los estados ya existentes, por lo que no tenemos que tener una parte separada del estado para almacenar algo que ya podemos descifrar del resto del estado. Pero si no lo estamos almacenando en el estado, entonces ¿dónde lo tenemos? Necesitamos saber si alguien ganó el juego al renderizar, porque en ese caso, vamos a renderizar el componente Resultado. Por lo que simplemente podemos agregar alguna lógica extra entre obtener el estado del gancho React, que está conteniendo el valor de los cuadrados y renderizando la cuadrícula en base a este estado. Cualquier cosa que escribas en esta función, como
discutimos, nos volveremos a ejecutar con cada render. Cuando estés re-renderizando este componente, detectará si alguien ganó el juego y luego puede tomar una decisión para renderizar la pantalla de resultados en base a eso. Por lo que estas son dos opciones y siéntete libre de ir con lo que prefieras. Al final, ambos tendrán una variable ganadora la cual
contendrá si el círculo o la cruz ganó el juego, o si es empate o no contendrá nada y eso indicará que el juego sigue encendido.
12. Y el ganador es...: En este último capítulo, vamos a crear nuestra pantalla de resultados. Anteriormente, ya logramos derivar nuestro estado en una variable ganadora, cual contendrá, si el juego ha sido ganado por
el círculo, la cruz, o nadie ganó el juego y el juego sigue encendido. Llegará la pantalla de resultados al componente TicTacToe porque el componente TicTacToe es el que está ensamblando la lógica principal del juego. En el diseño, el resultado aparecería encima de la cuadrícula pero técnicamente en HTML, va a estar junto a ella porque la cuadrícula ya tiene algún CSS distinto, que es específico para la cuadrícula, y no queremos tener la pantalla de resultados dentro de eso porque la pantalla de resultados se vería algo completamente diferente. Pero ahora tenemos dos etiquetas HTML dentro de los resultados de los componentes de TicTacToe. Y en React, no
podemos tener eso. Reaccionar siempre tiene que devolver una etiqueta HTML como resultado. Pero podemos solucionar esto fácilmente porque esa etiqueta de resultado puede tener un contenido y ese contenido puede tener múltiples etiquetas HTML. Es justo lo mismo que nuestra cuadrícula anterior está conteniendo nueve cuadrados. Simplemente agregaré una etiqueta envolvente como contenedor para el componente Tic Tac Toe, que tendría tanto la cuadrícula como la pantalla de resultados. Pero ahora tanto la pantalla de resultados como la grilla está apareciendo siempre y sólo
queremos tener la pantalla de resultados en caso de que alguien ganara el juego o si es empate. Queremos renderizar esta pantalla de resultados condicionalmente en base al atributo ganador. Podemos hacer el mismo truco que acabamos de hacer en el componente Cuadrado cuando
renderizamos un círculo o una cruz basado en el valor del cuadrado. Ahora basado en el valor del ganador, vamos a renderizar el resultado. Como acordamos, si el ganador está vacío, eso significa que el juego sigue encendido. Esto también significaría que si estás usando esta condición, que si el juego sigue encendido, entonces la pantalla de resultados no aparecerá. Logramos ocultar la pantalla de resultados durante el juego. También queremos hacerlo lo suficientemente inteligente como para saber quién ganó el juego porque en este momento simplemente dice resultado, no dice cuál es el resultado. Para ello, necesitamos pasar el atributo ganador a este componente la
misma manera en que pasamos atributos a las casillas. Entonces del otro lado en el componente de resultados, necesitamos atrapar esta variable y podemos tener algún renderizado condicional basado en su valor. Por defecto, esto aparecería debajo de la cuadrícula. Pero si quieres tenerlo en la parte superior de la cuadrícula, entonces podemos agregar algo de estilo CSS y con eso, podemos ponerlo posición absoluta y establecer algunas otras propiedades para que sea un poco más agradable. Una vez que hayas terminado el juego, se ha anunciado al ganador, ¿qué sigue? Si quieres jugar otra ronda, entonces tienes que refrescar la página. En lugar de eso, también podemos definir una función de reinicio que restablecería nuestro estado. Porque estamos restableciendo nuestro estado, tenemos que definir esto en el componente TicTacToe porque ese es el que está teniendo acceso al estado. Pero después de eso, podemos pasar esta función de reinicio a nuestro componente de pantalla de resultados. Esta va a ser otra devolución de llamada, justo lo mismo que pasábamos en una devolución de llamada al componente Square, que se utilizó una vez que se hace clic en el cuadrado. Este se utilizará si hace clic en el botón de reinicio en la pantalla de resultados. Simplemente pasaré esta función como propiedad y dentro del componente de pantalla de resultados, si define un botón para restablecer, entonces simplemente podemos pasar esta función al manejador de eventos OnClick de este botón, sin ningún evento de nivel medio manejadores en el medio. La diferencia entre éste y el del componente Square es que el del componente Square necesitaba alguna propiedad extra para pasarse a la función de componentes TicTacToe. Pero esta, las funciones de restablecimiento no necesitan ninguna propiedad en absoluto, por lo que se puede llamar simplemente, se puede vincular simplemente al manejador de eventos de este botón. Y eso es todo lo que es. Ahora tenemos un juego por el que puedes jugarlo, aparecerá
la pantalla de resultados y una vez que hayas terminado, podrás reiniciar el juego y jugar otra ronda.
13. Conclusión: Enhorabuena. Ahora tengo un juego de
Tic-Tac-Toe, y ojalá también tengas una comprensión mucho mejor de cómo funciona React. Pero si todavía tienes algunas preguntas o algunas partes siguen siendo confusas para ti, entonces siéntete libre de contactarme aquí o en CodePen. Por supuesto, este juego apenas está terminado. Podríamos tener mucho más,
pero eso habría hecho un tutorial mucho más largo. En lugar de eso, puedes encontrar un enlace debajo de este video el cual está explicando algunas características adicionales como cómo agregarle animación, o cómo definir una lógica informática y jugar contra la computadora. Siéntete libre de seguir adelante con este proyecto, y una vez que hayas terminado no olvides compartirlo. No puedo esperar a verlo.