Since their introduction, hooks have become very important for web development in React. Beginner courses now primarily focus on functional components, and teach hooks like useState and useEffect() right away. Indeed, learning hooks can empower beginner developers to start building apps with React almost straight away.
The goal of this article is to delve deep into hooks and help you better understand what makes them useful, and how to make best use of them.
Facebook created React to meet rising needs for interactive features. Great thing about React is that web applications built with it automatically refresh and show updated information, without needing to manually refresh the page. This allowed Facebook to easily build wonderfully user-friendly web applications.
Problem with React back then was that all the ‘smart’ and dynamic features were exclusive to class components. These required more lines of code to set up, and were sometimes difficult to read. Functional components existed only for presentational purpose. That all changed in 2016, when React introduced hooks for functional components. Hooks gave these ‘dumb’ components all the necessary functionalities to implement dynamic features.
With hooks, functional components could maintain a state, use lifecycle hooks, and much more. As a result, functional differences between class and functional components became none existent. Because of their simpler syntax, React developers started to favor functional components over their class-based alternatives.
React developers started to create custom hooks to implement advanced personalities. React libraries started to move their functionalities to hooks as well. Major React libraries like react-router adopted hooks and used them to implement key features. This forced most React developers to start using functional components.
React uses hooks (for example, useRef) to implement important web application features. For example, we need a useRef hook to get element by id in React. Because hooks play such an important role, we feel it’s important to understand what are hooks and why they’re so important.
Let’s start looking at React hooks from the most common and move on to discuss less commonly used ones as well.
Functional components did not used to have the ability to maintain the state. That changed with the introduction of hooks. It is specifically the useState hook that allows us to create state variables in functional components. State variables are often used to hold temporary values or user inputs. Functional components can keep track of changes in the component thanks to the state.
The useState returns two values – state variable itself that can hold a value, and a function to update it. Even in class components, you can not directly update the state. Instead, you need to use the setState() method to merge old state with the new. Similarly, useState() provides a specific function to update a state variable. The only way to update state is by calling the function. Argument to the useState() hook sets the default state value. Many developers use this to specify a type of value when initializing the state. For example, initialize a Boolean as true or false and lock it to make sure it doesn’t change to another type of value.
Every time the state changes, React will re-render the component that maintains the state. It will also re-render all the children component in the tree. React does this to always display the latest changes and have up-to-date information on the screen.
State is often used to store current user inputs. For example, you might have a text field with an onChange event handler that accesses current input value and stores it in the state. On the other hand, the input element might also get its value from the state. These are called controlled inputs, and they are the key to achieving consistency of data in React.
Second most commonly used hook in React. It essentially replaces lifecycle methods from class components that we all know and love. It is necessary to perform any side effect in React. Whether that is loading external data from an API, manually adding an event listener, or something else entirely.
Specifically, useEffect() must accept two arguments, and an optional third argument. Two arguments are:
A dependency array that contains state variables. React will ‘listen’ for changes to these variables, and will run the side-effect every time it notices changes. This way, useEffect() has the power to run side effect only for changes to some components, but not to others. Or developers can leave the dependency array empty, in which case the side effect will run only when the component mounts.
The third optional argument is a cleanup function, which is useful to remove subscriptions or to remove events added in the callback function. Essentially, to clean up web application after user shuts it down.
In large web applications, we often have very tall component trees. Using props to pass down data from parent to child ten levels down is not very practical. Application soon starts looking messy and it’s simply easier to make a mistake.
React team realized this would be a challenge and introduced the useContext hook. It allows parents to add values to a context that is available to all components. Child components can directly access values in the context.
Instead, team behind React introduced the concept of refs, short for references. useRef() allows you to create a reference, which you can link with a DOM element by setting its ref attribute to the empty ref. From that point on, you can confidently work with the variable that contains the element reference.