Nuestro componente Wizard carece de flexibilidad de cara al usuario, no permite modificar la forma en que sus componentes se renderizan. Pero podemos refactorizar su código para otorgar esta flexibilidad mediante el uso del patron de diseño conocido como componentes compuestos y un estado compartido entre ellos. Para compartir implícitamente este estado utilizamos la API Context de React junto al hook useHook
que ofrece una forma sencilla y directa para consumir este estado compartido.
Con esto el componente entrega flexibilidad al usuario permitiéndole decidir cómo renderizar cada elemento del wizard.
Instructor: [0:00] Tenemos aquí un simple componente Wizard, que utiliza useState para manejar el estado y tiene una API como ésta, que simplemente recibe un cierto número de páginas a renderizar.
[0:09] Este componente no es muy flexible ya que define por sí mismo la forma de renderizar cada uno de los ítems del componente. Esto puede mejorarse utilizando componentes compuestos. Para eso primero definiremos la API que deseamos y utilizaremos un formato básicamente común que es el de nombres compuestos utilizando puntos.
[0:23] Definiremos un sector de nuestro componente para renderizar las páginas y renderizaremos los lugares por separado. Una vez definida la API, comenzaremos a refactorizar nuestro código. Lo primero es, moveremos estos botones que están aquí dentro de Wizard como componentes individuales y crearemos nuestro nuevo componente Pages, que por ahora, simplemente retornará children.
[0:46] Podemos ver fácilmente que hay algunos valores que faltan y que debemos definir cómo obtenerlos. Estos valores provienen desde el componente principal y del estado. Una forma de compartir el estado entre componentes relacionados es utilizar la API Context. Para eso crearemos un nuevo contexto que llamaremos WizardContext y que se obtiene utilizando React.createContext.
[1:05] Luego definimos el componente Context como padre de todo el árbol de componentes WizardContext.Provider. Este componente recibe un valor que definiremos como la variable context que mantendrá activePageIndex y las dos funciones relacionadas con los botones. Lo pasamos como valor de contexto y ahora podremos acceder a estos valores desde cualquier componente y consuma este contexto.
[1:27] Para consumir los valores compartidos del contexto de nuestros botones, utilizaremos el hook useContext que recibe como parámetro el contexto del que queremos consumir, y retornará un objeto con todo el context.
[1:37] En este caso nosotros queremos acceder solo a activePageIndex y goPrevPage. Podemos usar destructuring para acceder solo a esto valores. Haremos lo mismo con el botón Next, pero en este caso necesitamos acceder a NextPage.
[1:48] También es necesario conocer el valor total de páginas que estamos utilizando para tener este límite superior. Como no lo conocemos y no corresponde definirlo en el botón, lo obtendremos del contexto.
[1:58] La forma más fácil de conocer el total de páginas a renderizar es preguntárselo al usuario del componente, así que pasaremos steps como una prop desde acá donde se va a definir el uso del componente.
[2:07] Ahora tenemos que definir qué cosas se renderizarán en el componente principal. Los botones los tenemos renderizados como componentes independientes, por lo tanto, podemos eliminarlos desde el componente principal. La función de wizard_content está siendo realizada por nuestro nuevo Pages, es por eso que lo podemos remover y simplemente renderizar children.
[2:24] Ahora bien, dentro de este componente Pages, utilizaremos la misma estrategia anterior para obtener la página que corresponde renderizar. Este valor de currentPage depende de activePageIndex que proviene desde el contexto. Retornamos componente. Ahora lo que nos falta es definir estos nombres de componente para obtener nuestra API completa.
[2:42] Para eso simplemente haremos uso de un formato tipo alias que nos permitirá definir nuestros nuevos componentes compuestos. Normalmente, en término de estilo, tenemos el control directamente al momento de renderizar.
[2:52] Vamos a utilizar el div que teníamos definido con anterioridad. Tenemos nuestro componente Wizard funcionando igual que antes, pero utilizando componentes compuestos lo que nos permite mayor flexibilidad a la hora de utilizarlo.
[3:02] Para esto, utilizamos React Context, para compartir valores de estado definidos en el componente principal y utilizamos el hook useContext para consumir dichos valores.