Best Practices

Unlocking the Power of React Custom Hooks

Simplify your code, enhance code reusability, and follow coding standards
  • LinkedIn
  • YouTube

Introduction

Story Image

Working on a large project may sometimes be overwhelming, like understanding the application architecture, reading the documentations, learning the codebase, and following coding standards. Even though there’s a project onboarding, this is not enough for newly joined members to completely adhere to the common practices of the project. But being able to implement these common practices as early as possible will be beneficial for both you and the team. In this article we’ll be focusing on one aspect of following coding standards, implementation of custom hooks

Each codebase has specific coding standards, in a React project codebase hooks provide a more concise and functional way of handling state and side effects in the application. There are built-in hooks provided by React, we have state hooks, which are useState and useReducer that lets our component to remember information, we have the useContext hook that lets a component receive information from distant parents without props drilling, we also have useEffect hook that lets us manage side effects to our components such as data fetching, DOM manipulation, etc. There are quite a number of built-in react hooks, to check the others you can visit the official React documentation. It is also possible and a very common practice to combine these built-in hooks to create more complex and reusable hooks. These are commonly called custom hooks.

Built-in React hook usage: useState

Let’s first look at the React useState hook in action. A very common example of useState is a theme toggler. We’ll write it using the useState and eventually convert it to a custom hook.

Story Image

Figure 1: ThemeToggler function component utilizing useState hook to remember the state and able to update the isDarkTheme using the setIsDarkTheme updater function.

Story Image

Figure 2: Rendered button and click action of the ThemeToggler component.

Upon clicking the “Toggle theme” button, we can see that the background changes. 😂 From the example above, we have function component named ThemeToggler that is using the useState hook, the hook contains the declared state, in this case the state is assigned to a variable isDarkTheme that is set with initial state of boolean false, and the updater function, declared as setIsDarkTheme. We also have the toggleTheme handler function that updates the state when the button is clicked. In our example, initially the state was set to false, when the “Toggle theme” button is clicked the state will update to be the opposite of the previous state value, from false to true or from true to false. 

Extracting logic to a custom hook: useDarkTheme

If for example this kind of behavior will be implemented in multiple places, we don’t want to rewrite this logic every time we need it. What we can do is to extract this logic and create a custom hook for it. We can create a separate file and name it useDarkTheme.ts that contains something like this:

Story Image

Figure 3: Code snippet for useDarkTheme custom hook.

And we’ll also update our ThemeToggler component to use this hook, it will be something as below:

Story Image

Figure 4: Updated ThemeToggler component that is using the useDarkTheme custom hook.

In the code snippet in Figure 3, we can see that the body of the code block is just the useState declaration and handler function in Figure 1. The only difference is that in our custom hook we return the state and the handler function that updates the state. Really, we just extracted it to a new file. 😂 With this implementation we can reuse the useDarkTheme hook to everywhere that needs to be. We don’t have to rewrite the useState statement every time we need it. This is just a very simple example implementation of a custom hook, we can also have a combination of built-in React hooks for more complex logic. Let’s see another example, where we fetch data from a server.

Custom hook useFetch: useState and useEffect in action

Now let’s see this piece of code we have:

Story Image

Figure 5:  Implementation of useFetch hook using useState and useEffect.

The idea is that we have multiple instances where we fetch different kinds of data, could be product or user list, specific product or user info, etc. We declared three states called data, loading, and error. The data will contain the data response from the API fetch call, loading is the state when the fetch is ongoing or completed, and error will contain error message when the API call is not successful. In the useEffect block, we defined a fetchData function where we set the loading to be true, just to make sure that the loading is true, same goes with the error. Then, we have the try…catch block where the fetch is happening. We have the fetch method accepting the url as the parameter where we are getting the data. In the if…else statement, we are going to check the status of response.ok, if it’s true, we set the data to be the response we got from the fetch call otherwise, we throw an error that will then be caught in the catch block. In the catch block we just have an error logging and of course the updater function for our error state. And in the end, finally block, we just set the loading to false indicating the process is completed. This fetchData function will then be called inside the useEffect block. We then return the data, loading, and error states.

Now, time to use this hook, it will look something like this:

Story Image

Figure 6: Usage of useFetch hook

Just like the useDarkTheme hook, we deconstruct the return states we have in the useFetch hook. We also provide the url that we will get the data from. We can now then use these states to conditionally render elements in our component. While the loading is true, render a paragraph element indicating that the fetch is ongoing, same goes when there’s an error. And last, we check the length of data, if it is not 0 and there is no error, we render a list of names. Let’s see it in action.

Story Image

Figure 7: Rendered component using the useFetch hook.

Conclusion

Phew, that was long! Coming from Node.js background I had hard time adjusting with this kind of practice. But as time goes by and bug/feature tickets continue to flow, 😂 time will come that this practice will become a mindset when developing React applications. So, with this practice we’ll be able to practice code reusability, simpler components, and we can adhere to the team coding standards practice resulting in code consistency in the codebase which benefits the team and of course yourself. 😎

About the Contributor

Discuss this topic with an expert.