React Hooks, introduced in React 16.8, revolutionized how developers build functional components by allowing them to use state and other features of React without writing a class. With hooks, developers can reuse logic across components, manage component lifecycle, and work more efficiently within functional components.
In this article, we’ll dive into the basics of React Hooks, explore the most common hooks, and understand how they enhance the React ecosystem.
What Are React Hooks?
Before React Hooks, managing state and lifecycle methods within React required the use of class components. Hooks allow functional components to handle side effects, state, and other React features, simplifying code and reducing boilerplate. Hooks follow these rules:
- Only call hooks at the top level: Hooks should be used at the top of a functional component or in other hooks, not inside loops, conditions, or nested functions.
- Only call hooks from React components: Hooks must be called inside a React functional component or custom hook.
Commonly Used React Hooks
Get ready to have a close look at the most commonly used hooks and their use cases.
1. useState
The useState
is the most widely used React hook which helps you to add state to functional components. It accepts an initial state and returns a pair: the current state value and a function to update that state.
Example:
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} Times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
In this example, useState
initializes count to 0 and the setCount function updates the state when the button is clicked.
2. useEffect
The useEffect
hook is primarily used to perform side effects in function components. Side effects could be data fetching, manual DOM manipulation, subscriptions, or logging.
Example:
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} Times`;
}, [count]); // Only re-run the effect if count changes
return (
<div>
<p>You clicked {count} Times</p>
<button onClick={() => setCount(count + 1)}>
Click Me
</button>
</div>
);
}
In this example, the useEffect
hook updates the document title every time the count
changes. The second argument, [count]
, ensures that the effect only runs when count
is updated, optimizing performance by avoiding unnecessary re-renders.
3. useContext
useContext
allows you to subscribe to React context without writing a class component. It enables components to access shared data like themes, user settings, or authentication status without prop drilling.
Example:
import React, { useContext } from 'react';
const ThemeContext = React.createContext('light');
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button style={{ background: theme === 'dark' ? '#333' : '#FFF' }}>I am styled by theme context!</button>;
}
Here, useContext
accesses the value from ThemeContext
, making the ThemedButton
responsive to changes in theme without passing props through intermediary components.
4. useReducer
useReducer
is a React hook which allows you to add a reducer to your component. It is similar to useState. But is more suitable for managing complex state logic involving multiple sub-values or when the next state depends on the previous one. It also works well when managing local component state and state transitions in a predictable way.
Example:
import React, { useReducer } from 'react';
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
</div>
);
}
In this example, useReducer
helps manage state transitions by dispatching actions (increment and decrement). It is commonly used for more complex state updates than what useState
can handle. In continuation to what are React hooks, we discuss useRef
next.
5. useRef
useRef
creates a mutable object whose .current property persists through renders. It is often used for accessing DOM elements directly or storing mutable values that don’t trigger a re-render when updated.
Example:
import React, { useRef } from 'react';
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// Access the input element and focus it
inputEl.current.focus();
};
return (
<div>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</div>
);
}
In this example, useRef
is used to access the DOM input element directly and focus it when the button is clicked.
6. useMemo
useMemo
memoizes expensive computations to optimize performance, ensuring that the calculation only re-runs when its dependencies change.
If you are interested in the practical application of the React hooks in real world, please visit here. We offer affordable web design and development using React and similar technolgoies.
Example:
import React, { useState, useMemo } from 'react';
function ExpensiveCalculationComponent({ a, b }) {
const memoizedValue = useMemo(() => {
return a + b; // Imagine a complex calculation here
}, [a, b]);
return <div>The result is: {memoizedValue}</div>;
}
In this example, useMemo
ensures that the calculation only happens when a
or b
changes, preventing unnecessary re-calculations on every render.
7. useCallback
The useCallback
React hook memoizes functions, and then returns a memoized version that only changes if one of the dependencies has changed. It is used when you have a component in which the child is rerendering again and again without a need. This can optimize performance, especially when passing functions to child components.
import React, { useState, useCallback } from 'react';
const ExpensiveComponent = React.memo(({ onClick }) => {
console.log("Rendered");
return <button onClick={onClick}>Click Me</button>;
});
function App() {
const [count, setCount] = useState(0);
const increment = useCallback(() => {
setCount((c) => c + 1);
}, []); // Memoized function that only changes when dependencies change
return (
<div>
<p>{count}</p>
<ExpensiveComponent onClick={increment} />
</div>
);
}
8. useLayoutEffect
useLayoutEffect
is a version of useEffect. It fires synchronously after all DOM mutations. It’s useful when you need to perform side effects that involve DOM measurements or manipulations that should happen before the browser paints.
import React, { useLayoutEffect, useRef } from 'react';
function Box() {
const boxRef = useRef();
useLayoutEffect(() => {
console.log(boxRef.current.getBoundingClientRect());
}, []);
return <div ref={boxRef} style={{ width: '200px', height: '200px', backgroundColor: 'blue' }} />;
}
9. useImperativeHandle
useImperativeHandle
is a React hook that customizes the instance value that is exposed when using ref. It’s useful when you want to expose certain actions of a component without giving full access to the DOM.
import React, { useRef, useImperativeHandle, forwardRef } from 'react';
const CustomInput = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => inputRef.current.focus()
}));
return <input ref={inputRef} {...props} />;
});
function App() {
const inputRef = useRef();
return (
<div>
<CustomInput ref={inputRef} />
<button onClick={() => inputRef.current.focus()}>Focus Input</button>
</div>
);
}
10. useDebugValue
useDebugValue
is useful for custom hooks. It allows you to display a label in React DevTools for easier debugging.
import React, { useState, useDebugValue } from 'react';
function useCustomHook(value) {
const [state, setState] = useState(value);
useDebugValue(state ? 'On' : 'Off'); // Debug label
return [state, setState];
}
function App() {
const [isOn, toggle] = useCustomHook(false);
return (
<div>
<p>{isOn ? 'On' : 'Off'}</p>
<button onClick={() => toggle(!isOn)}>Toggle</button>
</div>
);
}
11. useTransition (React 18)
useTransition
allows you to mark some updates as “non-urgent,” letting React update the UI without blocking high-priority updates (such as typing input).
import React, { useState, useTransition } from 'react';
function App() {
const [input, setInput] = useState('');
const [list, setList] = useState([]);
const [isPending, startTransition] = useTransition();
const handleChange = (e) => {
setInput(e.target.value);
startTransition(() => {
setList([...Array(10000).keys()].map((item) => e.target.value));
});
};
return (
<div>
<input type="text" value={input} onChange={handleChange} />
{isPending ? <p>Loading...</p> : <ul>{list.map((item, i) => <li key={i}>{item}</li>)}</ul>}
</div>
);
}
12. useDeferredValue (React 18)
useDeferredValue
allows you to defer a value, making React wait to update lower-priority renders until high-priority updates finish. This is useful for complex UI that may cause jank.
import React, { useState, useDeferredValue } from 'react';
function App() {
const [input, setInput] = useState('');
const deferredInput = useDeferredValue(input);
return (
<div>
<input type="text" value={input} onChange={(e) => setInput(e.target.value)} />
<p>Deferred Value: {deferredInput}</p>
</div>
);
}
13. useId (React 18)
useId
generates unique IDs that are stable across server and client renders, useful for accessibility and form inputs.
import React, { useId } from 'react';
function App() {
const id = useId();
return (
<div>
<label htmlFor={id}>Name: </label>
<input id={id} type="text" />
</div>
);
}
Best Practices for Using Hooks
- Keep Components Simple: Avoid overly complex logic inside hooks. Break down functionality into multiple hooks if necessary.
- Avoid Side Effects in Render: Only use hooks like
useEffect
oruseLayoutEffect
for side effects, not in the render function itself. - Custom Hooks: Create reusable logic using custom hooks to keep your components clean and DRY (Don’t Repeat Yourself).
Conclusion
This concludes our discussion on what are React hooks. React Hooks have revolutionized how developers build and manage state in functional components. From basic state management with useState
to the more advanced performance optimizations of useTransition
and useDeferredValue
, hooks have simplified component logic and enhanced scalability. Understanding both the foundational and newer hooks is essential to writing efficient, clean, and maintainable React code.
P.S.: The React is getting popular day by day. If you are curious about the future of React, read here: