Podemos utilizar el hook useReducer para agregar una nueva capa de flexibilidad a nuestro componente al aplicar el patrón de diseño state reducer que permitirá al usuario del componente acceder al estado del mismo.
Para lograr esto modificaremos el componente para utilizar useReducer. En este camino nos encontraremos con un bug que podremos resolver al utilizar el hook useCallback para evitar infinitos re-renders del componente.
Instructor: [0:00] Hemos flexibilizado el uso del componente Wizard, permitiendo al usuario definir cómo renderizar sus componentes. Pero podemos ir un paso más allá, permitiendo que decida cómo manejar el estado del componente.
[0:14] Este patrón de diseño se conoce como State Reducer y es un tipo de inversión de control. Para lograr esto, primero tenemos que agregar lógica a nuestro estado para permitir su modificación desde el exterior. Pero al utilizar useState, esa lógica se vuelve engorrosa.
[0:31] Una mejor opción es usar el hook useReducer. UseReducer utiliza el patrón reducer, que no es más que una función que permite modificar el estado del componente en base a una acción determinada. También utiliza el estado inicial, al que llamaremos defaultInitialState. Crearemos nuestra función reducer, la llamaremos defaultReducer. Esta función recibe el estado y una acción.
[0:55] Luego, deconstruimos el estado para tener acceso al valor activePageIndex. Ahora, utilizaremos un simple switch para actuar sobre las distintas acciones. Para esto, definiremos un objeto con los nombres de las acciones.
¿[1:07] Qué acciones tenemos que definir? Una acción para ir a la siguiente página, otra para ir a la página previa y, finalmente, una para definir el número de steps o pages a renderizar. Ahora, utilizaremos estas acciones para definir nuestros casos.
[1:24] Si la acción realizada es NEXT_PAGE, entonces retornamos una copia del estado utilizando la sintaxis spread y modificamos el valor activePageIndex sumándole uno. En caso de ser la acción PREV_PAGE, solo debemos modificar activePageIndex sustrayendo uno.
[1:45] Finalmente, para el caso SET_STEPS retornamos el estado y definimos el valor del atributo steps utilizando el valor obtenido desde el payload de la acción.
[1:58] Ahora, en el componente principal, reemplazamos el uso de useState por useReducer, que retorna una tupla cuyo primer valor es el estado y, el segundo, una función llamada dispatch que permite lanzar las acciones. Utilizaremos esta función dispatch para modificar nuestras funciones.
[2:19] En la función goNextPage reemplazamos nuestro código por una llamada a dispatch, que recibe un objeto con un atributo type cuyo valor será actions.NEXT_PAGE. En la función goPrevPage haremos lo mismo, pero la acción a utilizar será actions.PREV_PAGE.
[2:40] Nos queda definir la función setSteps. Creamos esta función que recibe como argumento un valor n y llamamos a dispatch con un type actions.SET_STEPS y un atributo payload con el valor de n.
[2:55] Ahora nuestro componente emite un error. Se renderiza múltiples veces, esto ocurre porque nuestro componente WizardsPages, la llamada al hook useEffect se ejecuta múltiples veces, ya que la lista de dependencias cambia constantemente. En esta lista tenemos setSteps, que es la función que acabamos de definir en Wizard.
[3:15] En cada nuevo renderizado esta función cambia. Para evitar esto podemos hacer uso de un nuevo hook, React.useCallback, que recibe como parámetros una función, que será nuestra definición de setSteps y una lista de dependencias, que en este caso, es solo la función dispatch, que React no se asegura que es idempotente, es decir, no cambiará.
[3:39] Ahora nuestro componente Wizard funciona correctamente utilizando el hook useReducer, que recibe una función reductora y un valor para el estado inicial. La función reductora recibe un estado y un objeto que define la acción y retorna el nuevo estado.
[3:53] Además, utilizamos el hook useCallback para optimizar el comportamiento del renderizado de nuestro componente al memoizar la función. Es decir, la función solo es redefinida si una de sus dependencias cambia.
[4:05] Ahora el código de nuestro componente está listo para aplicar el patrón de diseño State Reducer.