Podemos utilizar el poder del hook useReducer
para aplicar el patrón de diseño state reducer. Este patrón de diseño permite ofrecer al usuario, mayor control sobre el componente, permitiéndole acceder al estado interno para aplicar algún requerimiento.
El componente ofrece una API para acceder a su estado e internamente maneja la unión de ambos reducer, el de usuario y el interno. Además en este ejemplo, se permite al usuario definir el estado inicial del componente, otorgando así aún más flexibilidad al utilizar el componente Wizard.
Matías Hernández: [0:00] Ahora que nuestro componente utiliza el hook userReducer ya podemos aplicar el patrón stateReducer. La idea es permitirle al usuario acceder al estado del componente. Para esto, primero definiremos la API necesaria, permitiremos al usuario definir el estado inicial y un reducer personalizado que pasará como prop.
[0:21] En el estado inicial definiremos que el índice activo será el número 2, es decir, la tercera página. El reducer capturará el actions.NEXT_PAGE y simplemente escribirá en la consola y finalmente retornará el estado directamente.
[0:34] Pasaremos estos valores como props en nuestro componente principal. Reducer será reducer y ahora en la definición de nuestro componente principal recibiremos estas nuevas props agregando reducer e initialState al listado y pasaremos estos valores al hook useReducer directamente.
[0:55] Renombramos este initialState a defaultInitialState para evitar confusión de nombres y podemos ver que nuestro componente ahora inicia en la tercera página, pero al hacer click en el botón Atrás nada pasa.
[1:07] Esto es porque estamos utilizando el reducer definido por el usuario que no modifica el estado. Lo que necesitamos es que nuestro reducer llamado defaultReducer siga funcionando y además tome el reducer del usuario, es decir, necesitamos combinar ambas funciones.
[1:25] Para lograr esto, crearemos una función sencilla que combinará la llamada a estas funciones reducer. Llamaremos a estas función combinReducer(). CombinReducer aceptará un número indefinido de funciones, así que utilizaremos la sintaxis spread y retornará un nuevo reducer que recibe un estado y una acción.
[1:49] La variable reducers pasada como argumento es un arreglo, por lo que podemos utilizar el método [inaudible] reduce para combinar sus elementos. Reduce recibe dos argumentos, una función reductora y un valor inicial.
[2:06] La función reductora tiene dos argumentos a su vez, el acumulador y el elemento actual que llamaremos nextReducer. El estado inicial de reduce será en este caso el valor de state. Aquí lo que retornaremos será el nuevo estado creado por el reducer actual, es decir, ejecutaremos nextReducer con el estado actual indicado por el acumulador y la acción con la que fue llamado.
[2:33] Ahora ya podemos utilizar esta función para combinar nuestros reducers. Reemplazaremos aquí el uso de la prop reducer por una llamada a combineReducers que recibirá primero nuestro reducer interno, llamado defaultReducer y luego el reducer del usuario. Ahora nuestro componente funciona correctamente.
[2:54] Pero nos queda un caso que resolver. ¿Qué ocurre si el usuario no utiliza estas props? Tenemos un error, y esto es porque la prop reducer no está definida. Esto lo podemos resolver simplemente asignando un valor por defecto al que llamaremos defaultReducer. Este reducer debe ser un reducer simple, pero que no duplique la lógica que ya tenemos.
[3:18] Para evitar esta duplicación de lógica, renombremos nuestro actual defaultReducer como wizardReduer y crearemos un nuevo defaultReducer como una simple función que recibe un estado y una acción y retorna directamente el estado.
[3:35] Ahora aún tenemos un error y eso es porque initialState tampoco está definido. Podemos pasar un valor por defecto lo que resuelve el problema, pero ¿qué pasa si el usuario agrega un estado inicial? En este caso no vemos el problema porque nuestro estado es sencillo, pero el estado definido por el usuario sobrescribe al nuestro.
[3:54] Aquí una buena idea es combinar el estado por defecto con el definido por el usuario. Para eso utilizaremos la sintaxis spread en defaultInitialState y en la prop initialState expandiendo sus valores. Finalmente cambiamos el valor por defecto de la prop pasada por el usuario a un objeto vacío.
[4:14] Ahora nuestro componente permite al usuario acceder al estado pasando un valor inicial de estado y una nueva función reducer que capture las diferentes acciones.
[4:22] Al hacer click en el botón siguiente, podemos ver el reducer del usuario en acción. También permitimos al usuario definir el estado inicial de nuestro componente al exponer la prop initialState. Este [inaudible] se conoce como estate reducer y es una forma del patrón de inversión de control.