Hooks in the Latest ReactJS Version
React 16.8, the latest version of the popular JavaScript library, includes an implementation of React Hooks. They are functions that let ‘hook into’ React state and lifecycle features for stateless components. Dan Abramov introduced the new functionality at React Conf in October 2018. Following positive user feedback and some adjustments, Hooks became available in a stable release on February 6, 2019.
This short article explains why those functions in the new ReactJS version are so important and how they facilitate writing React applications.
Why React 16.8 Is Great
The newly introduced Hooks offer the following benefits:
- More concise code. Hooks let developers reduce that part of a component which is responsible for the logic and view. They are similar to syntactic sugar: less code is written, but the functionality level remains the same.
- Increased component reusability. Hooks make components cleaner. The less complex logic and dependencies they have, the easier they are to reuse and test.
React 16.8 doesn’t contain any breaking changes. To enforce best practices with Hooks, React engineers recommend enabling a new lint rule eslint-plugin-react-hooks rather than using Create React App. They will, however, include the plugin into Create React App by default soon.
NB! Hooks will work with React packages that are 16.8.0 or higher.
Hooks solve at least three problems that React developers have been tackling for years:
1. Reused stateful logic.
React 16 allowed attaching reusable behavior to a component, but Hooks offer a significant improvement. So far, higher-order components have been in place for this purpose. The problem is that in the long run, such patterns may cause a ‘wrapper hell,’ i.e. countless components within other components.
Hooks help to prevent the wrapper hell. They enable developers to extract stateful logic from a component to test it independently and reuse. It can be done without changing your component hierarchy. Hooks also allow integrating stateful logic into stateless components, removing the limitations that stateless components used to impose.
Simpler and more straightforward logic requires fewer splits into smaller components. Consequently, their reusability increases.
2. Large-sized components.
When using components, the developer may need to subscribe to particular events or run a function for mounting a component, and then unsubscribe from the event and run a function for unmounting it. Moreover, they may need to add some logic after it was updated. This leads to the component logic bloating and splitting between the component lifecycle parts and allows for introducing bugs and inconsistencies. It looks like this:
export default class ReactLargeSized extends React.Component {
componentDidMount() {
const {actionId} = this.props;
this.sunscribeToAction(actionId);
this.fetchActionData(actionId);
this.startTimers();
}
componentWillUnmount() {
const {actionId} = this.props;
this.unsunscribeFromAction(actionId);
this.cancelPendingRequests();
this.stopTimers();
}
componentDidUpdate() {
…
}
…
render () { return …; };
}
With the stateful logic all over the place, such components are difficult to break into smaller ones and to test. Hooks allow splitting one component into smaller functions based on relations between the pieces, e.g., adding and removing a subscription or fetching data.
3. Understanding of the classes in React 16.
The absence of lifecycle methods in functional components is confusing not only for beginners. Rewriting a functional component as a class one is tiresome. The classes functionalities are not only more difficult to understand than functional components. They minify insufficiently and are resource-intensive and more difficult to compile.
Hooks provide a solution. See how one of the original built-in Hooks in React 16.8 – useState function – works:
Stateful example:
import React from ‘react’;
export default class Hello extends React.Component {
constructor(props) {
super(props);
this.state = {
name: ‘John’
}
this.handleNameChange = this.handleNameChange().bind(this);
}
handleNameChange(e) {
this.setState({name: e.target.value});
}
render () { return …; };
}
The sample above shows the following process:
- the developer creates a state with initial data;
- creates a handler function for change event;
- displays a view with data and handler function in render method.
Here is the same process with Hooks:
import React, { useState } from ‘react’;
export default function Hello(props) {
const [name, setName] = useState(‘John’);
function handleNameChange(e) {
setName(e.target.value);
}
render () { return …; };
}
The code includes:
1. useState – the Hook allows to use state in functional components. It returns an array where the first element is a value from state, and the second is a recording function in state.
2. setName – function which writes the data in state (equivalent setState({name: e.target.value})).
The use of state for a functional component requires importing useState first. Then, with useState, the developer initializes the name variable and a function which can update setName data in it. Call to setName will change the value of the name variable and drive it to re-render the displayed elements.
As a result, the code is more concise, the functional component needs no re-writing, and the state can be reused.
Selected React Hooks
A few more built-in Hooks are available, such as:
useContext
The function facilitates import of variables from Context without additional wrappers in a component. It looks as follows:
import {useContext} from ‘react’;
import {someContext} from ‘./Context’;
…(further in the functional component)
const someVariable = useContext(someContext);
useEffect
This Hook allows running a function after React has inserted a component’s contents into DOM and removing event handlers. The joint use of componentDidMount, componentDidUpdate, and componentWillUnmount in components is an equivalent.
useEffect(() => {
// some logic that was in componentDidMount and componentDidUpdate
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener(‘resize’, handleResize);
return () => {
// some logic that was in componentWillUnmount
window.removeEventListener(‘resize’, handleResize);
}
})
useEffect is consistent by default, i.e. is performed every time the component renders and replaces componentDidMount and componentDidUpdate, while the function in return for useEffect replaces the logic from componentWillUnmount.
Custom Hooks
Users can create own Hooks as JS functions which call other Hooks. Custom Hooks let extract the logic from a component to facilitate testing. Developers can reuse stateful logic between components without adding new ones to their tree.
Hooks are rather a convention than a feature. The name of a custom Hook should always start with ‘use,’ such as:
function useItemInStock(itemID) {
const [isInStock, setInStock] = useState(null);
// some custom logic
return isInStock;
}
The operation logic of some React Hooks can be separated from a component. Within it, call to a custom Hook looks like a standard call to useDocumentTitle(name). However, the developer is still able to work with the component lifecycle, state, and context.
Hooks allow developers to use React without classes, but Facebook doesn’t plan to get rid of them. Instead, they mean Hooks to cover all existing use cases for classes.
To Recap
With the newly introduced React Hooks, developers can use lifecycle methods in stateless components. It’s a robust tool which extends React features considerably. Developers can use it in React DOM and React DOM Server for server-side rendering. They can reuse stateful logic and build own Hooks to share reusable logic between different components. Hooks provide a more direct API to React props, state, context, refs, and lifecycle, a new powerful way to combine them, and access to escape hatches. The API helps the code to stay on the optimizable path. Hooks make code reuse easier, empowering developers to write their components more simply.
Built-in Hooks in the latest React version work side-by-side with existing code and with classes, can be adopted gradually, and don’t require the developers to learn complex functional or reactive programming techniques. Custom Hooks can cover use cases like form handling, animation, declarative subscriptions, timers, and more.
The new convention in React 16.8 promotes flexibility and speed of development. Properly harnessed Hooks allow the developers to speed up and optimize the app loading.
Contact us if you’re interested in such development!