This graph shows how many times the word ______ has been mentioned throughout the history of the program.
Muy bien, pues nada, si podéis hacer que las preguntas se ciñan a las partes del curso, mejor, ¿vale?
Si no, las intentaré ignorar, porque si no, vamos a saltar de una cosa a otra y es un poco raro, ¿vale?
Así que lo vamos a intentar evitar.
Muy bien, ¿vamos a guardar en Local Storage?
Lo acabo de decir Rafael, hace un momento he explicado que una de las cosas que vamos a hacer es guardar la partida en Local Storage.
Ok, pues nosotros vamos a utilizar VIT para crear nuevos proyectos.
VIT es un empaquetador de aplicaciones web, una alternativa a Webpack muy chula, muy rápida, que está genial, ¿vale?
Entonces, vamos a hacer aquí un npm create bit arroba latest, para que utilice la última versión.
Lo ejecutamos, le decimos el nombre proyecto.
Le voy a poner aquí 02 y aquí podríamos poner, bueno, nuestro juego, ¿no?
Que va a ser el tic-tac-toe, tic-tac-toe.
Esto es el 3 en, no sé cómo lo llamáis en Latinoamérica, si 3 en raya o 3 en línea, no sé.
Pero el tic-tac-toe básicamente es el 3 en raya en España, como lo llamamos de toda la vida.
Vale, aquí le vamos a decir que seleccionamos React y vamos a decirle que lo hacemos con JavaScript más SWC.
SWC es como un babel para compilar el JavaScript, pero mucho más rápido.
En clases posteriores vamos a ver TypeScript, ¿vale?
Y haremos proyectos con TypeScript, así que no te preocupes.
Si vas a, porque seguro que estás preguntando ya en el chat.
Y vamos a hacerlo con TypeScript, lo haremos más adelante.
Ahora nos vamos a enfocar con JavaScript a entender React y poco a poco iremos haciendo más cosas, ¿vale?
Así que ahora con JavaScript.
Muy bien, y ahora vamos a 02 tic-tac-toe, vamos a hacer el npm install que nos está pidiendo por aquí
y levantaremos nuestro proyecto con npm run dev.
Ok, muy bien, npm run dev.
Vamos a tener por aquí el proyecto y ya nos ha levantado aquí en el puerto 5173.
Vamos a hacerlo esto más pequeñito, así que lo vamos a tirar para acá.
Más que nada, para que tengamos juntitos estas cositas, ¿vale?
Así podemos ver mientras programamos, vamos a poder ver lo que vamos haciendo, ¿no?
Vamos al proyecto, al 2.
Ya nos ha creado aquí todos los ficheros, como todo el boilerplate que necesitamos
para que tengamos una aplicación de React.
Y aquí ya tenemos una aplicación de React con un contador que le podemos dar
y empieza a incrementar, ¿vale?
Perfecto.
En el source tenemos el main, que es el punto de entrada de nuestra aplicación.
Este concepto es súper importante y no solo de RIA, que es de la vida en general, ¿vale?
El punto de entrada de nuestra aplicación.
Digamos que es desde donde se construye nuestra aplicación, ¿vale?
Es el primer archivo que se carga para cargar nuestra aplicación.
O sea, esto es lo primero.
A partir de aquí tenemos que ir importando todo el resto, ¿vale?
Muy bien, ¿qué más tenemos por aquí?
Bueno, tenemos un componente app, que aquí podemos ver, que tiene un montón de cositas.
Perfecto.
Y este app es el que vamos a eliminar.
Si yo elimino todo esto y pongo return, vamos a poner tic-tac-toe y guardo los cambios,
pues ya tengo aquí mi tic-tac-toe.
Ya hemos terminado.
No, no, no.
Es broma, es broma.
Pero sí que voy a quitar por algo que necesitamos.
Luego lo iremos añadiendo conforme lo necesitamos, ¿vale?
Para que poco a poco, pues, vayamos increchendo, ¿ok?
Oye, muchas gracias por la ride, Matías.
Muchísimas gracias.
Estamos aquí con un curso desde cero de React y vamos a empezar aquí con un proyecto práctico
donde vamos a hacer un 3 en raya, un tic-tac-toe, ¿vale?
Y vamos a ver un montón de cosas interesantes.
Manejar estado, lógica de programación, renderizado condicional, renderizado de listas, eventos
y guardar en el local storage.
O sea, vamos a ver un montón de cosas.
Así que quédate, que va a estar bien chulo.
Bueno, muy bien.
Para empezar nuestro tic-tac-toe, yo creo que lo más interesante, primero, sería que pudiéramos
ver el tablero, ¿no?
Voy a hacer una cosa para que no perdamos tiempo, ni tú ni yo, en el tema de los estilos.
Una cosa que voy a hacer siempre en todos los proyectos es copiarme el CSS y no hacerlo
desde cero.
Porque creo que no aporta mucho al curso, ahora mismo, que hagamos nosotros los estilos
desde cero.
Entonces, yo todos los estilos, o los busco por ahí, de otro proyecto, lo que sea, todos
los estilos siempre me los voy a copiar.
Pero no te preocupes, porque estos estilos que yo voy a utilizar, ¿ves?
Yo me he copiado estos estilos del tic-tac-toe, estos estilos también te los podrás copiar
tú, o tú los podrás adaptar.
Pero estos estilos los vas a tener en el repositorio de GitHub para que tú también los utilices,
¿vale?
Para que no los tengas que hacer desde cero y te puedas enfocar en aprender React, ¿no?
Que aprendas React y te tengas que preocupar siempre con los estilos, los estilos y tal.
Entonces, he copiado ya todos estos estilos que tenemos una clase board, que es donde
vamos a dibujar nuestro board.
Tenemos que asegurarnos que utilizamos bien los classnames.
No te preocupes, porque te iré diciendo, oye, aquí tienes que utilizar este, aquí el
otro.
Y también tenemos uno turn, que es para decir qué turno es, y uno para el ganador, porque
vamos a enseñar también como una modal en el que vamos a tener el ganador.
Y luego el square, que digamos que es de nuestro tablero, vamos a tener cada una de las
posiciones.
Así que ahí lo vamos a tener.
Muy bien.
¿Por dónde empezamos?
Bueno, vamos a empezar por el principio, obviamente, ¿no?
Pero lo primero y lo más importante, yo creo que es que tengamos en cuenta los turnos que
vamos a tener.
Así que voy a crear una constante que vamos a decir, bueno, ¿qué turnos tenemos?
Pues tenemos el de la X, ¿no?
Que en este caso sería X.
Y tenemos el de la O, que en este caso sería una O.
La voy a hacer minúscula, lo puedes hacer como tú quieras, ¿vale?
Luego esto lo puedes cambiar por un símbolo.
Luego lo cambiaremos por un símbolo que queda mejor.
Pero por ahora vamos a empezar con esto.
Tenemos una constante con un objeto, esto es JavaScript puro, y ya está.
Entonces, ahora ya sabemos que podemos tener turnos, que son o la X o la O.
Muy bien.
Ahora bien, vamos a dibujar nuestro tablero.
Nuestro tablero vamos a tener aquí, vamos a tener que dibujar, vamos a poner board.
Y esto va a ser un board que va a ser un array de nueve posiciones y la vamos a rellenar.
Ahora la puedes rellenar con null.
¿Por qué?
Porque en este tablero lo que rellenaremos después en realidad son las X o las O.
Pero ahora mismo lo importante es que tengamos el tablero.
Ya verás que esto lo moveré después para que vayamos viendo qué tenemos que hacer con todo esto, ¿no?
¿Qué hacemos con este tablero?
Pues lo queremos dibujar.
Así que aquí en la app vamos a quitar este H1.
Bueno, más que quitarlo, lo vamos a reutilizar.
Pero vamos a envolver todo este con un main.
Bien, ponemos el class name que tenemos el board.
Vamos a dejar el H1 dentro.
Y aquí vamos a crear nuestro tablero, que va a ser una sección que va a tener el class name que le vamos a poner, es el del juego.
Y este class name es porque aquí si buscamos, pues tendríamos, ves, board game.
Y aquí tenemos un display grid donde vamos a utilizar tres columnas.
Entonces, vamos a decirle, es una columna de una fracción que se tiene que repetir tres veces.
De esta forma, como tenemos nueve posiciones, lo que va a hacer es esas nuevas posiciones las va a distribuir para que nos quede el tablero como queremos.
Vamos a hacer aquí un board, map.
Y aquí le puedes poner cell, le puedes poner como tú quieras.
Yo voy a poner que vamos a utilizar el índice, porque el índice va a ser justamente lo que queremos renderizar.
Vamos a tener la posición 0, la 1, la 2, la 3.
Eso es lo que queremos hacer, ¿vale?
Y aquí, pues tenemos que renderizar algo.
Por ahora, voy a hacer caso a lo que me está diciendo esto, para básicamente renderizar algo y que podamos ver ya nuestra pequeña aplicación.
Voy a cerrar este return aquí, voy a guardar los cambios.
Bueno, ya ves que al menos ya tenemos las posiciones de nuestro tic-tac toy, ¿no?
El 0, el 1, el 2, 3, 4, 5.
Ahora no debería os mostrar el número, porque el número no lo necesitamos, pero bueno, por ahora visualmente ya nos sirve, ¿no?
Muy bien. Aquí, esta parte del div, vamos a utilizar el concepto de componente, que ya vimos en su día, ¿no?
¿Qué es lo que tiene que ser cada parte del tablero?
Pues vamos a tener un cuadrado, que va a ser la posición donde se va a poder jugar cada uno de los movimientos.
Vamos a crear justo encima de la app, vamos a crear aquí un square, que va a ser como el cuadrado del tablero.
Y aquí vamos a utilizar diferentes propiedades.
Vamos a decirle, bueno, el children, que será lo que tiene que tener dentro del tablero, por si queremos mostrar la X o la O.
Vamos a tener también una forma de actualizar el tablero, porque cuando hagamos clic en esa posición tendremos que actualizar el tablero.
Vamos a tener también, vamos a indicar, bueno, vamos a dejar esto, vamos a dejarle el índice, ¿vale?
Vamos a tener el índice, para saber ese cuadradito, qué índice es.
Y aquí lo que vamos a tener, pues aquí sí, vamos a renderizar algo.
Lo que queremos que es renderizar aquí.
En lugar de renderizar este div, renderizaremos squares.
Así que vamos a poner aquí un div con el class name, pero en lugar de cell, yo ya tengo un class name que lo tengo por aquí,
que si lo buscamos, es square, ¿vale?
¿Ves el square, que ya tiene como unos estilos?
Pues vamos a utilizar este square, para que mejore visualmente esto.
Y por ahora vamos a poner aquí dentro el children.
Por ahora ya tenemos aquí un componente separado de la app y que ya podemos reutilizar.
Vale que esto es la clave de React, reutilizar.
Así que este return, ahora en lugar de este div que habíamos puesto por aquí, vamos a hacer algo.
Vamos a poner que aquí queremos utilizar el square.
¿Y el square, qué necesitamos?
Esto lo vimos, ¿no?
Para renderizar una lista de elementos, necesitamos utilizarla aquí.
¿Qué es la key?
La key es un identificador único de ese elemento que renderizamos en una lista.
Las listas de elementos se renderizan con el punto map, porque map devuelve un array.
Así que lo que estamos haciendo es, vale, quiero que cada elemento lo renderices aquí dentro.
Y tengo que identificar cada elemento que renderizo con una key.
Si no, React se me quejará igualmente.
En este caso vamos a utilizar el index.
Pero si te acuerdas lo que dijimos en la última clase, es que siempre que puedas tienes que utilizar el identificador único.
Y a veces el índice no es un identificador único.
Pero en este caso sí, porque fíjate que aquí 0, 1, 2, 3, 4, 5, estos son únicos.
Nunca vamos a cambiar el índice de esas posiciones.
Así que lo vamos a considerar como que es único.
El propio índice se lo vamos a pasar como prop, porque ya hemos visto que en el square que lo necesitábamos.
También aquí le podríamos pasar más cosas, pero por ahora vamos a pasarle esto.
Ahora bien, hemos dicho que aquí vamos a guardar el índice, ¿no?
Vamos a guardar el índice para ver aquí cómo queda.
Ya empieza a verse un poquito mejor, ¿no?
Ya empieza esto a pillar un poquito de colorcito, ¿no?
Bueno, el tema es que en lugar de los ceros, aquí lo que deberíamos ver en cada uno,
fíjate que serían cuando el usuario pulsa para ver si pone los ceros o las X.
Así que vamos a quitar el índice.
No es lo que queremos mostrar.
Pero entonces, ¿qué queremos mostrar aquí?
No queremos mostrar nada, lo dejamos vacío.
Lo cierto es que vamos a necesitar un estado.
Vamos a necesitar un estado para guardar cuando el usuario hace clic en cada posición.
Así que el board que yo lo había puesto aquí fuera, vamos a necesitar meterlo dentro de la aplicación.
Porque cuando en el square se haga un clic, vamos a tener que actualizar el tablero para volver a renderizarlo y que el usuario pueda ver,
oye, si se ha puesto una X o no se ha puesto una X.
Por ahora, vamos a crear este, vamos a mover esto que lo había hecho aquí, lo vamos a mover aquí, ¿vale?
Y vamos a pasarlo como si fuese un estado.
Un estado básicamente es un valor que va a, cada vez que cambie, va a volver a renderizar nuestro componente.
Y por lo tanto, cada vez que nuestro tablero cambie, que alguien que tenga su turno cambie un valor en nuestro tablero, queremos que se vuelva a renderizar.
Así que tiene sentido.
Y ahora vas a ver un ejemplo mejor que no el hecho de que siempre sea un estado de true o false, como puede ser un interruptor, ¿no?
Que cuando tiene true se ve la luz, cuando false está apagado.
Bueno, también hay estados más complejos, como en este caso un tablero.
Para crear un estado, ¿qué tenemos que hacer?
Utilizar el hook useState.
Y aquí le tenemos que pasar el estado inicial de nuestro tablero.
El estado inicial ya lo sabemos.
Es justamente el que hemos hecho antes.
Y aquí, en lugar de ahora tener una variable, tenemos un array de dos posiciones.
En la primera tendríamos el board.
Y en la siguiente tendríamos una forma de actualizar el board.
Así que ahora ya por lo menos ya tenemos este board, setBoard.
Y tenemos aquí un array que sería así.
Mira, de hecho puedo poner aquí un console.log del board para que veas cómo tenemos este array.
A ver si lo pone.
Ay, no lo pone.
A ver si...
StartConsoleNinja.
A ver si funciona el ninja este.
Vamos a ver.
Porque a veces esto funciona, pero ya veo que este no...
Pause, restart...
A veces me sale aquí en el console.log, pero hay veces que no sé por qué se vuelve tonto.
Bueno, si no, no pasa nada.
Os lo puedo enseñar aquí.
Os lo puedo enseñar aquí para que lo veáis, ¿vale?
Pero ahora mismo es una array que tiene todas las posiciones y las tiene nulo.
O sea, porque todavía no estamos jugando.
Pero imagínate que ahora ya podríamos empezar a decir, oye, en lugar de utilizar este, que debería ser el correcto,
imagínate que lo hago a mano, ¿no?
Y pongo, pues aquí tenéis la X, aquí tenéis la X y aquí tenéis la X, etc.
Mira, vamos a hacer por ahora que sea a mano, ¿no?
Mira, el Gijacopalio me había dicho.
Vale, ahora tenemos que asegurarnos que sí que mostramos estos juegos, ¿no?
Aquí, que los podemos mostrar aquí, que ahora mismo no lo estamos mostrando.
Este H1, que este H1, que no tiene aquí espacio, vamos a ponerle espacio, ¿vale?
Para que no quede tan juntito.
Margin, bottom, 16 píxeles.
¿Vale?
Ahora, ahora queda separado.
Vale, pues vamos a ver que se vea este tablero, que al menos lo veamos visualmente.
¿Qué podemos hacer con esto?
A ver, lo que podemos hacer aquí es acceder al board, acceder al index y, básicamente, mostrar la jugada.
Ya ves que ya empezamos a tener el X, el X, o...
Estos símbolos son un poco raros.
Normalmente, los que se utiliza, y lo podemos cambiar muy fácilmente,
sería más bien el de multiplicación, que es este de aquí.
Mira, ¿ves?
Si te fijas, son diferentes.
O sea, se tendría que utilizar el de la derecha.
Pero, bueno, después los cambiamos, por ahora los dejamos así.
Aunque se vea un poco feo, luego lo arreglamos.
Entonces, al menos ahora sí que seríamos capaces de mostrar la jugada.
Pero tenemos que seguir avanzando, ¿no?
Tenemos que empezar a decir, bueno, porque fíjate que aquí tendríamos un ganador.
Y, en cambio, esto habría seguido jugando, ¿no?
Esto también lo tendríamos que arreglar.
¿Cómo hacemos que detecte cuando hemos tenido un ganador?
Pues, eso lo vamos a hacer cada vez que el usuario haga clic en uno y actualizamos el tablero.
Lo que vamos a hacer es, vale, voy a ver si ahora ha ganado o no ha ganado.
Pero, para que hacer clic actualice el tablero, ¿cómo lo sabemos si no sabemos el turno del jugador?
Así que vamos a tener que ir por partes.
Primero, vamos a necesitar otro estado para saber de quién es el turno.
Si es de la X o del círculo, ¿no?
Primero el turno.
Después, vamos a tener que hacer que esto, en lugar de que empiece así, esté como estaba antes.
Que esté vacío.
Y que cada vez que se hace clic, se rellena.
Así que vamos a hacer eso.
Vamos a volver a poner este estado inicial como que está todo vacío.
Y vamos a crear un estado para saber de quién es el turno.
Vamos a hacer que empiece el turno la X.
Así que el estado, ¿cómo se hace?
Con UseState.
Le pasamos el valor inicial.
Esto nos devuelve un array de dos posiciones.
Primera posición, el valor del estado.
Segunda posición, cómo actualizar el estado.
Ahora bien, necesitamos también visualmente saber quién tiene el turno.
Porque si no, es muy difícil.
¿Cómo podemos hacerlo?
Pues esto, por suerte, podemos hacerlo en React muy fácilmente.
Porque podemos tener aquí otra sección más.
Section.
Vamos a ponerle class name.
Le vamos a llamar turn.
Porque esto lo tenemos aquí en el CSS.
Y aquí podemos decir que vamos a mostrar un square que tenga el turno.
Vamos a poner aquí turns.
Aquí vamos a mostrar el turno de la X.
Y aquí el turno del círculo.
Pero para que visualmente, ¿ves?
Lo tenemos aquí.
Pero para visualmente saber a quién le toca,
vamos a poner aquí una prop.
Que le vamos a llamar isSelected.
Y esto, ¿cuándo vamos a pasarle true al isSelected para saber cuál de las dos está seleccionada?
Y así poder cambiar visualmente cuál es.
Pues vamos a decirle, como tenemos un estado, podemos decir,
ah, cuando en este turn tengamos, cuando el turn sea igual a turns.x,
el que va a estar seleccionado es justamente este de aquí, el de la X.
Pero cuando el que esté seleccionado, vamos a copiar esto,
cuando esté seleccionado el de la O, pues vamos a poner este.
Así que aquí lo cambiamos por la O.
De esta forma vamos a tener una forma de que el componente square cambie visualmente.
Y esto es súper importante porque esta es la base de React.
El hecho de poder cambiar visualmente un componente a través del estado de un componente padre.
Lo que está haciendo el componente padre app, es decir, cuando mi estado es uno u otro,
voy a hacer que el componente hijo se vea una forma u otra.
Le va a pasar diferentes parámetros.
Y entonces en el square ahora podríamos aquí en isSelected,
se lo vamos a pasar como prop, ahora es como le llega,
y aquí en el class name vamos a decir, vale, va a tener square,
pero si isSelected es true, vamos a tener aquí, vamos a ver cómo se llama aquí, isSelected.
Y así vamos a poder enseñar visualmente si le toca a uno o le toca a otro.
Y este class name lo utilizamos aquí.
Y ya estaríamos haciendo un renderizado condicional, aunque sea solo de a quién le toca el turno.
Así que al menos ya tenemos aquí que ya sabemos que le toca el turno a la X.
Pero cuando le doy clic a esto, esto todavía no está funcionando.
Y necesitamos que sí que funcione.
¿Qué podemos hacer aquí?
En el square, que es el componente que hemos hecho para cada uno de los cuadritos,
podríamos pasarle un update board.
Y este update board lo que tenemos que hacer es tener una función que le diga lo que tiene que hacer.
El update board, ¿qué es lo que tiene que hacer?
Vamos a hacer update board y vamos a crear nuestra función
para indicarle cada uno de los pasos que va a hacer nuestra función.
Esta va a ser una de las funciones más importantes,
porque es la que se va a encargar tanto de actualizar estados,
de cambiar los turnos, de ver si es el ganador o no es el ganador.
Vamos a poner aquí el update board.
¿Vale?
Y lo pasamos por aquí.
Fíjate en una cosa, que esto es importante.
En square le pasamos el update board y le pasamos no la ejecución de la función,
le pasamos la función para que cuando queramos,
desde dentro del square, se actualice y se ejecute realmente el update board.
Esto es muy importante porque un error muy común es que la gente hace esto.
Y esto sería la ejecución de la función.
No, le estamos pasando la función para ejecutarlo cuando queramos.
Entonces aquí, ¿ves que teníamos el update board?
¿Cuándo tenemos que ejecutar este update board?
Pues le vamos a poner aquí on click y aquí tendríamos el update board, ¿no?
Y lo podríamos ejecutar aquí.
Pero aquí necesitamos hacer alguna cosita.
Así que por ahora vamos a poner handle click y vamos a tener update board.
Y por ahora lo vamos a dejar así, ¿vale?
Y este vamos a poner aquí el handle click y esto luego lo actualizamos.
Luego vemos qué tenemos que hacer aquí,
pero por ahora lo importante es que cuando el usuario haga click en el div,
vamos a tener un on click que llama al handle click que llama al update board.
Por ahora lo dejamos así.
Luego veremos si tenemos que pasarle alguna información o lo que tenemos que hacer.
Por ahora solo hacemos esto.
Vamos a volver al update board que lo tenemos por aquí, ¿ves?
Que tenemos nuestra función vacía.
¿Qué es lo que le va a llegar al update board?
Al update board por ahora no le va a llegar nada,
pero bueno, aquí podríamos pasarle cosas.
Luego veremos qué le podemos pasar.
¿Qué podemos hacer aquí?
Bueno, aquí me dicen cosas que podría hacer.
Más o menos necesitamos hacer algo así,
pero bueno, por ahora vamos a ver.
Primero, vamos a cambiar el turno para asegurarnos que podemos cambiar un turno entre uno y otro.
Así que vamos a tener el nuevo turno,
lo tenemos que calcular con lo que tenemos actualmente.
Y si el turno es igual al de las X,
significa que el nuevo turno va a ser el de los círculos.
Y si no, va a ser el de las X.
Aquí lo que estamos haciendo,
como no podemos hacer un cambio de booleano,
no le podemos pasar de true a false,
lo que estamos haciendo aquí es decirle,
oye, si el turno es de las X,
el nuevo turno va a ser el del siguiente.
O sea, el del círculo.
Y si es del círculo, será el de las X.
Eso es lo que estamos haciendo, ¿vale?
El chat está muy lento, pero mido está espídico.
No me jodas.
Bueno, pero ¿qué pasa?
No entendí por qué se pasa la función y no la ejecución.
Vale.
Buena pregunta.
A ver.
Es súper importante esto,
porque yo no quiero ejecutar la función.
Si yo ejecuto aquí la función,
vamos a hacerlo.
Vamos a hacerlo, ¿vale?
Vamos a hacerlo para que lo veas.
Mira, console.log update.board,
para que veas que esta función,
cuando se ejecuta, qué es lo que hace.
Tú imagínate que le pasas la ejecución de la función.
Claro.
Y tú renderizas esto.
Si tú vas a...
Mira, se ha ejecutado la función nueve veces.
¿Tú quieres ejecutar la función nueve veces
porque se ha renderizado la square?
No, tú no quieres ejecutar la función
hasta que el usuario no haga clic.
Si tú le pasas la ejecución de la función,
estaría rompiéndolo.
Porque lo que estás haciendo es...
No, no, ejecuta esta función.
Y esa función puede estar haciendo setStates.
O sea, estarías, cada vez que se renderiza,
estarías actualizando el board.
No tiene sentido.
Tú lo que quieres es ejecutarlo
solo cuando lo necesitas,
solo cuando se hace clic.
No cuando se renderiza.
Esa sería la diferencia, ¿vale?
Y súper importante.
Y esto es un error que muchas veces la gente dice,
pero ¿por qué no la ejecución?
Bueno, porque la ejecución la querrás hacer cuando quieras.
No cuando se renderiza.
Y esa es la diferencia.
Entonces, queremos controlar realmente
cuando se ejecuta esto.
¿Sí?
Vale.
Vamos a dejar como estaba.
Entonces, queremos que la ejecución
se haga cuando se haga clic.
Entonces, por eso se la pasamos como parámetro
a nuestro square, ¿vale?
El square es cada uno de estos,
de estas posiciones en el board, ¿ok?
Ok.
Vale.
Entonces, aquí tenemos el new turn.
Pero ya sabéis que para poder actualizar el estado,
lo que tenemos que hacer es llamar al setTurn.
Así que vamos a hacer setTurn
y le pasamos el nuevo valor que tiene el turno.
Por ahora, tenemos esto, ¿no?
Cada vez que vamos a hacer clic en cada uno de los cuadrados,
vamos a cambiar el turno, ¿vale?
Pues, fíjate.
Bueno, no sé si te puedes fijar porque estoy encima.
Pero, bueno, dejadme arreglar eso en un momento.
Vamos a ver para que no aparezca yo arriba.
Vamos a poner esto aquí.
Claro, es que si ponemos esto aquí y esto,
align centers, justify, justify content center.
Vale.
Esto sí, pero esto, start.
No, items start.
Ahora no me acuerdo.
¿Items start?
Ahora no me acuerdo cómo es.
Bueno, si quitamos este y este, vale.
Mejor, ¿no?
Y así lo vemos arriba y así no tiene ningún problema, ¿vale?
Así lo tenemos mejor.
Vale.
Pues, fíjate que ahora cuando le voy dando aquí a cada uno,
ahora al menos ya estamos cambiando el turno.
¿Cómo estamos haciendo esto?
Vale, vamos a volver a tirar del hilo para ver un poquito esto.
Lo que estamos haciendo es,
estamos renderizando cada uno de los squares dentro del tablero.
Cada square tenemos aquí el índice y tenemos el update board.
Y el update board se lo estamos pasando como prop al componente square.
El componente square lo que dice es, bueno,
cuando alguien haga clic en el div, o sea, en este div,
este div que tenemos aquí entre todo lo que está dentro de este borde,
pues, lo que va a hacer es ejecutar la función handleClick.
La función handleClick ejecuta la función update board
que le estamos pasando desde arriba, ¿vale?
Y esta función update board es la misma función que estamos ejecutando aquí.
O sea, esta función la que le estamos pasando como parámetro
y esta función en la que tenemos aquí es la que ejecutaremos aquí.
Si tú aquí le pasas un parámetro,
este parámetro va a ser el que le va a llegar aquí, ¿vale?
Así que esto es lo que estamos haciendo.
Desde el componente padre app le pasamos la función update board
y esta función update board la ejecutamos solo cuando hacemos clic en el div, ¿ok?
O sea, que es súper importante que estemos pasándole la función, no la ejecución, ¿vale?
¿Por qué usas una variable new turn y no haces el setState negando turn?
Hostia, Idant. Esa es bastante tela, ¿eh?
A ver, vamos a ver.
Turn no es true o false.
¿Es X o O?
¿Es X o O?
Claro, no es true o false, ¿vale?
O sea, que no se puede negar y ya está.
También yo creo que es mucho más legible si hacemos esto,
que no utilizamos que esto sea true y que esto sea false.
Yo creo que es mucho, mucho, mucho más legible.
Entonces así lo evitamos.
No es un boleano, es un string que en realidad estamos utilizando como un enum, ¿vale?
Así que eso, ¿eh?
Y turn, sí, el turn es del turno, ¿eh?
Estos son los turnos, no es de prender.
Estos son de los turnos, ¿vale?
Vale.
Pues ya al menos tenemos una forma de ir cambiando el turno.
Pero obviamente en el Update Board tenemos que hacer más cosas, ¿no?
Cuando actualizamos el Board tenemos que hacer más cosas.
Cuando el usuario hace clic, le vamos a pasar el índice para saber en cuál de estas ha hecho clic.
De esta forma decimos, vale, ha hecho clic en la 0, en la 1, en la 2, en la 3, en la 4, en la 5, en la 6, en la 7 o en la 8.
Y así vamos a poder actualizar el Board con esa información.
Vamos a decir, vale, vamos a ver, vamos a tener que decirle, vamos a tener un nuevo Board y vamos a poner que el nuevo Board, como recibe el índice, vamos a ponerle el valor del turno actual.
Y así el usuario que ha dado clic, que tenía turno, le ha dado clic a esa posición, en esa posición ha guardado ese turno, que puede ser aquí X, U, O, una de las dos, ¿no?
Así que al menos con esto ya tendríamos el Board.
Obviamente todavía tenemos que actualizarlo.
¿Cómo lo hacemos?
Tenemos setBoard, newBoard, ya está.
Con estas tres líneas vamos a actualizar ya que el Board se actualice, o sea, que se vea visualmente.
Vamos a ver, ¿vale?
Pues ya tenemos al menos, o sea, ya tenemos el juego, ya casi tenemos el juego.
Bueno, como ves, hay una cosa que no está bien.
Uno, el juego no termina nunca.
Dos, puedes sobreescribir donde estás haciendo clic.
Obviamente son cosas que tenemos que mejorar, ¿no?
Pero ya tenemos lo que sería todo lo que es la actualización del estado en que se ve visualmente, ¿no?
Que si le doy aquí a la X, aquí acá, tan, tan, necesitamos que el usuario gane, o sea, saber cuándo gana y además necesitamos que, si lo he completado todo, que termine el juego, que pare ahí.
Pero al menos en este ratito ya tenemos todo esto, todo esto hecho.
Ahora, antes de continuar, primera pregunta.
¿Por qué hago esto?
¿Por qué he hecho una copia del Board?
¿Por qué no hago esto?
¿Por qué no hago esto?
Y ya está, ¿no?
Y aquí le pongo el Board.
¿Por qué no hago esto?
Ya os pongo la X.
¿Por qué esto está mal?
Está mal, ¿vale?
Esto está mal.
Hacer esto, hacer esto, está mal.
¿Por qué está mal?
Porque no tenéis que mutar nunca las props ni el estado.
Tenéis que tratarlo como si fuesen inmutables.
Esto quiere decir que los arrays no tenéis que modificar el valor que tiene, sino que tenéis que hacer es siempre crear un nuevo array con el valor nuevo.
¿Y esto cómo lo podéis hacer?
Podéis hacerlo, en este caso, con un Spread Operator, ¿no?
Aquí podéis decir, bueno, pues del Board quiero que todos los elementos que estén dentro del Board me los metas en un nuevo array.
Por eso es súper importante, y yo os decía en la primera clase, que saber entender el Spread y el REST Operator es súper importante, ya no solo en React, en JavaScript en general.
Si no sabes esto, cuando termines esta clase, te pones a estudiar esto, porque significa que te queda mucho.
Y sin esto es imposible que luego pases cualquier prueba técnica.
Así que esto lo tienes que aprender.
Súper importante.
Puedes hacer una copia de un array de forma superficial, que esto también es importante, con esto.
Para hacerla de forma profunda, si fuese necesario, podrías utilizar StructureClone.
En este caso no lo necesitamos y por eso no lo vamos a utilizar.
Pero al menos podríamos hacer una copia profunda del array así.
No lo necesitamos, pero solo para que lo sepas.
Entonces, los estados siempre hay que tratarlos como inmutables.
Entonces, tú me dirás, ostras, ¿pero por qué?
¿Por qué hay que hacerlo como inmutable?
Porque si tú lo mutas, si tú lo que haces es modificar el estado, lo que puede ocurrir es que haya problemas de renderizado.
Porque tú has modificado el estado directamente en lugar de utilizar el Setboard.
Lo que estás haciendo es modificar el estado actual sin llamar al Setboard.
Y lo que puedes tener son discrepancias en el renderizado.
Por eso es importante que los datos siempre sean nuevos.
Los datos del nuevo renderizado siempre sean nuevos.
Siempre hay que pasarle un array nuevo.
Siempre hay que pasarle un objeto nuevo.
Siempre tiene que ser algo nuevo.
O el mismo.
Si es el mismo, no pasa nada.
Pero si lo han modificado, tiene que ser uno nuevo.
Así que por eso estamos haciendo la copia.
Muy importante esto.
Súper importante.
Y esto ahora sí que está bien.
¿Vale?
Hemos hecho la copia para evitar modificar el original.
¿Ok?
¿Sí?
¿Importante?
¿Fino?
¿Ok?
Bueno, pues ya tenemos aquí esto.
Ahora, ¿qué podemos hacer para evitar que se sobreescriba?
Porque ya ves que se sobreescribe.
A ver, lo primero que podríamos hacer es que cuando vamos a actualizar el board, voy a decir,
oye, si en el board, en el índice, hay algo, pues oye, no escribas, ¿no?
No actualizamos esta posición si ya tiene algo.
Como por defecto son nul todas las posiciones, claro, si tú intentas actualizar la posición,
aquí miras, oye, la posición tiene algo, pues return, ¿vale?
O sea, no hace nada.
Ahora, al menos, vamos a ver que cuando yo intento hacer clic encima de una posición
que ya hay algo, no me deja.
Es sencillo, pero efectivo.
Y ahora, lo demás sí que funciona, pero ya no me deja hacer clic en ningún sitio.
O sea, ya sería una forma de decir, oye, hasta aquí, hasta aquí.
Esto ha terminado, hasta aquí.
Bueno, ya tenemos lo del nuevo board.
Esto sería actualizar el tablero.
Vamos a decir aquí que esto es cambiar el turno.
Pero necesitamos dos cosas, ¿no?
Una, saber cuándo hemos ganado.
Por ejemplo, si yo hago esto, significa que ha ganado la X.
Tendríamos que parar el juego aquí.
Tenemos que decir, oye, ha parado el juego.
¿Cómo podemos hacer esto también?
Con un estado.
Podemos saber cuándo tenemos un ganador utilizando un estado.
Así que lo que vamos a hacer aquí es crear un nuevo estado que nos diga
winner, setWinner, useStateNull, ¿vale?
Vamos a decir que el null es que no hay ganador y el false es que hay un empate, ¿vale?
Vamos a utilizar el null como que no hay ganador y el false un empate.
Esto lo podéis hacer de otra forma.
Por ejemplo, en winner podríais utilizar también un enum como hemos hecho con los turnos.
Pero lo vamos a hacer así por simplificarlo.
Pero luego la idea es que muevan las manitas.
O sea que esto al final yo lo dejo así, pero lo importante es que lo entiendas y a partir
de aquí tú hazlo como tú quieras.
Hay muchas formas de ver cuál es el ganador en el 3 en raya.
Y esto es importante para la lógica de programación, que muchas veces la gente me pregunta, ¿cómo
practicar la lógica de programación?
Bueno, pues aquí tienes un ejercicio de lógica de programación mucho más interesante que
hacer un ejercicio por ahí suelto.
Mucho más interesante hacer un juego que normalmente tiene mucha lógica de programación
que no hacer un ejercicio suelto.
Y mira que a mí el AppGIS me encanta, pero es mucho mejor hacer esto porque al final
ves la gracia de la lógica de programación.
Una forma muy fácil, y yo creo que no solo fácil, sino a veces recomendada, pero no del
todo la más óptima, sería tener todas las combinaciones ganadoras.
O sea, podríamos tener una constante que le vamos a llamar combos o winner combos.
Winner combos, ¿no?
Y podríamos tener un array donde tenemos todas las combinaciones ganadoras.
Por ejemplo, la línea, si tienes el 0, el 1 y el 2, el 3, el 4, el 5, el 6, el
7 y el 8.
Entonces, esas son combinaciones ganadoras, ¿no?
Por lo tanto, aquí deberíamos poner el 0, 1 y el 2, el 3, 4 y 5, 6, 7 y 8.
Luego también tendríamos las verticales.
O sea, tendríamos el 0, 4, 7, ¿no?
Pues bueno, ahí las tenemos.
El 0, 3, 6.
A ver, 0, 1, 2, 3, 6.
Vale, 0, 3, 6.
Perdón.
Menos mal que dije, compañero, las he hecho bien.
1, 4, 7 y 2, 5, 8.
Estas serían las verticales.
Pero también nos faltan las diagonales.
Que serían 0, 4, 8, 2, 4, 6 y ya está.
No teníamos más.
Estas serían.
0, 4, 8 sería esta, esta y esta.
Mira, te la puedo hacer.
Te puedo hacer.
Esta sería una.
Y la otra sería hacia el otro lado, ¿no?
Esta.
Esta sería otra.
Esta sería otra.
Estas son todas las combinaciones ganadoras.
Hay otras formas de hacerlas, muchas más óptimas,
que te invito a que lo practiques.
El hecho de cómo puede saber el ganador o la ganadora rápidamente,
haciendo el mínimo número de combinaciones.
Pero, bueno, nosotros lo vamos a hacer así,
porque es bastante fácil de hacerlo.
Y podríamos tener aquí un método que le vamos a llamar checkWinner,
que recibe un board, como el board, como el tablero para chequear.
Y aquí, pues, podemos decir, vale, para cada combinación que tenemos
en los winner combos, estos, vamos a hacer una cosa.
Vamos a mirar.
Vamos.
Muy bien, joder, qué bueno, qué hackopilot.
Vamos a recuperar las posiciones A, B y C.
O sea, vamos a recuperar el 0, el 1 y el 2.
Y lo que voy a hacer es, primero, voy a mirar si el board to check
en la posición A existe algo, ¿no?
Pero luego voy a mirar si en la posición A y en la B es la misma.
O sea, ¿qué estaría mirando, por ejemplo?
Primero voy a mirar si en el 0 hay un número.
Luego voy a mirar si, o un número no, si hay una X, ¿no?
Si hay una X, por ejemplo, una X o una O.
Y luego voy a mirar si en el 0 y en el 3 está exactamente la X
y luego la X o la O y luego la O.
O sea, si tienen lo mismo.
Y lo mismo ya para el tercero, ¿no?
Que sería el A igual a C.
Bueno, esto lo podemos hacer de diferentes formas,
pero esta sería una, ¿no?
Si la A es igual a la B y la A es igual a la C,
significa que tenemos un 3 en raya.
O sea, que tenemos ya un ganador.
Así que vamos a devolver quién es el ganador.
Y el ganador sería el que está justamente en el primer elemento.
En este elemento, esto nos debería devolver X o O.
Una de las dos.
Y ya sabremos con esto fácilmente cuál es el ganador.
Si no tenemos ganador, bueno, pues no pasa nada.
Aquí, en el for, vamos a decir que esto nos devuelva a null,
que no tenemos ganador, ¿no?
Si no hay ganador, pues ya está.
Con esto revisamos todas las combinaciones ganadoras,
ganadoras, para ver si X o U ganó, ¿vale?
Y ya está.
Con esto ya podemos chequear si tenemos un ganador.
¿Y cuándo tenemos que chequear si tenemos un ganador?
Lo tenemos que chequear siempre.
Lo tenemos que hacer, ¿cuándo lo tenemos que hacer?
Bueno, pues chequear si tenemos un ganador lo podemos hacer
cuando actualizamos el board, por ejemplo.
O sea, cuando estamos haciendo todo esto,
podríamos decir, bueno, pues aquí vamos a revisar si hay un ganador.
Vamos a ver si ahí tenemos un ganador.
Vamos a ver si tenemos un new winner.
Utilizamos el check winner, le pasamos el nuevo tablero
que hemos creado aquí, porque tenemos que ver el nuevo tablero
que tiene el último movimiento.
No vamos a utilizar el antiguo, ¿vale?
Si tenemos un nuevo ganador.
Bueno, esto significa que tenemos que hacer un set winner
y ya está, tenemos un new winner, punto.
Ya está.
Ahora, vamos a ver si tenemos ganador, si no tenemos ganador
y qué hacemos con este ganador.
Cuando hemos actualizado nuestro componente con el ganador,
tenemos que renderizarlo también, porque si no, no vamos a verlo.
Y vamos a seguir jugando y tal, y esto no tiene sentido.
De hecho, cuando tenemos un ganador,
también tenemos que evitar que se pueda seguir actualizando el board.
O sea que, no solo cuando el board intentemos hacer clic en la misma,
sino que si tenemos un ganador, ¿no?
Si aquí, board no sé qué, o tenemos un ganador,
pues entonces no funciona seguir jugando,
porque no tendría sentido.
Tenemos un ganador y seguimos jugando.
No tendría sentido.
Así que, por eso, cuando en esa posición del tablero ya hay una ficha
o tenemos un ganador, pues si tú haces clic, no funciona.
Ahora, vamos a ver si al menos paramos de jugar.
Yo hago aquí esto, vale, vale.
Ya no me funcionan los clics porque ha detectado que tengo un ganador,
pero visualmente no estamos viendo quién ha ganado.
Tenemos que informarle al usuario,
oye, ha ganado Pepito, ha ganado quien sea.
Podríamos hacerlo con un alert, ¿no?
Podríamos decirle, oye, aquí un alert, alert.
Mira, ganó.
El ganador es...
Bueno, esto sería un poco pirata, ¿no?
Pero bueno, al menos sabemos que ha ganado la X, ¿ves?
Y se muestra aquí.
Ha ganado la X.
Fíjate, esto es interesante, ¿no?
Fíjate que cuando lo he hecho así,
fíjate que sale el alert antes de que se vea visualmente.
¿Sabes por qué pasa esto?
Esto pasa porque he puesto el alert antes del set winner.
Pero si lo pongo después, ¿va a salir la X o no?
Pregúntatelo.
Piensa en ello.
¿Va a salir la X o no?
¿Va a salir la X o no?
No sale tampoco.
¿Por qué cuando pongo el alert justo después del set winner
visualmente no se ve la X?
¿Qué significa esto?
¿Qué significa esto?
Esto es súper importante en React
y lo tienes que tener aquí a fuego, ¿vale?
Con lo que te voy a decir ahora mismo.
La actualización de los estados en React son asíncronos.
Esto quiere decir que esto no es síncrono.
Esto no bloquea.
No bloquea el renderizado.
Y entonces de repente vas a ver aquí que ya tienes en winner el nuevo valor.
Como esto actualiza el estado, actualiza el estado, la actualización del estado es asíncrono.
Esto quiere decir que puede ocurrir en 100 milisegundos, 250, pero no bloquea la ejecución del código que viene justo después.
Y esto es muy importante no solo por esto que estamos viendo visual, sino porque si yo pongo un console.log de winner aquí, ¿vale?
Si yo pongo aquí un console.log de winner, hay gente que si esto fuese síncrono, si esto fuese síncrono, que no lo es, si fuese síncrono, aquí en el console.log de winner, que estamos leyendo el estado,
estamos aquí leyendo el estado, aquí alguien podría pensar que winner tiene el estado nuevo y no lo tiene, porque es asíncrono.
Así que esto tenlo muy en cuenta.
En la actualización del estado es asíncrono.
Y aquí puedes tener el nuevo valor o no lo puedes tener.
No puedes contar con ello, ¿vale?
De hecho, vamos a verlo.
Si yo pongo un ganador, ¿ves?
Me sale null justo después de actualizarlo.
Porque, claro, como esto es asíncrono, aquí no tiene todavía el nuevo estado, ¿vale?
Esto es súper importante.
Lo tenéis hasta aquí, esto lo habéis entendido bien, porque esto es súper importante, ¿vale?
Esto es un algo que hay mucha gente que todavía con años de experiencia falla en esto.
Y esa es una de las razones por las que fíjate que aquí en el check winner no le estoy pasando el nuevo board.
Porque si yo le pasase, si no le pasase el parámetro y en check winner, en lugar del board to check, utilizase el del estado, tendríamos un problema.
Este es un error muy común.
Muy común.
Y esto es básico.
Es básico.
Pero no sé si es que los cursos no lo explican.
No lo sé.
Pero esto es básico.
Si no sabes esto, no entiendes React.
O sea, no entiendes el estado.
No sabes cómo funciona.
Si yo en el board, en el check winner, le utilizase el estado, claro, alguien podría pensar que aquí en board ya tiene el estado nuevo.
Pero el problema es que no lo tiene.
No lo tiene porque como esta actualización es asíncrona, pues no podemos fiarnos que el check winner va a poder ver el valor que hemos visto.
Así que tenemos que asegurarnos, mira, vais a ver el error.
Vamos a verlo y vais a ver que tiene un error.
Vais a ver que esto falla.
Si yo ahora hago esto, pam, pam, pam, pam, pam, pam.
Ojo, me ha dejado jugar, pero ya no me deja jugar.
¿Por qué ha pasado esto?
Me ha dejado hacer una jugada más pese a que había ganado ya.
Y eso es porque el check winner ha funcionado con el valor del estado anterior.
Y tenemos que asegurarnos que utiliza el nuevo.
La forma de solucionarlo ya la hemos hecho.
Es muy sencilla.
Es que nos aseguramos de que le estamos pasando el valor que necesita.
Ya está.
Esta sería una forma, ¿no?
El hecho de decir, oye, por parámetro, cosa que a mí parece una buena práctica en general para asegurarte que estás utilizando el valor correcto, ¿vale?
Lo ves, queremos el new board, pues le ponemos aquí el new board o el board to check, ¿vale?
Esto es un error muy común porque la gente tira el estado como suena mañana.
Esto sería una forma de arreglarlo y tendríamos otra forma de arreglarlo todavía.
No en este caso, aunque sí que lo podríamos hacer, o sea, podríamos hacerlo, pero os voy a explicar un caso de arreglarlo en el setWinner.
El setState, o sea, cuando actualizamos el estado, podemos pasarle un nuevo valor o le podemos pasar un callback.
Le podemos pasar una función y la función que tenemos aquí, lo que tenemos que devolver aquí, tenemos que devolver el new winner.
O sea, este sería el valor que queremos que actualice.
Y aquí como parámetro le llega el previous winner.
Esto, en este caso, aquí no lo necesitamos.
No lo necesitamos porque no sirve para nada.
Es opcional porque el previous winner aquí no lo necesitamos, pero si quisiéramos, podríamos verlo.
Podríamos decir, console.log, vamos a poner el ganador es el new winner, el anterior era previous winner.
Y esto sería una forma de poder acceder al estado anterior y al nuevo estado al que tú quieras tener aquí, ¿vale?
Pero no puedes, igualmente esto, como puedes ver aquí, esto es asíncrono, igualmente esto te asegura que dentro puedes tener acceso al new winner, porque eso lo vas a pasar tú, porque tú sabes lo que tienes que hacer.
Vas a poder tener acceso al anterior por si quieres hacer cualquier tipo de comparación entre el anterior y el nuevo, pero ten en cuenta que esto igualmente es totalmente asíncrono, ¿vale?
O sea, que esto no asegura que fuera, o sea, lo que pasa es que aquí puedes ejecutar lo que tú quieras, ya está, con el valor anterior.
Pero no puedes pasarle un callback de decir, no puedes hacer una sincawait, ¿sabes? No puedes decirle, no puedes hacer algo así, no puedes hacer aquí una wait, esto no funciona porque no devuelve una promesa, no puedes hacer eso.
Punto pelota, ¿vale? Ahí lo tendríamos, ¿vale?
Pero el prep winner no sería un ganador válido, ¿no? Hombre, sería null, válido es porque null es null, ¿no? O sea, no pasa nada, o sea, sería null, no pasa nada, ya está.
Vale, os voy a subir mientras, porque a lo mejor alguien lo está intentando hacer a la vez, os voy a subirlo, bueno, os voy a subir todo por ahora, voy a subir start team second project, ¿ok?
Y sincronizamos los cambios. Ok, muy bien. Entonces, hemos avanzado, ya tenemos nuestro juego, pero fíjate, un juego es un ejercicio maravilloso porque poco a poco te vas dando cuenta de los problemillas que van apareciendo
y hacer que todo funcione como esperamos es bastante interesante. Pero bueno, lo importante es que ya tenemos el new winner, en este caso ya lo tenemos todo bien, tenemos el ganador,
todo parece que funciona bien, pero cuando, si no tenemos un ganador, bueno, podría intentar aquí a ver si no la lío, si no tenemos un ganador,
ah, que tengo un ganador, a ver, otra vez no, otra vez no, otra vez no, aquí no soy capaz de hacer que no sean ganador, joder. Vale, vamos a hacerlo así.
Eh, aquí esto, esto, este le vamos a dejar aquí, este aquí, a ver si es que, ¿cómo la lío? Este lo dejamos aquí, este lo dejamos aquí, que la lío, ¿eh? Vale.
Bueno, pues fíjate que el juego en realidad ha terminado, pero no puedo seguir jugando básicamente porque he hecho la lógica de que si ya tiene una cosa,
o sea, si ya tiene una X, una O, no puedas ponerla, pero realmente no sabemos si hemos empatado, si ha terminado el juego, no lo hemos notificado.
Y esto también lo vamos a tener que hacer. Así que nos faltaría aquí, vamos a poner aquí, check if game is over, ¿vale? Lo vamos aquí, vamos a poner el to do para hacerlo después,
nos faltaría esto. Por ahora vamos a hacer, mostrar si tenemos un ganador, ¿vale? Vamos a decirle, oye, si hay un ganador, vamos a hacer algo.
Para eso, tenemos aquí una sección para el juego, tenemos una sección para los turnos, y ahora vamos a hacer una sección, vamos a hacer una sección con un renderizado,
un renderizado condicional, de forma que si winner es diferente a null, porque null es por defecto, o sea, no tenemos winner, vamos a hacer algo.
Si es diferente a null, significa que puede ser false, puede ser X o puede ser círculo, vamos a renderizar algo.
Vamos a poner un section, class name, vamos a ponerle winner. Este estado es una modal, este class name es una modal que tengo por aquí, ¿vale?
¿Ves? Winner, lo tengo aquí, que esto nos va a dibujar una modal. Y en esta modal vamos a poner con un div, vamos a tener aquí el texto,
y aquí tenemos que decir quién ha ganado, quién de los dos ha ganado. Bueno, pues vamos a ver. En el caso de que el winner sea false,
vamos a decir que ha habido un empate, pero si no, vamos a decir quién ha ganado. Y el que ha ganado, vamos a decir, ganó más winner, ¿no?
Para ver la X y A y tal, pero voy a hacerlo un poquito más bonito. En este caso voy a hacer que el que ganó muestre también,
como en el turno que hemos hecho esto, que también lo muestre. Y así, pues, hombre, quedará un poquito más bonito
porque si a alguien le interesa ver y quiere empezar a mejorar esto. Así que vamos a poner un header aquí, vamos a poner aquí que el win,
o sí, el win, que si tenemos un winner, vamos a poner aquí square y dentro le ponemos el winner. Así lo dibujamos dentro de una square
y queda más bonito, ¿no? Y vamos a poner por aquí un footer, por ahora lo vamos a dejar vacío. Bueno, vamos a poner el botón ya
para hacerlo después, que sea empezar de nuevo, ¿no? Porque nos gustará volver a empezar. ¿Ves? Aquí tenemos ya, ganó la X,
porque la X había hecho esto aquí. Ah, es que en realidad ganó la X, la madre que me parió. No es que había terminado,
es que ganó la X, la madre que me trajo. Bueno, vamos a ver, vamos a ver. Antes había ganado la X y yo diciendo, no, he hecho que no funcione.
Vale, ahora sí. ¿Ves? Ahora no ha ganado ninguno. Ahora no ha ganado ninguno. Vale, aquí es donde deberíamos decir,
si ha terminado, pues hacemos el winner a false. Ahora vamos a hacer que gane la X. ¿Ves? Ahora sí tenemos en la modal
que nos dice, ha ganado la X. Si hacemos que gane la Y, debería también aparecernos, ¿no? Esto por aquí, este aquí.
Ganó este. O sea, ya tenemos la lógica de cuál de los dos ha ganado. Tenemos aquí empezar de nuevo.
Este es súper fácil. Normalmente, en los juegos, o no tanto en los juegos, sino en cualquier aplicación de React
en la que quieras empezar de nuevo, vamos a tener aquí, vamos a poner reset game, ¿no?
Vamos a hacer que cuando se clique, se ejecute la función reset game.
Lo que tenía que hacer la función reset game, igual que lo tendría que hacer en cualquier aplicación,
por ejemplo, para resetear un formulario, para resetear lo que sea,
lo único que tenemos que hacer es asegurarnos que seteamos el estado a sus valores iniciales.
O sea, si aquí teníamos array nuevo fill null, que empiece el turno la X y que el winner es null,
pues exactamente eso es lo que tenemos que hacer. El board, pues array nueve fill null.
El turno, turno X, set winner null. Y esto automáticamente ya, lo que va a pasar cuando hagamos clic en esto,
es que empieza otra vez. Y esto es una de las grandes maravillas que tiene React.
El hecho de que nuestra UI es replicable, se replica. Lo importante es que le pasemos las mismas props
y que tengamos el mismo estado. Y esto lo que hace es que nuestra interfaz se replique.
Y este concepto lo puedes llevar no solo a un juego, es que en cualquier página web esto te va a pasar.
Filtros de búsqueda, un formulario, si te gusta o no te gusta algo, si has empezado a escribir un texto,
lo que sea que se te ocurra, esto es tan fácil como volver a resetear con el estado que quieres.
Punto pelota. Punto pelota.
Ya está. ¿Ves? Tú vas jugando, pam, pam, pam, dices X. Empezar de nuevo. Y empieza de nuevo.
Tan sencillo como esto. Porque hemos reseteado directamente el estado.
No hemos tenido que resetear la página para hacer esto, que muchas veces eso va a ser innecesario
y una mala práctica porque a lo mejor hay estados que sí que nos interesa guardar.
Claro, si tú actualizas la página, primero es que tienes que cargar todos los recursos de nuevo
y luego que hay estados que a lo mejor sí que te interesan. Esto puede ser parte de una aplicación mucho más grande
en la que solo quieres que se resete el estado de este componente. O sea que no es lo mismo hacer un location.reload
que resetear el estado. ¿Vale? Vamos a poner aquí en el tic-tac-toe, vamos a poner aquí también otro botón
que sea justamente esto. Reset del juego. Y vamos a hacer aquí el on-click con reset...
Había puesto reset game, ¿no? Reset game. Y así vamos a tener también un botón fuera, aquí, para poder hacer un reset del juego.
¡Pum! Reseteamos el juego.
Nos falta todavía dos cositas. Empieza... Está bastante cerca ya de... Ya pintaba todo bastante bien
y yo creo que lo que es de visualizar, de qué tenemos que renderizar, lo tenemos bien.
Nos faltan dos detalles. Uno, que mientras... Cuando empezamos el juego, si yo reseteo, se pierden los cambios.
Que esto lo vamos a hacer con local storage y yo creo que es muy interesante para que vean lo fácil que es también.
Y lo segundo que también nos falta es el hecho de que si no gana nadie, que veamos que es un empate, ¿no?
Y cómo podemos hacer esto, ¿no? Vale. Para lo del empate, ya había dicho aquí, check if game is over.
Es muy fácil, muy fácil ver si realmente hay un empate y el juego ha terminado.
¿Cómo lo vamos a hacer? Vamos a hacer aquí if new winner set winner, ¿vale?
Y si no, else if. Vamos a ver si... Bueno, es que es esto básicamente, casi.
Pero lo que vamos a hacer aquí es hacer un check in game y le vamos a pasar el new board.
Y si tenemos esto, pues vamos a poner set winner false.
Si chequeamos el juego, si chequeamos el juego y no tenemos ningún ganador, ¿no?
Hacer check in game. Si chequeamos el tablero, vemos que se han dicho todos los movimientos posibles.
No vemos ningún ganador. Hacemos set winner false y esto significa que ha habido un empate.
Ha terminado el juego y tenemos un empate.
Vamos a hacer este check in game. Lo vamos a hacer aquí.
Check in game. Le pasamos el new board.
Y esto es súper fácil de ver si... Mira, revisamos el empate y si no hay más espacios vacíos en el tablero.
Efectivamente. Es así de fácil.
Para revisar esto, como si te acuerdas el tablero, el new board, era un tablero que era así, ¿no?
New, null, null, null. Y nosotros vamos poniendo x, la o, x.
O sea, vamos como en cada posición vamos poniendo nuevos valores.
Entonces, ¿qué pasa? Que una vez que todas las posiciones que tenemos en el tablero,
todas las posiciones sean diferentes a null, significa que se han hecho todos los movimientos.
Y por lo tanto, ya ha terminado el juego y ha terminado como empate.
Para hacer eso, utilizamos el new board, utilizamos el every.
Decimos, si todas las squares, si todas las posiciones del array new board, todas,
tiene que el square es diferente a null, esto significa que será x o y,
pues el square x o y, significa, pues nada, que ha terminado el juego.
Por lo tanto, esto devolverá true.
Chequeamos si ha terminado el juego.
Si todos son diferentes a null, pues significa que ha terminado el juego.
Y con esto, ya lo tendríamos.
Ahora, si soy capaz, si soy capaz de...
Mira que...
Mira que tengo tela, ¿eh?
A ver, si soy capaz.
¿Lo he conseguido?
¡Muy bien! Empate.
Bueno, pues ya tenemos el empate.
Ya tenemos el empate.
¡Buey! Me ha costado, ¿eh?
Me ha costado más el tema del empate que hacer el programita.
Bueno, pues ya tenemos aquí el empate y ya, pues empezar de nuevo y ya tendríamos nuestro juego.
Con esto, hemos hecho toda, toda la lógica, toda la lógica de un 3 en raya
y lo hemos hecho, pues nada, aquí en vivo, en directo, en una horita.
En una horita, mientras lo estaba explicando, ¿vale?
Ahora, a partir de aquí, podéis hacer lo que queráis.
Una pregunta, el board.map.
Board.map.
Esto, esto es la primera posición.
La primera posición sería el square, o sea, lo que hay en ese square.
Esto, realmente, podríamos hacer esto aquí, ¿eh?
Así, ya está.
Entonces, debería ser exactamente lo mismo.
Yo le había puesto esto porque al principio no lo estábamos usando, pero ya está.
¿Queremos confeti para celebrar?
Claro, podemos hacer...
Ahora vamos a arreglar algunas cosas.
Vamos a hacer lo de guardar la partida, que es súper interesante guardar la partida, ¿vale?
También lo vamos a hacer, guardar la partida.
Podríamos hacer lo del confeti, si queréis.
Eso es súper fácil.
Vamos a...
Mira, vamos a hacer lo del confeti en un momento.
Canvas confeti.
Vamos a añadir una dependencia para que cuando terminemos, hacemos el confeti, ¿vale?
Vamos a import confeti from canvas confeti.
Y vamos a hacer que si hay un winner, ¿no?
Set winner, ta, ta, ta.
Winner, si hay un ganador, aquí, set new winner.
Vamos a hacer el confeti y ya está.
Y así, cuando tengamos un ganador, echamos un confeti.
En un momentito, ¿eh?
En un momentito.
¿Habéis visto?
En un momentito.
O sea, con esto hemos hecho que saque el confeti en el caso de que tengamos un ganador.
Pero, bueno, son estos detalles que son súper sencillos y que, pues, bueno, le da subidita, ¿no?
Vale, cosas...
Esto habría que mejorarlo, ¿no?
Primero, vamos a separar los componentes.
Por ejemplo, aquí tenemos el square.
El square, para sacarlo en un fichero aparte, podríamos crear aquí components y podríamos tener aquí square.jsx.
En lugar de tenerlo aquí, vamos a sacarlo y lo vamos a poner aquí.
Para sacarlo, ponemos también aquí export.cont.square.
Y para utilizarlo aquí, hacemos un import de square from components square.jsx.
Esto lo ponemos aquí y ya lo tendríamos.
Luego, también podríamos crear un fichero que sea constantes.
constants.js.
Y entonces, en lugar de tener aquí estas constantes, podríamos sacarlas y las vamos a sacar aquí.
Entonces, tenemos aquí el export y export.
Y así, allá donde queremos utilizar estas constantes, que tienen bastante sentido en nuestra aplicación,
las podremos importar fácilmente.
Tenemos el de dos turnos y tendremos el winner combos.
Y ya está.
Con esto ya tenemos más cositas que hemos sacado.
¿Qué más podríamos hacer?
En realidad, hay cosas que podríamos sacar incluso de...
Podríamos sacar de la parte del componente.
Fíjate que este check winner, si lo quisiéramos testear, es muy interesante porque no necesitamos nada visual aquí.
Aquí lo que tenemos es justamente un check winner que le pasas un board y hace este check a través del winner combos,
pero ya está.
O sea, que esto también lo podrías llegar a sacar.
Lo puedes sacar en un utis, lo puedes sacar donde tú quieras.
Igual podríamos tener también una carpeta logic, donde tengamos aquí board.js y aquí vamos a tener check winner.
Pues exportamos el check winner.
El check winner tenemos que importar el winner combos y el check winner from.
Vamos a poner from para que quede bien con el board, no sé qué.
Si no hay ganador.
Y esto lo podemos ya quitar de aquí.
Quitamos esto de aquí, importamos el import check winner from y el check winner ponemos este.
Ahora ya empezamos a utilizar menos cositas aquí.
¿Qué más podríamos hacer?
Podríamos hacer más componentes que, como podéis ver aquí, los componentes que hemos hecho es un poco regular.
Por ejemplo, toda la parte del winner, toda esta parte en realidad, la podríamos extraer.
Muy fácil.
Esta es la gracia de React, ¿no?
Componetizar.
Crear el winner.jsx.
Vamos a poner winner modal, ¿vale?
Porque tiene sentido que sea modal.
Y esta parte fácilmente nos vamos a nuestra aplicación.
Y esta parte quitamos...
Mira, esto es interesante, ¿no?
Hay veces que esto lo queremos dejar fuera o lo podemos dejar dentro.
En este caso, lo vamos a dejar dentro para que quede todavía más fácil, ¿no?
Vamos a poner aquí el winner modal, que ahora mismo todavía no lo estamos exportando.
Export con... bueno, function.
Winner modal.
Return.
Vale.
Y aquí fácilmente vamos a ver lo que necesitamos.
Fíjate que aquí empezamos con un renderizado condicional.
Lo que podemos hacer aquí es mejor decir, oye, si el winner es null, ya directamente me devuelve a null.
Y esto ya lo vamos a dejar para salir de la función antes.
Va a quedar mucho más claro, va a ser mucho más fácil de leer.
Ay, espérate.
Aquí se me ha escapado esto, que he hecho una mezcla, ¿vale?
Ya está.
Fíjate que con esto lo que estamos consiguiendo es, oye, si no tengo winner, devuélveme null.
Punto.
Ya está.
Más rápido.
Y esto me lo deja bastante más limpio, porque así no tenemos que ver, yo qué sé, si...
No estamos anidando mucho.
Es mucho más fácil de leer.
A ver, aquí vamos a poner winner text, ¿no?
Y el winner text pues también lo podemos dejar aquí.
Y así de esta forma esto lo podemos dejar mucho más limpio, ¿vale?
Esto es ya como buenas prácticas.
A mí me gusta mucho dejar la parte del renderizado lo más limpia posible.
Y también vamos a poder ver qué es lo que necesitamos para que esto funcione.
Obviamente necesitamos, por un lado, necesitamos el winner, necesitamos también el reset game.
Y necesitamos también, creo que ya está, ¿no?
El reset game, el winner y ya está, ¿verdad?
Vale.
Y entonces, como hemos hecho un export function y no un export default function, cuando importemos,
vamos a importar de forma nombrada, winner modal, desde los componentes estos.
Y ya empezamos a componentizar nuestra interfaz.
Le tenemos que pasar el reset game, reset game y el winner.
Y le pasamos el winner.
Y esto es algo que tendríamos que empezar a ir repitiendo constantemente y ver cuáles son las oportunidades que podemos ir separando, ¿vale?
Ya vemos que por ahora parece que todo funciona bien, no he roto nada, ¿vale?
Ah, mira, me he cargado el winner modal por algo, algo hecho ahí que no le ha gustado.
Square is... Ah, no he importado el square, ¿vale?
Este square, este de aquí, no lo he importado.
Pues nada, lo importamos.
Square y ya está.
Con esto, ahora sí.
A ver, ahora sí.
Ya lo tenemos.
Pero en un momento, o sea, no hemos tardado absolutamente nada, no hemos tardado absolutamente nada en cómo componentizarlo.
Obviamente, desde el principio tenemos que haber hecho así, ¿no?
Pero esto, la idea también era que pudieras ver cómo lo vamos haciendo paso a paso,
que lo puedas acompañar muy fácilmente y que luego veas cómo tienes que utilizar React para todo lo que pueda ser reutilizable,
lo puedas componentizar.
No de que metas todo en un componente y tal, sino que lo puedas componentizar, ¿vale?
Y aquí, por lo mismo, mira, este check-in game también, esto sería parte de la lógica del board.
Export const.
Lo bueno de esto es que este check-in game, como no tiene nada de...
Si lo puedes pasar por parámetro, tiene más sentido que lo separes directamente.
Porque piensa que esta lógica, ahora, esta lógica la puedes usar en un componente de React,
en uno de Angular, en uno de Vue, en uno de Svel, porque todos son JavaScript.
Entonces, esa es una de las grandes maravillas que tenemos.
El hecho de poder separar lógica, que es JavaScript puro, y directamente hacerlo que sea totalmente agnóstico al lenguaje o a la biblioteca que estamos utilizando de interfaz.
Esto es una muy buena práctica.
Muchas veces se nos pasa y dejamos nuestros componentes de React llenos de cosas que realmente podrían estar fuera.
Y que el día de mañana, si tú quieres hacer este mismo juego, el tic-tac-toe, en otro, en Solid 10, en Svelte, lo que sea, todo esto lo vas a poder reutilizar, porque es la misma lógica.
Así que eso es súper importante y estas cosas son las que marcan la diferencia, ¿vale?
Vale, entonces, este Check in Game, ya hemos visto que ahora, pues, lo podríamos sacar aquí del Check in Game.
Y, bueno, podríamos sacar más cosas, ¿no? Por ejemplo, generar un nuevo turno.
Todas estas cosas también se podrían llegar a hacer.
Pero, bueno, yo creo que por ahora esto también, el tablero, lo podrías sacar en un componente.
Este trabajo, este ejercicio ya os lo dejo a todos ustedes, a vosotros, porque creo que es muy interesante el hecho de que vayáis viendo cómo, pues, no sé, mejorar visualmente.
Podéis incluso hacer que esto sea multiplayer.
No es muy difícil con lo que hemos hecho.
Igual, mira, más adelante igual lo hacemos.
También os había dicho que íbamos a utilizar algo más bonito, porque es verdad, ¿veis?
Esta X y esta O.
No quería mezclaros al principio.
Pero fijaos lo bonito que es esto, que ahora, con todo lo que hemos hecho, si cambiamos esto aquí, solo cambiando esto aquí, con los símbolos bonitos, ¿vale?
En lugar de la X y la O utilizar los símbolos que le pertoca, si guardamos los cambios, fíjate que ya, vale, lo que le falta, porque veo, ah, no, ya está.
O sea, ya con esto, ya con esto, tenemos, ya con esto, solo con este cambio, o sea, tú aquí le podrías poner lo que quieras.
Y automáticamente, ya en toda tu aplicación, como hemos utilizado un enum, aquí puedes ver justamente la potencia de tener una constante y utilizar un enum.
¿Por qué?
Solo cambiando aquí estos dos strings, visualmente nuestra página cambia totalmente.
Cambia totalmente.
Y fíjate que ahora las X que estamos utilizando son mucho más bonitas, ¿vale?
Así que, fíjate, en un momento lo hemos hecho también.
Ahí lo tenéis.
Ahí lo tenéis, ¿vale?
Símbolos bonitos.
Ahí lo tenéis.
Podríais utilizar emojis si queréis, claro, también podéis utilizar emojis.
Y además que son de colores.
O sea que, circle, a ver si hay un circle, no sé si utilizar este, pero mira, ya, ¡ay!
Espérate, eso es que, pero ya está, lo tendríais así.
En un momento.
Pin, pam, pin, pam.
En un momentito.
Podéis utilizar emojis, podéis utilizar lo que queráis, ¿vale?
Lo que queráis.
Vale, os voy a subir estos cambios.
Ah, nos queda una cosa muy interesante que es la de guardar la partida, ¿vale?
Vamos a poner, vamos a ver, vamos a ver, extract two components tic-tac-toe.
Muy bien.
Vamos a poner esto.
Vale.
Vamos a hacer una cosa muy fácil para que entiendas la potencia del estado en React también, ¿vale?
Vale.
Cuando nosotros estamos jugando, si yo reseteo, pierdo los cambios.
Tiene sentido también.
O sea, tiene sentido porque, claro, no hemos hecho nada para que no los pierda.
¿Qué podemos hacer para asegurarnos que cada vez que actualizamos, si tenemos una partida a medias, pues podamos seguir jugando, ¿no?
O sea, que digamos, oye, no quiero perder los cambios.
Yo lo que quiero es tener aquí mis cambios de la partida que tenía antes.
Entonces, bueno, lo que vamos a hacer es que cada vez que haya un movimiento, vamos a hacer en Update Board este, cuando hacemos un movimiento, vamos a hacer una cosa.
¿Dónde deberíamos guardarlo?
A ver, podríamos guardar todo el estado cuando tenemos un ganador y tal, pero bueno, yo voy a hacer antes de saber si tenemos un ganador.
O sea, vamos a hacerlo justamente antes.
O sea, lo vamos a hacer aquí.
¿Por qué?
Vamos a dejar siempre que se quede en el último movimiento, por si acaso.
Vamos a guardar aquí partida, ¿no?
¿Por qué?
Porque tengo el nuevo turno, tengo el board.
¿Cómo vamos a hacer esto?
Con el local storage.
Vamos a llamar el local storage, hacemos setItem y le decimos que vamos a guardar el estado del tablero.
Importante.
En local storage lo que puedes guardar es un string.
Por lo tanto, si tú intentas guardar directamente el array, lo que te va a guardar no es lo que esperas.
Lo que te va a guardar, y lo podemos ver, es otra cosa.
¿Vale?
Tú puedes guardar esto, intentas guardar un array, ¿no?
A y B.
Y sí, te ha guardado algo, pero no...
¡Oh, ojo!
Que igual me ha guardado ahí.
Vale, no es lo que esperas.
¿Vale?
¿Ves que me lo ha guardado así?
Esto es porque ha llamado al método toString automáticamente.
Y tú cuando en un array llamas al método toString...
Ah, no sé si esto funcionará.
Pero vamos a probarlo.
Si tú llamas al toString, ¿ves?
Te lo convierte directamente en esto.
Te hace un join con las comas.
Vale.
Esto no lo queremos.
Así que...
¡Fuera!
Esto es lo que queremos.
Lo que queremos es que nos guarde un string y lo vamos a guardar el stringify.
Vamos a llamar del JSON stringify para que el array nos lo convierta en un string,
pero después lo podamos volver a transformar.
Y esto mismo lo vamos a hacer con el turno, para saber quién le tocaba el turno.
O sea, no vamos a volver la partida y que el turno se resetee.
No tiene sentido.
Así que vamos a guardar estos dos aquí.
Guardamos aquí la partida.
Ahora, ¿qué tenemos que hacer para saber que tenemos que leer esto?
Bueno, cuando inicializamos el estado, fíjate que estamos haciendo aquí array 9.fill.null.
No podemos...
Y os voy a explicar una cosa.
Está prohibido esto.
O sea, no podéis leer aquí el local storage y entonces aquí utilizarlo así.
Esto no está bien.
Tampoco está bien hacer esto.
Si tengo desde local stage, entonces voy a poner el estado aquí, así.
Si no, lo hago de otra forma.
Esto tampoco.
El useState, todos los hooks nunca jamás pueden estar dentro de un if.
Esto es súper importante.
¿Por qué?
¿Por qué es esto?
Porque React guarda la posición de cada useState en un array interno en memoria y dice, primero
se ejecuta este, luego se ejecuta este, luego se ejecuta este.
¿Qué pasa si lo pones en un if?
Pues que pierde las posiciones.
Dice, ostras, pero si primero se ejecutaba este, ¿por qué ahora se ejecuta este?
Entonces los va a mezclar.
Los useState siempre, siempre tienen que estar en el cuerpo de tu componente.
No puede estar dentro ni de un if, ni de un while, ni de un loop, ni de nada.
Tienen que estar en el cuerpo de la función.
Y la forma correcta que tenemos de leer el local storage y inicializar el estado, dependiendo
si tenemos local storage o no, es que en lugar de pasarle directamente el valor, le
podamos pasar aquí una función.
Le podemos pasar una función, ¿vale?
Y aquí lo que sí que podemos hacer es, oye, voy a recuperar el board from storage y esto
vamos a hacer windowLockerStorage.getItem y entonces vamos a devolver que si tengo board
from storage, vamos a hacer un json.parse de lo que tenía en el storage para que esto
sea con lo que inicializamos el estado.
Esto sería el valor inicial de nuestro estado y si no, utilizamos el valor por defecto que
tenemos, ¿vale?
Esto sería.
Lo podéis hacer de otra forma, si no os gusta las ternarias o nada, se entendéis.
Podéis hacerlo con un if.
Si tengo from storage, hago un return del json.parse board from storage y si no, devuelvo
el array 9 fill null.
Total, como arriba hemos puesto un return, como aquí hemos puesto un return, no ejecutará
esta línea de aquí.
Cualquiera de las dos funciona sin problemas, ¿vale?
Cualquiera de las dos.
¿Por qué esto lo hacemos dentro de esta función?
Vale.
Esto es súper importante y también mucha gente no lo sabe.
Vamos a poner aquí un console.log y le voy a llamar inicializar estado, ¿vale?
Y vamos a poner aquí un console.log que le vamos a llamar render, ¿vale?
Inicializar estado del bot le vamos a llamar, para que lo veas claro.
Vale.
Bueno, ahora aquí alguna cosa me he petado.
Vamos a ver que me he cargado.
Ah, it's not valid.
Ah, hostia.
Es que he guardado.
Es porque he guardado en el local storage lo que, window, local storage, he guardado
lo que no deberíamos haber guardado.
Remove item board, ¿vale?
Para empezar de cero.
Vale, ahora.
Entonces, ahora tengo esto, refresco y fíjate que se ha quedado esto.
Si miramos la consola, vamos a ver render, inicializar estado del board, ¿vale?
Render, inicializar estado del board.
Si yo me pongo render, render, o sea, cada vez que se renderiza, ejecutamos este console.log.
¿Qué significa?
¿Por qué es importante esto?
Porque si tú pones este local storage get item, lo pones fuera, se va a ejecutar,
se va a ejecutar esto de aquí, en cada render.
La línea 11 se va a ejecutar en cada render de forma innecesaria.
Y esto es lento.
El leer del local storage es muy lento.
Es súper lento, es síncrono y bloquea.
Por lo tanto, vamos a intentar evitar que en cada render vuelva a leer el local storage
cuando no lo necesita.
Si tú haces esto, si tú haces esto en cada render, va a leer el local storage.
Entonces, ya sabemos que podemos inicializar el estado pasándole el valor directamente,
pero en este caso, como queremos que la inicialización, porque ya sabéis,
la inicialización solo ocurre una vez, pues podemos pasarle una función que solo se va a ejecutar una vez,
porque la inicialización del estado solo ocurre una vez, y entonces dentro vamos a leer el local storage.
Así nos evitamos que nuestros componentes sean más lentos, lo hacemos aquí una vez, y ya está.
Así que vamos a seguir la misma técnica, la vamos a seguir con los turnos, ¿no?
Vamos a decirle, bueno, vamos a tener aquí turn from storage window.local storage.
En este caso, como directamente es un string, pues no hay ningún problema.
Podríamos hacer return turn from storage, y si no, turns.x.
Ya está. Si tengo algo desde el storage, utilizo el de storage.
Si es null o undefined, utilizo el por defecto, que es turns.x.
Todavía nos falta una cosita más, un detallito, ¿no?
Porque, claro, si yo lo doy al reset del juego y refresco,
ves que me ha vuelto a guardar lo que tenía el local storage.
Tenemos que asegurarnos que cuando reseteamos el juego,
también reseteamos lo que tenemos en el local storage.
Así que vamos al local storage, remove item board,
y también lo mismo lo hacemos con el turn.
Y ya está. Ahora, cuando reseteamos el juego, si refrescamos,
vemos que sí que se resetea.
Y si jugamos y lo dejamos por lo que sea, medias, y refrescamos,
pues vuelve a estar aquí nuestra partida preparada para que empecemos a jugar.
¿Vale?
Así que ya tenemos aquí nuestro primer jueguecito con el tic-tac-toe,
con todo el tema de Riaq.
Preguntas que tenga la gente, mis amigos, ¿qué tal?
¿Qué preguntas tienen?
Nicolás dice...
¡Fua, Nicolás! He explicado esto tantas veces, Nicolás.
De hecho, es que lo expliqué ayer.
Que si esto y esto es lo mismo.
No son lo mismo, obviamente, ¿no?
Esto...
Ah, sí, faltaba guardar el...
Ahora lo arreglo.
Esto, mira si esto es falsi.
Y esto, mira si esto es null o undefined, ¿vale?
Vale, el turno se me ha olvidado porque...
Ah, no, pues el turno aquí está bien.
Turno, turno, getItemTurn.
TurnoX, si lo tenemos de storage...
Pues no sé, ¿dónde se me ha quedado?
Hola, aquí estamos, ¿qué tal?
Y si creo que el usuario elija el contenido de los cuadros,
pues también lo podrías hacer, pero bueno, habría que hacer más cosas.
Lo podríamos hacer, pero habría que hacer más cosas
y tenemos que explicar algunas cositas, ¿no?
El turno no ha funcionado bien.
Puede ser...
A ver, vamos a ver.
Sí, el turno no ha funcionado bien.
Vamos a ver aquí el getItemTurn.
Cuando...
Ah, es que aquí...
Es que aquí esto...
Tenemos que guardar el nuevo turno.
He guardado el anterior, ¿vale?
Ahora ya está, ahora ya está.
¿Ves?
Ahora sí, ahora sí.
¿Ves?
Estoy refrescando, refresco y ahora sí que se queda bien.
Aquí me había equivocado y estaba guardando el anterior y no el correcto, ¿vale?
Ahora sí que está funcionando bien, ¿vale?
Mira, y empate.
Empezar de nuevo, refrescamos.
Ahora sí, ¿eh?
Midu, siempre que tenemos que cortar una función para que no siga ejecutando código,
si al cumplir la condición quiere devolver un alert, ¿está mal hacer un return alert?
¿Un alert?
Pregunto esto porque muchas veces escuché que el return se utiliza para recuperar un dato específico
y no tanto va a ejecutar algo como un alert o un console lock.
A ver, el alert es que bloquea...
Yo no os recomiendo que utilicéis nunca alert, a no ser que sea para probar algo,
porque lo que vais a encontraros es que el alert bloquea la ejecución del código.
Yo no os lo recomiendo, ¿eh?
Si quieres hacer un multiplayer, pues a ver, puedes utilizar una base de datos.
Es que esto que hemos hecho en local storage, esto lo podríamos hacer en otros sitios, ¿no?
Vaya, no me sabía lo del estado asíncrono.
Pues es bastante importante en React, ¿eh?
Hay otra forma de guardar estado al recargar la página o siempre se usa así.
En este caso hemos utilizado local storage, lo podríamos guardar en una base de datos,
podríamos hacer, podríamos pedir esto a la base de datos otra vez en el useEffect,
lo vamos a ver ahora también.
¿Podrías explicar la diferencia entre spread y el rest?
Hoy no, porque estamos ahora con React y creo que ahora no es interesante.
¿Puedes limpiar local storage.clean?
Es una práctica regular, ¿eh?
Javier.
Porque, a ver, en este caso sí que funcionaría, lo tenemos muy controlado,
pero yo creo que es buena práctica que borréis específicamente lo que queréis,
porque si no vais a borrar demasiadas cosas quizás.
¿Puedes mover el getItem afuera del componente en este caso?
Claro que sí.
Podría, esto por ejemplo, podríamos guardar aquí en logic,
podríamos tener storage, podríamos tener aquí un index.js,
podríamos hacer export const save game.
Y aquí le podríamos pasar la información que queremos guardar, ¿no?
Por ejemplo, queremos guardar tanto el board como el turno, ¿no?
Pues esto lo puedes tener fuera.
Esto lo sacas de aquí y lo tienes aquí.
Y también export game to storage, reset game storage.
Y esto, pues, window.local storage, esto, el reset.
Reset game.
Y así, pues, podéis empezar a hacer esto y sacarlo de ahí.
¿Que el día de mañana lo hacéis en otro sitio?
Bueno, pues aquí podéis empezar a hacer las mismas llamadas y ya está.
Y esto, pues, lo importáis.
Import save game storage, reset game storage.
Y entonces, en el reset game storage, llamáis esto aquí, quitáis esto de aquí.
Y aquí, guardar la partida, save game to storage.
Tendríamos el board, el board que sería el JSON stringify del new board.
Así que lo ponemos aquí.
El JSON stringify incluso lo podéis hacer dentro.
O sea que, mira, vamos a hacerlo dentro.
Creo que tiene más sentido.
Esto lo vamos a hacer dentro para que no tenga que el componente preocuparse de tener que hacer eso.
Pues, si el día de mañana queréis hacer esto, mira, ya está board.
Este ponemos board.
Esto turn.
Y ya hemos hecho aquí, pues, otra cosa, ¿no?
Tenemos ahora, pues, un set game to storage, reset game storage.
Y lo tenemos aquí.
Y esto debería funcionar exactamente igual.
Y funciona igual.
Bueno, ahora la he liado otra vez porque...
Ah, ¿ves?
Otra vez.
El turn, este que habíamos puesto aquí, otra vez está mal porque le tengo que pasar el new turn.
¿Ves?
New turn.
Pero ya está.
Con esto ahora ya lo hemos refactorizado en un momento.
¿Vale?
Y ya está.
Ya lo tendríamos.
Lo del estado síncrono es muy frecuente.
A mí me sucedió que un formulario me quedaba siempre el último carácter pendiente.
Hoy veremos lo de Zustan.
No, eso lo veremos más adelante.
Me refiero a hacer el gateintent antes de montar el componente afuera.
No.
Lo podrías hacer, pero no tiene sentido.
Tiene sentido que lo hagas justamente al inicializar el estado.
Midu.
Con lo cual, siempre que se tenga algo de duda, se ha de poner dentro del estado y jamás fuera del estado.
A ver.
A ver.
Hay que tener en cuenta una cosa.
Ahora mismo estamos ejecutando React solo en el cliente.
Entonces, vamos a quedarnos con el scope de lo que estamos haciendo ahora.
No podéis, si ejecutáis React, los componentes de React en el servidor, no podéis acceder a window.localStorage.
Y por lo tanto, lo que hemos hecho no funcionaría en el servidor.
Pero más adelante veremos si está en el localStorage y tenemos servidor, cómo tenemos que recuperarlo correctamente.
Pero hay que tener en cuenta esto.
Esto es porque ahora solo estamos ejecutando en el cliente.
¿Hacer un ajedrez cuándo?
Pues dale cañita.
O sea, cuando tú quieras.
Mueve las manos.
¿Dónde entra aquí el useEffect?
Todavía no entra a ningún sitio.
¿Cómo se llama el programa?
¿Dónde haces eso?
Visual Studio Code.
¿Podrías explicar de nuevo los callbacks dentro del useState, por favor?
Ostras.
Entonces, el callback dentro del useState básicamente es una función que tiene que devolver el valor con el que quieres inicializar el estado.
Y ya está.
Eso es lo que es.
¿Vale?
El año pasado hice un Conecta4.
Claro, es que con lo que hemos hecho hoy ya podéis empezar a hacer un Conecta4.
Podéis empezar a hacer un montón de cosas.
No conocía Bit.
¿Qué diferencia hay de Create Recap?
Que uno está hecho con Webpack, otro con Bit.
Y que Bit funciona bastante más rápido.
Y un montón de diferencias más que obviamente no nos da tiempo a explicar aquí.
Vale.