React Hooks were introduced in React 16.8 and they are functions that allow you to use state and other React features without writing a class component.
ReactJS has gained immense popularity over the years due to its ability to build complex and interactive web applications. One of the key features of ReactJS is the introduction of hooks, which has revolutionized the way developers write code. In this article, we will discuss ReactJS hooks in detail and understand how they work.
What are ReactJS Hooks?
ReactJS Hooks are functions that allow you to use React state and lifecycle features from function components. Before the introduction of hooks, developers had to use class components to manage state and lifecycle methods. With hooks, developers can use state and lifecycle methods in functional components, making it easier to write clean and reusable code.
There are several built-in hooks in ReactJS, including useState
, useEffect
, useContext
, and useReducer
. These hooks provide a way to manage state, perform side effects, and interact with the React context API.
Types of React Hooks
There are several types of Hooks available in React:
- useState: This hook is used to add state to functional components.
- useEffect: This hook is used to add side effects to your components. It allows you to perform actions when certain conditions are met, such as when the component mounts or when the component updates.
- useContext: This hook is used to access context within your components. It allows you to pass data from a parent component to a child component without having to pass props down through each level of the component tree.
- useReducer: This hook is used to manage complex state logic within your components. It is similar to useState, but is more suitable for managing state that has multiple sub-values or when the next state depends on the previous state.
- useCallback: This hook is used to memoize functions so that they are only re-created when their dependencies change. This can help with performance when passing functions as props to child components.
- useMemo: This hook is used to memoize values so that they are only re-calculated when their dependencies change. This can help with performance when performing expensive calculations within your components.
- useRef: This hook is used to create a mutable reference that persists between renders. It can be used to access the DOM, manage timers/intervals, or store any mutable value that needs to persist between renders.
- useLayoutEffect: This hook is similar to useEffect, but it is executed synchronously immediately after all DOM mutations have been applied. This makes it useful for interacting with the DOM or measuring element sizes/positions.
- useImperativeHandle: This hook is used to expose certain methods of a child component to its parent component. It is commonly used when working with third-party libraries that require direct access to a component's methods.
- useDebugValue: This hook is used to display custom labels for custom hooks in React DevTools. It can be used to add more context to the hooks used within your application.
- Custom Hooks: Custom hooks allow developers to create reusable logic that can be shared across multiple components.
1. useState Hook
The useState
hook is used to manage state in functional components. It takes an initial value as its argument and returns an array with two elements: the current state value and a function to update the state.
import React, { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
In the example above, we are using the useState
hook to manage the count
state. We initialize the state to 0
and update it using the setCount
function.
2. useEffect Hook
The useEffect
hook is used to perform side effects in functional components. It takes a function as its argument and runs it after every render.
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
In the example above, we are using the useEffect
hook to update the document title after every render. The function passed to useEffect
is called after every render, which means that the title will be updated every time the count
state changes.
3. useContext Hook
The useContext
hook is used to consume values from the React context API. It takes a context object as its argument and returns the current context value.
import React, { useContext } from 'react';
import { ThemeContext } from './theme-context';
function ThemeButton() {
const theme = useContext(ThemeContext);
return (
<button style={{ background: theme.background, color: theme.foreground }}>
I am styled by theme context!
</button>
);
}
In the example above, we are using the useContext
hook to consume the ThemeContext
value. We can use this value to style the button based on the current theme.
4. useReducer Hook
The useReducer
hook is used to manage complex state in functional components. It takes a reducer function and an initial state as its arguments and returns an array with two elements: the current state value and a dispatch function to update the state.
import React, { useReducer } from 'react';
// Define initial state
const initialState = { count: 0 };
// Define reducer function
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
}
function Counter() {
// Call useReducer hook
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<h2>Count: {state.count}</h2>
<button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
<button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
</div>
);
}
export default Counter;
In this example, we define an initial state object { count: 0 }
and a reducer function that takes the current state and an action object as arguments and returns the updated state based on the action type.
We then call the useReducer
hook and pass in the reducer function and initial state. This returns an array with the current state and a dispatch function that we can use to update the state by passing in an action object with a specific type.
In the Counter
component, we display the current count value from the state object and render two buttons that call the dispatch function with the appropriate action object when clicked. This updates the state and triggers a re-render of the component with the updated count value.
5. useCallback Hook
This hook is used to memoize a function in a functional component. It is similar to the shouldComponentUpdate method in class components. Here's an example:
import React, { useCallback, useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
// This function will be memoized with useCallback
const handleClick = useCallback(() => {
setCount(count + 1);
}, [count]);
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Click me!</button>
</div>
);
}
In this example, we're using useCallback
to memoize the handleClick
function so that it's not recreated on every render. This can be useful for performance optimization, especially when passing functions down to child components that may trigger unnecessary renders.
The useCallback
hook takes two arguments: a callback function and a dependencies array. The callback function is the function that will be memoized, and the dependencies array is an array of values that the function depends on. If any of these values change, the function will be re-created.
In our example, the handleClick
function depends on the count
state variable, so we include it in the dependencies array. This ensures that the function is only re-created when the count
variable changes.
By using useCallback
in this way, we can improve the performance of our app by reducing unnecessary renders and optimizing our function creation.
6. useMemo Hook
useMemo
is a React hook that allows you to memoize the result of a function so that it is only re-computed when the inputs change. It is useful when you have a costly computation that doesn't need to be re-computed on every render.
Here's an example of how to use useMemo
:
import React, { useState, useMemo } from 'react';
function Example() {
const [countA, setCountA] = useState(0);
const [countB, setCountB] = useState(0);
const total = useMemo(() => {
console.log('Calculating total...');
return countA + countB;
}, [countA, countB]);
return (
<div>
<h1>useMemo Example</h1>
<p>Count A: {countA}</p>
<p>Count B: {countB}</p>
<button onClick={() => setCountA(countA + 1)}>Increment A</button>
<button onClick={() => setCountB(countB + 1)}>Increment B</button>
<p>Total: {total}</p>
</div>
);
}
export default Example;
In this example, we are using the useMemo
hook to memoize the calculation of the total
variable. The useMemo
hook takes two arguments: a function that calculates the memoized value, and an array of dependencies that trigger a re-calculation of the memoized value when they change.
In this case, we only want to re-calculate the total
value when either countA
or countB
change, so we include them in the dependencies array. The first time the component renders, the total
value is calculated and stored, and subsequent re-renders will use the memoized value instead of re-calculating it every time.
This can be especially useful when dealing with expensive calculations or complex data structures that don't need to be re-calculated on every re-render. By memoizing the calculation using useMemo
, we can optimize our component's performance and avoid unnecessary re-renders.
7. useRef Hook
useRef
is a hook in React that returns a mutable ref object whose .current
property is initialized to the passed argument (initialValue). It is used to access DOM nodes or to store any mutable value that does not trigger a re-render of the component. It can also be used to hold on to any value that persists between renders.
Here's an example of how to use the useRef
hook:
import React, { useRef } from 'react';
function MyComponent() {
const inputRef = useRef(null);
const handleClick = () => {
inputRef.current.focus();
};
return (
<div>
<input type="text" ref={inputRef} />
<button onClick={handleClick}>Focus Input</button>
</div>
);
}
In this example, we are using the useRef
hook to create a reference to the input
element. We then pass this ref to the input
element using the ref
prop. In the handleClick
function, we access the current
property of the ref object to focus the input element.
Note that the useRef
hook does not cause a component to re-render when its value changes, which is useful for storing values that you do not want to trigger a re-render. However, the useRef
hook should not be used to manage state, as it does not cause a re-render when its value changes. For state management, use the useState
or useReducer
hooks.
8. useLayoutEffect Hook
The useLayoutEffect
hook in React is very similar to the useEffect
hook, but it is executed synchronously immediately after all DOM mutations. It is often used to measure the size or position of a DOM element and perform some action accordingly.
The useLayoutEffect
hook takes two arguments - a callback function and an optional array of dependencies.
Here is an example of how to use the useLayoutEffect
hook to measure the height of a DOM element:
import React, { useLayoutEffect, useState, useRef } from 'react';
function Example() {
const [height, setHeight] = useState(0);
const elementRef = useRef(null);
useLayoutEffect(() => {
setHeight(elementRef.current.clientHeight);
}, []);
return (
<div ref={elementRef}>
The height of this div is {height}px.
</div>
);
}
In this example, we first define a state variable height
and a ref
object elementRef
. We then use the useLayoutEffect
hook to measure the height of the div
element referenced by elementRef
and set the height
state variable to the measured height.
Note that we pass an empty array as the second argument to useLayoutEffect
, which ensures that the effect is only executed once, immediately after the DOM mutation caused by rendering the component. This is necessary to ensure that we get an accurate measurement of the height of the element.
In the return statement, we simply render the div
element and display the height
variable.
9. useImperativeHandle Hook
The useImperativeHandle
hook is a way to expose certain functions or values of a child component to its parent component. This hook can be useful in cases where you want to provide a way for the parent component to interact with the child component in a specific way.
Here's an example of how to use useImperativeHandle
:
import React, { forwardRef, useImperativeHandle } from 'react';
const ChildComponent = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focusInput: () => {
inputRef.current.focus();
}
}));
return (
<input type="text" ref={inputRef} />
);
});
export default ChildComponent;
In this example, the ChildComponent
is a simple text input field. The useImperativeHandle
hook is used to expose a function called focusInput
that can be called from the parent component to focus the input field.
The forwardRef
function is used to forward the ref
prop to the ChildComponent
. This allows the parent component to access the ref
and call the focusInput
function.
Here's an example of how to use the ChildComponent
in a parent component:
import React, { useRef } from 'react';
import ChildComponent from './ChildComponent';
const ParentComponent = () => {
const childRef = useRef();
const handleButtonClick = () => {
childRef.current.focusInput();
};
return (
<div>
<ChildComponent ref={childRef} />
<button onClick={handleButtonClick}>Focus input</button>
</div>
);
};
export default ParentComponent;
In this example, the ParentComponent
renders the ChildComponent
and provides a way to call the focusInput
function. When the button is clicked, the handleButtonClick
function is called, which calls the focusInput
function on the ChildComponent
using the childRef
.
Note that the useImperativeHandle
hook should be used sparingly, as it can make your code harder to reason about and maintain. It's generally recommended to use it only when necessary and to keep the exposed functions or values as simple as possible.
10. useDebugValue Hook
The useDebugValue
hook is a utility hook that helps to debug custom hooks. It allows you to display custom hook values in React Developer Tools. This hook takes two arguments, a value and a formatter function, and returns the value passed to it.
Here's an example of how to use the useDebugValue
hook:
import { useDebugValue, useState } from 'react';
function useCustomHook(initialValue) {
const [value, setValue] = useState(initialValue);
useDebugValue(value, value => `Value: ${value}`);
const updateValue = (newValue) => {
setValue(newValue);
};
return { value, updateValue };
}
In the example above, useCustomHook
is a custom hook that takes an initial value as an argument and returns an object with a value
property and an updateValue
function. The useDebugValue
hook is used to display the value of value
in React Developer Tools. The formatter
function formats the value to display it in a specific way.
Using the useDebugValue
hook can help you debug your custom hooks and make it easier to see what values they are returning. It is particularly useful when working with complex hooks that have multiple values or when you need to inspect the values of a custom hook in React Developer Tools.
What is a Custom Hook and How to Create it?
In ReactJS, a custom hook is a function that allows you to reuse stateful logic across your components. By creating a custom hook, you can abstract complex logic into a reusable function that can be easily imported and used in different components.
Custom hooks are created using the same basic syntax as a regular hook: they start with the word "use" and can use other hooks within them. For example, here's a custom hook that uses the useState hook to manage the state of a boolean value:
import { useState } from 'react';
function useToggle(initialState) {
const [value, setValue] = useState(initialState);
function toggleValue() {
setValue(!value);
}
return [value, toggleValue];
}
This custom hook, called useToggle
, takes an initial state value and returns an array with two elements: the current value of the boolean and a function to toggle it. To use this custom hook in a component, you simply import it and call it like any other hook:
import { useToggle } from './useToggle';
function MyComponent() {
const [isOn, toggleIsOn] = useToggle(false);
return (
<div>
<p>The current value is: {isOn ? 'on' : 'off'}</p>
<button onClick={toggleIsOn}>Toggle</button>
</div>
);
}
This example demonstrates the power of custom hooks - by creating a reusable function that abstracts away the complexity of state management, we can easily use it in multiple components and keep our code clean and modular.
Conclusion
React hooks provide a simpler and more efficient way to write React code. They allow you to use React features in functional components, which reduces the amount of boilerplate code needed. Additionally, they allow you to reuse stateful logic across components, which can be a huge time saver. If you're not using hooks yet, give them a try in your next project.
If you want to learn more about React and other related topics, check out our other posts on ReactJS and getting started with ReactJS.