React Hooks Explained – Part Two
We have recently gone through React Hooks in the first part of this series, which transformed the React development patterns and the generic ecosystem. We think of hooks as functions that allow React developers to use life-cycle methods and manage the state within functional components. Hooks offer developers to get the power to use functional components for almost any need, from rendering the UI component to handling the state and the component logic. Let's remind ourselves three React Hooks core principles.
- Make sure to not use Hooks inside loops, conditions, or nested functions;
- Only use Hooks at the top level of React Functional Component;
- Always start a Hook with the name of "use", because you are using the super powers of the React library
Make sure you have familiarized yourself and feel confident with the detailed hooks description from Part One before you move on! We will go through some really scary ones this time (just kidding, this will be fun).
This hook is often referred to as one of the scariest, while in reality, it is just a conceptually different state management way. The state management logic is a different concern that should be managed in a separate place.
React provides the
useReducer hook to separate the concerns (rendering and state management). The hook does so by extracting the state management out of the component. And yes, it is that simple!
This hook returns an array of two values just like the
useState hook, where the first value is the reactive state that you want to show in the UI.
However, the second value is where things get different. Now you need the function that can dispatch the action instead of the state update how it was with the
useState hook. Heads up! An action is just an object that has the type, which can be any string you want and an optional data payload.
You might dispatch an action when the button is clicked, which will trigger your reducer function. The reducer function is something you define and then pass as an argument to the
useReducer hook. The function takes the current state and the action as arguments, and uses these two values to compute the next state which is usually handled inside the switch statement. Lastly, the hook takes the initial state as it's second argument.
Despite being able to use the
useReducer Hook to handle complex state logic in our app, it’s important to note that there are certainly some scenarios where a third-party state management library like Redux may be a better option. Generally, as was stated before, this hook provides more predictable state transitions than
useState, which becomes more important when state changes become so complex that you want to have one place to manage state, like the render function.
As your application grows in size, you’ll most likely deal with more complex state transitions, at which point you’ll be better off using
useReducer. And yes, this scary hooks is that simple!
useImperativeHandle Hook customizes the instance value that is exposed to parent components when using
ref. As always, imperative code using refs should be avoided in most cases. The Hook is similar to but it lets you do two things:
- It gives you control over the value that is returned. Instead of returning the instance element, you explicitly state what the return value will be (see snippet below).
- It allows you to replace native functions (such as blur, focus, etc) with functions of your own, thus allowing side-effects to the normal behaviour or a different behaviour altogether. Though, you can call the function whatever you like.
Imagine you build a reusable component library in React, you may need to get access to the underlying DOM element and then forward it, so it can be accessed by the consumers of your component library. You can access a native DOM element with the
useRef hook that we came across earlier, then you can wrap the component in
forwardRef (forward reference to the element) to make the
ref available when someone uses this component.
When you need a bidirectional data and logic flow, but you don’t want to overcomplicate things by introducing state management libraries, the
useImperativeHandle Hook is a great choice.
For example, I once used the
useImperativeHandle Hook when I needed to open a modal component when a button in the parent component was clicked.
Defining state in the parent component would cause the parent component and its children to re-render each time the modal button was clicked, therefore, I wanted the state in the child component. Instead, I stored the modal state in the
Modal component using
Do you remember the
useEffect Hook, which also happens to be one of the most confusing for junior developers to catch up with? The
useLayoutEffect Hook works just like the
useEffect Hook since their signatures are identical, but it fires synchronously after all DOM mutations.
The above means the code will run after rendering the component but before the actual updates have been painted on the screen. Now React will wait for your code to finish running before it updates the UI.
This can be useful if you need to make DOM measurements (like getting the scroll position or other styles for an element) and then make DOM mutations or trigger a synchronous re-render by updating the state.
Heads up! Use the
useLayoutEffect Hook if you need to mutate the DOM and/or do need to perform measurements, or try
useEffect if you don't need to interact with the DOM at all or your DOM changes are unobservable (seriously, most of the time you should use this).
Not that many developers are aware of the
useDebugValue hook, although it enables a much better debugging experience when it comes to debugging custom hooks.
useDebugValue is a simple inbuilt hook which provides extra information about the custom hook internal logic within the React DevTools. It allows you to display supplementary, practical information next to your custom hooks, with optional formatting. This is incredibly useful if you are creating a library for others to use since they can easily see information about your hook, but it also is useful for your own hooks since you can quickly see detailed information about your hooks.
The hook extends the visualization of data about custom hooks inside of the Component Inspector of the React DevTools. Consider installing the React DevTools Browser extension if you do not have one yet!
In this custom
useUser hook we have a
useDebugValue hook that takes a string as its only argument. Passing a single string to
useDebugValue is by far the most common way to use this hook and when you do you will see something interesting in the React dev tools if you click on a component that utilizes this hook.
On the right side of the screen you will see a section labeled
hooks and inside the section you will see the
User hook and next to that you will see the debug information we passed to
Custom React Hook
Custom React hook is always a new composition of one or multiple hooks. If a custom hook does not use any hooks internally, it's not a custom hook and shouldn't have the prefix "use".
Our first step in creating the custom React hook will be the custom hook function definition in its own file, much like a React component since a custom hook is like a component, but it stores logic instead of presentation. This means we will have a file called
There is a
useState at the beginning which will query
localStorage to get the value if it already exists, but if not then the
initialValue will be set for the state. Then
useEffect is used to update
localStorage every time the value is updated.
The reason the function version of
useState is used is because it will check the
localStorage first before setting the state to the
useEffect was used to check
localStorage instead like below it would cause the component to render twice. Once when the
initialValue was set and once after the
Based on the use case above we know that the
useLocalStorage hook will need a
key to store the state as well as an
initialValue to set the state to if there is nothing in
localStorage. We can thus update our code to look like this.
Then in the component where
localStorage is being used the code can be simplified to just one simple line which has the same return values as
useState since the
useLocalStorage hook will behave exactly like
Now all that is left to do is move over the logic for how to handle
localState into the
useLocalStorage hook and return
As you can see the majority of this code is the same as the previous
localStorage code, but there are two main differences. The first is that the key is no longer a
const since it is a parameter to the
useLocalStorage hook so that needs to be set as a dependency for
useEffect. Second, the
setValue variables are being returned from the hook in the exact same format as
useState so this hook can be used in the exact same way as
That is all there is to creating custom hooks in React. They are no more than just fancy functions that can use React hooks inside of them, but they are incredibly powerful in cleaning up code and sharing logic between components!
Magebit is a full service eCommerce agency specialized in Magento. At Magebit we create the wonders of eCommerce and support small sites as well as large enterprises.
Subscribe to our blog
Get fresh content about eCommerce delivered automatically each time we publish.