React Hooks revolutionized the way we build functional components in React. They offer a powerful and concise way to manage state, side effects, and other functionalities. This blog post delves into all the core React Hooks, equipping you to craft dynamic and interactive React applications.
1. useState: The cornerstone for managing state within functional components. It returns an array containing the current state value and a function to update it.
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
}
2.useEffect: Manages side effects in functional components, such as data fetching, subscriptions, or DOM manipulation that occurs after rendering. It accepts a callback function and an optional dependency array to control when the effect runs.
import React, { useEffect, useState } from 'react';
function UsersList() {
const [users, setUsers] = useState([]);
useEffect(() => {
const fetchData = async () => {
const response = await fetch('https://api.example.com/users');
const data = await response.json();
setUsers(data);
};
fetchData();
}, []); // Empty dependency array ensures data is fetched only once
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
);
}
3.useContext: Provides a way to share state across components without explicit prop drilling. It accepts a context object and returns the current context value.
import React, { createContext, useContext, useState } from 'react';
const ThemeContext = createContext('light');
function App() {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(theme === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={theme}>
<Toolbar onToggleTheme={toggleTheme} />
<Content />
</ThemeContext.Provider>
);
}
return (
<button onClick={onToggleTheme}>
Change Theme ({theme})
</button>
);
}
function Content() {
const theme = useContext(ThemeContext);
return <div style={{ backgroundColor: theme === 'light' ? 'white'
: 'black' }}>Content</div>;
}
4.useReducer: Manages complex state logic using a reducer function. It accepts a reducer function and an initial state, returning the current state and a dispatch function to update it.
import React, { useReducer } from 'react';
const initialState = { count: 0 };
const 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'
})}>Increment</button>
<button onClick={() => dispatch({ type: 'decrement'
})}>Decrement</button>
</div>
);
}
5.useCallback: Memoizes a callback function to prevent unnecessary re-renders of child components that depend on it.
import { useCallback } from 'react';
export default function ProductPage({ productId, referrer, theme }) {
const handleSubmit = useCallback((orderDetails) => {
post('/product/' + productId + '/buy', {
referrer,
orderDetails,
});
}, [productId, referrer]);
6.useMemo: Similar to useCallback, it memoizes a value based on its dependencies, preventing unnecessary re-computations.
import React, { useMemo } from 'react';
function ExpensiveCalculation(props) {
const result = useMemo(() => {
// Perform expensive calculation here
return complexCalculation(props.data);
}, [props.data]); // Recalculate only if props.data changes
return <div>Result: {result}</div>;
}
7.useRef: Creates a mutable ref object that persists across re-renders.
import React, { useRef } from 'react';
function InputField() {
const inputRef = useRef(null);
const handleClick = () => {
inputRef.current.focus(); // Access DOM element through the ref
};
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={handleClick}>Focus Input</button>
</div>
);
}
8.useLayoutEffect: A specialized version of useEffect that runs after DOM mutations but before the browser paints the screen.
import React, { useLayoutEffect, useState } from 'react';
function MyComponent() {
const [size, setSize] = useState({ width: 0, height: 0 });
useLayoutEffect(() => {
const updateSize = () => {
setSize({ width: window.innerWidth, height: window.innerHeight });
window.addEventListener('resize', updateSize);
updateSize(); // Call initially to set size on render
return () => window.removeEventListener('resize', updateSize);
// Cleanup function
}, []);
return (
<div style={{ width: size.width, height: size.height }}>
{/* Content */}
</div>
);
}
9.useImperativeHandle: Exposes imperative methods from a functional component to its parent component.
import React, { forwardRef, imperativeHandle, useState } from 'react';
const MyInput = forwardRef((props, ref) => {
const [value, setValue] = useState('');
imperativeHandle(ref, () => ({
focus: () => inputRef.current.focus(),
getValue: () => value,
}));
const inputRef = useRef(null);
const handleChange = (event) => {
setValue(event.target.value);
};
return <input ref={inputRef} value={value} onChange={handleChange} />;
});
function ParentComponent() {
const inputRef = useRef(null);
const focusInput = () => {
if (inputRef.current) {
inputRef.current.focus();
}
return (
<div>
<MyInput ref={inputRef} />
<button onClick={focusInput}>Focus Input</button>
</div>
);
}
10.useDebugValue: (for development only) Allows you to display a custom value in the React DevTools for debugging purposes.
import { useDebugValue } from 'react'; function useOnlineStatus() {
// ...
useDebugValue(isOnline ? 'Online' : 'Offline');
// ...
}