Personally, I am a fan of functional components and their simplified syntax. With these hooks, you will be able to implement any dynamic feature that you want, and you no longer need to bother with class components.
Without further ado, let’s get to topic and learn about hooks in React.
useState
Probably the most important hook in React, necessary for all dynamic features. useState hook allows functional components to maintain state, something that was not possible before React 16.8. You only need to import the useState hook and call it in your functional component.
Its syntax is much simpler than what you’re probably used in class components. Simply create two variables using square brackets and call the useState hook. It will return two values – the first variable to hold state value, and second function to update it.
You are probably aware that maintaining state is necessary for all the interactive features. Most dynamic rendering or dynamic styling uses state variables to set a condition. Event handlers access the input value and store it in the state. With class components, you had to use the setState() method, whose syntax was sometimes confusing. Having the useState() hook return a single dedicated function to update the variable is much simpler than the alternative.
Normally, event handlers only take one argument – instance of SyntheticEvent. However, there’s a way to pass additional data to event handlers in React.
Keep in mind that the argument to the useState() hook is set as the default value for the state variable. You can use this behavior to set default values for state variables, and control what the component looks like, or what it displays by default.
You can pass any type of value to the useState() hook. It accepts integers, strings, Booleans, arrays, objects and even null. The hook returns an array, so you need to create two variables using destructuring syntax to hold these two values.
Like the setState() method, updating state variables using the function returned from useState() hook triggers a re-render in the component and all of its children.
Often you will use the setter function to store API data in the state, and then use that data to render components for every item.
The useEffect hook
Second most important hook is the useEffect hook. It is used to replace lifecycle hooks of class components that run when component mounts, dismounts, and updates. It allows you to plug in side-effects at any moment of the component’s lifecycle. Even more than that, useEffect hooks allows you to specify the state variables to ‘watch out’ for. Changes to certain variables will trigger a re-render, while changes to others won’t.
Side-effects are very important part of React. You need them to set up subscriptions, do mutations, and log to the console, which helps with debugging.
First argument to the useEffect() hook is the side effect that you want to run. The second argument is the dependency array, which specifies state variables that the hook needs to ‘look out for’. If the dependency array is empty, the side effect will run only once – when the component mounts. This essentially replicates the results of componentDidMount() lifecycle method in React. If the dependency array contains multiple state values, then the side effect will run only if there’s change to those state variables. If there’s no second argument, the side effect will run every time the component updates. useEffect without a dependency array runs when the component mounts and every time it rerenders.
Finally, the third optional argument is a function that runs when the component unmounts. It is necessary to remove subscriptions and other tasks related to cleaning up the application. The kind of tasks you would handle in a componentWillUnmount() lifecycle method.
useEffect(), in combination with useState(), plays an important role in fetching external data. Often times you need to make a call to the API when the component mounts. You can do that by creating a useEffect hook with empty dependency array. Then store loaded data in the state, which you can then use to render components or elements as normal.
useContext hook
The easiest way to pass data between components is props. However, when there are too many levels of components, passing data down via props becomes cumbersome. It is a practice called prop drilling, and it takes too much effort and clutters up your code. React community has created several ways to work around this. Mostly state management libraries like Redux. But with useContext hook, React offers its own way to easily share data from one component to another.
As a first step, you need to create a context instance using React.createContext method. Then pass it to the useContext hook and create a context wrapper. Anything passed as props to this wrapper will be accessible in other components in the same app.
useRef hook
Finally, the useRef hook allows us to access DOM elements in React. We do not have getElementById() method in React. Instead, you can set ref attribute to a ref created using the useRef() hook.
You need to create a variable to store a ref instance. Set the variable to a ref instance returned by useRef() hook.
Once the ref is created and tied to an element using ref attribute, you have access to that element. You might use the reference to change the element throughout lifecycle of the component or change its contents to something else. Refs are also useful for a ‘scroll to element’ feature. You need a reference to the element to call scrollIntoView() method to scroll that element into view.
forwardRef() allows you to pass refs from parent to child components, so you can maintain access to the DOM element.
Summary
Hooks are very important in React. Especially if you want to avoid difficult syntax of class components. Hopefully this will help you build interactive web applications with dynamic features.