If you don’t like classes then hooks are here to help you. Hooks are functions that enable you to use React functionalities like state and lifecycle features without using classes. As the name suggests, it enables you to hook into React state and lifecycle features from function components.
Hooks do not work inside classes and are backward-compatible, meaning they have no breaking changes. So it is your choice if you want to use them or not. This new feature gives you the power to use all React features, even the function components.
Make sure you write hooks in standard follow form. Call hooks at the top level of React functions to ensure that hooks are called in the same order every time. Try to avoid calling them in loops, nested functions, or inside conditions. Also, make sure you call them from React function component, not from JavaScript functions. If you don’t follow that rule then it may result in some unexceptional behaviors. React also provides a linter plugin to ensure that these rules apply automatically.
You don’t have to install anything to use hooks. They come with React from the 16.8 version onward.
To declare, change and use states using hooks we have useState() function component. It returns two value pairs, the current state, and a function to update the state. It is something like this.setState in a class but it is different from this.setState by the fact that it does not combine the old and new state. UseState() can be called inside a function component or from an event handler. The initial state is passed inside useState() as an argument which is used during the first render. There is no bound on using multiple state hooks in a single component; we can use as many as we want.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import React, {
useState
} from 'react';
function usingStateHook() {
// use useState() to declare state variable count
const [count, setCount] = useState(0);
return (
<div>
{/* display count value */}
<p>Your click count is {count}</p>
{/* on click update count value using setCount */}
<button onClick={() => setCount(count + 1)}>
Button
</button>
</div>
);
}
In the example above, we have implemented a counter that counts the number of times the button is clicked. Here useState(0) initializes the count with ‘0’ and also returns the setCount function which can be used to update count. We are calling setCount in onClick event handler with value ’count+1’. Here count has the previous count value, like if previously the count was two, then setCount will increment it by one to make it three.
To perform side effects operations like changing DOM, data fetching, etc., from React function components we use Effect hook, i.e., useEffect. These operations are called so since they can affect other components and can’t be performed during rendering. UseEffect hook provides us the power of componentDidMount, componentDidUpdate, and componentWillUnmount. UseEffect is declared inside the components because they need to have access to their state and props. UseEffect can be run at first render, after every render, or used to specify clean up based on how we declare them. By default, they run after every render including the first render.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import React, {
useState,
useEffect
} from 'react';
function usingEffect() {
const [count, setCount] = useState(0);
/* default behaviour is similar to componentDidMount and componentDidUpdate: */
useEffect(() => {
// Change document title
document.title = `Clicked {count} times`;
});
return (
<div>
<p>Clicked count = {count}</p>
<button onClick={() => setCount(count + 1)}>
Button
</button>
</div>
);
}
In the above example we are updating the document title every time the count gets updated. Here useEffect is working similarly to componentDidMount and componentDidUpdate combined since it will run during the first render and also after every update. This is the default behavior of useEffect, but it can be changed. Let’s see how.
If you want to use useEffect as componentDidMount then you can pass an empty array [] in the second argument to useEffect as in the below example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import React, {
useState,
useEffect
} from 'react';
function usingEffect() {
const [count, setCount] = useState(0);
// Similar to componentDidMount
useEffect(() => {
// Only update when component mount
document.title = `You clicked {count} times`;
}, []);
return (
<div>
<p>Your click count is {count}</p>
<button onClick={() => setCount(count + 1)}>
Button
</button>
</div>
);
}
If you want to use it as componentDidUpdate, which only re-renders if a specific state is updated, then we can pass that state value in the second argument of useEffect as below:
1
2
3
useEffect(() => {
document.title = `Clicked {count} times`;
}, [count]); /* if count changes then only effect is re-run */
The above code is similar to the below code when we use classes.
1 2 3 4 5
componentDidUpdate(prevProps, prevState) { if (prevState.count !== this.state.count) { document.title = `Clicked {this.state.count} times`; } }
If you also want it to perform the componentWillUnmount function, (which can be used for clean up), then we can return value from useEffect.
1
2
3
4
5
6
7
8
9
10
11
useEffect(() => {
function handleStatusChange(status) {
setStatus(status);
}
API.changeStatusToTrue(props.status, handleStatusChange);
return () => {
/* for implementing componentDidUnmount behaviour */
API.changeStatusToFalse(props.status, handleStatusChange);
};
}, [props.status]); /* re-run only if props.status changes */
Other hooks include useContext, useReducer, useCallback, useMemo, useRef, useImpreativeHandle, useLayoutEffect, and useDebugValue. These are available in React, but they are used less.
useContext: In React we use context when we want to share data that we can think of as global to the React components tree. UseContext helps us to use context with hooks, this accepts the context object (i.e., the value returned from React.createContext), and then it returns the current context value.
const value = useContext(MyContext);
useReducer: This hook can be utilized in place of useState when there is some complex state logic present that has multiple sub-values or can be used when a state depends on the previous state. It takes a reducer (something like (state, action) => newState) and returns the current state and a dispatch method in pairs.
const [state, dispatch] = useReducer(reducer, initialArg, init);
useRef: It takes the initial value as an argument and returns a mutable reference object that contains a special property named ‘.current’ which is set with the value, which is passed as argument. Also, the return object will persist for the entire lifetime of the component.
const refContainer = useRef(initialValue);
useMemo: It is used in the functional component and returns a memoized value. Helpful when we need to implement memoization.
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
useCallback: It is used when we have a component whose child is being re-rendered over and over again unnecessarily. A memoized callback is returned by useCallback.
1
2
3
4
5
6
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
useImperativeHandle: It helps us to modify the ref instance value that is exposed to parent components (modify ref that has been created).
useImperativeHandle(ref, createHandle, [deps])
useLayoutEffect: It is the same as useEffect except for the fact that it fires synchronously after all DOM mutations.
useDebugValue: It is used when we need to display a label for custom hooks in React DevTools.
useDebugValue(value)
React also provides the ability to make our own hook, which is a javaScript function, with ‘use’ at the start of its name and which can call other hooks. We usually make custom hooks when we have some parts that can be used again in our project. First, we need to define them, which may include the use of other hooks, then we can call them just like other hooks.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function FriendStatus(props) {
const isOnline = useFriendStatus(props.friend.id);
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
function FriendListItem(props) {
const isOnline = useFriendStatus(props.friend.id);
return (
<li style={{ color: isOnline ? 'green' : 'black' }}>
{props.friend.name}
</li>
);
}
Hooks provide us with alternate ways to use different functionalities like lifeCycle methods without the need to write classes. It is an extra feature which can be used by those who don’t like classes. There are different types of hooks that have different functionalities. Among them, useState and useEffect hooks are used most frequently.