The React hooks API allows us to build our own hooks using function composition, giving us the power to share logic and features in a simple way among different components. This also helps us expose a better API for the components we build, empowering the user to decide how to use and render the data without forcing them with styling or structure decisions shipped with the component. In this model, the component only encapsulates the required logic to work.
Matías Hernández: [0:00] We have our Wizard component that allow the user of it to access its internal state by using the state reducer pattern. Now, we will take advantage of the custom hook. We'll update how the component is used. We will start by creating the API we want.
[0:15] I would like to have a navigation section that contains the buttons so we will create a component that we will call Navigation. Now, we will take this container, wizard.pages, and we'll refactor it to have another content and name. It will call just Pages. Finally, I would like to have a progress indicator that I will call Progress.
[0:37] Now, we must create these components starting with the navigation that will show the buttons and return a container with the "wizard_buttons" class. This will contain two buttons, one to go to the next action and one to trigger the go back action, each with the respective class names.
[0:53] Now, we create a container of the pages that we have called Pages that receives this children prop and returns a container with the class name "wizard_content". This component will only render the children prop. Finally, we create a ProgressBar component that will be a container with className "wizard_progress" that will render the text Step 1 of X.
[1:35] The first hook we will create is useWizardNavigation for the Navigation component which will simply expose some necessary state functions for that. We will use the destructuring syntax on useWizardContext and obtain goNextPage, goPrevPage, activePageIndex, and step state variables. Then, we will just return this object.
[1:57] Now, we create the hook for the Pages container. This component needs to know the active index to know what to render. We'll call it useWizardPages. It will return the value of activePageIndex. We will get this value from the context using useWizardContext.
[2:14] This hook will also do the work that our old WizardPages does by calling useEffect to define the number of steps. We will copy this piece of code and paste it into our new hook.
[2:28] Now, we add setSteps. The value of steps is received as an argument of the hook that we will call totalSteps. We'll replace here a dependency. Finally, we create a hook for the ProgressBar that we will call useWizardProgress. Here, what we need to know is the index we are in and the total number of steps. We will simply obtain these values from the context and return them.
[2:54] Now, we need to use these new hooks into the App.js file. We just import the new hooks and configure their usage. We import useWizardNavigation, useWizardPages, and useWizardProgress. Here, we have a small bug. We forgot to export the useWizardProgress hook.
[3:11] We use useWizardNavigation for the Navigation component. We get all the values that this hook returns. In the Previous button, we will need the action to execute when we click it. We pass the goPrevPage function on the onClick handler. We will do the same for the Next button using goNextPage.
[3:29] Now, we can add logic to disabled button. The Previous button is disabled when the currentIndex is . The Next button is disabled when the currentIndex is equal to the number of steps. As the index starts from , we do steps - 1.
[3:44] Now, we go to the Pages container where we will use the hook useWizardPages. We pass the number of pages to use. We will use the React.Childrencount() method to obtain the number of steps automatically.
[3:56] This hook returns the active index to decide what to render. Again, we will use React.Children.toArray to convert the children prop into an array, Thus, access its elements using the activePageIndex.
[4:11] Finally, we use useWizardProgress inside ProgressBar component. We get the currentIndex together with its total steps. We use these values to render this message. We change the 1 for activePageIndex and the X for steps. There is a little bug here. We fix it. Now, we have our wizard working.
[4:31] We can make a slight improvement in our hooks. We can use null-yes in activePageIndex to give it more readability. In Wizard.js, we change activePageIndex by the name currentIndex. We associate the value activePageIndex + 1. Now, in App.js, we change the reference to currentIndex. We change here too.
[4:53] Now, we update this comparison and currentIndex, We compare it with the total value of steps. We go back to the Wizard.js file. We do the same managed logic in useWizardProgress. We go back to the ProgressBar component. We change the reference as well.
[5:08] Now, we have our wizard working, but there is a little bug here. If you click the Previous button, it is possible to pass the lower limit. This is why we make the comparison with an index , but current index starts at 1. We just change it. It works correctly.
[5:23] In summary, we had created some custom hooks to offer new ways of using the component. Thus, making the API more flexible, allowing the user to implement an interface not tied to the opinions of the component.