Introduction
React hooks are functions that allow you to use state and other React features in functional components. They enable you to manage component lifecycle events, side effects, and context without needing to write class components. Here’s an overview of the most commonly used React hooks along with small examples for each.
1. useState
The useState
hook lets you add state to functional components.
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> ); } export default Counter;
2. useEffect
The useEffect
hook lets you perform side effects in function components, such as data fetching, subscriptions, or manually changing the DOM.
import React, { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState(0); useEffect(() => { 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> ); } export default Example;
3. useContext
The useContext
hook allows you to access the context values directly without needing to wrap components in the Context.Consumer
.
import React, { useContext } from 'react'; const ThemeContext = React.createContext('light'); function ThemedButton() { const theme = useContext(ThemeContext); return ( <button style={{ background: theme === 'dark' ? '#333' : '#FFF' }}> {theme === 'dark' ? 'Dark Theme' : 'Light Theme'} </button> ); } function App() { return ( <ThemeContext.Provider value="dark"> <ThemedButton /> </ThemeContext.Provider> ); } export default App;
4. useReducer
The useReducer
hook is an alternative to useState
for managing complex state logic in components. It is particularly useful when the next state depends on the previous one.
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: throw new Error(); } } function Counter() { const [state, dispatch] = useReducer(reducer, initialState); return ( <> Count: {state.count} <button onClick={() => dispatch({ type: 'increment' })}>+</button> <button onClick={() => dispatch({ type: 'decrement' })}>-</button> </> ); } export default Counter;
5. useRef
The useRef
hook allows you to create mutable references that persist for the full lifetime of the component. It can be used to directly access DOM elements.
import React, { useRef } from 'react'; function FocusInput() { const inputRef = useRef(null); const focusInput = () => { inputRef.current.focus(); }; return ( <div> <input ref={inputRef} type="text" /> <button onClick={focusInput}>Focus the input</button> </div> ); } export default FocusInput;
5. useMemo
The useMemo
hook returns a memoized value. It is useful for optimizing performance by memoizing expensive calculations.
import React, { useState, useMemo } from 'react'; function Example() { const [count, setCount] = useState(0); const expensiveCalculation = (num) => { console.log('Calculating...'); return num * 2; }; const memoizedValue = useMemo(() => expensiveCalculation(count), [count]); return ( <div> <p>Count: {count}</p> <p>Memoized Value: {memoizedValue}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); } export default Example;
7. useCallback
The useCallback
hook returns a memoized callback function, which can help prevent unnecessary re-renders of child components.
import React, { useState, useCallback } from 'react'; function Button({ handleClick }) { console.log('Rendering button'); return <button onClick={handleClick}>Click me</button>; } function Example() { const [count, setCount] = useState(0); const handleClick = useCallback(() => { console.log('Button clicked'); }, []); // Only recreate the function if dependencies change return ( <div> <p>Count: {count}</p> <Button handleClick={handleClick} /> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); } export default Example;
8. useLayoutEffect
The useLayoutEffect
hook is similar to useEffect
, but it runs synchronously after all DOM mutations. It is useful for reading layout from the DOM and synchronously re-rendering.
import React, { useLayoutEffect, useRef } from 'react'; function LayoutEffectExample() { const divRef = useRef(null); useLayoutEffect(() => { console.log('Div height:', divRef.current.offsetHeight); }); return <div ref={divRef}>Hello, world!</div>; } export default LayoutEffectExample;
9. useDebugValue
The useDebugValue
hook is used to display a label for custom hooks in React DevTools. It helps to provide a debug value that can show useful information about the hook.
import React, { useDebugValue, useState } from 'react'; function useCustomHook(value) { useDebugValue(value ? 'Value is present' : 'No value'); return value; } function DebugExample() { const [value, setValue] = useState(false); useCustomHook(value); return ( <div> <button onClick={() => setValue(!value)}> Toggle Value </button> </div> ); } export default DebugExample;
10. useImperativeHandle
The useImperativeHandle
hook customizes the instance value that is exposed to parent components when using ref
. It is typically used in conjunction with React.forwardRef
to allow parent components to interact with child components more easily.
import React, { useImperativeHandle, forwardRef, useRef } from 'react'; const CustomInput = forwardRef((props, ref) => { const inputRef = useRef(); useImperativeHandle(ref, () => ({ focus: () => { inputRef.current.focus(); }, getValue: () => { return inputRef.current.value; }, })); return <input ref={inputRef} type="text" />; }); function ParentComponent() { const inputRef = useRef(); const handleFocus = () => { inputRef.current.focus(); }; const handleGetValue = () => { alert(inputRef.current.getValue()); }; return ( <div> <CustomInput ref={inputRef} /> <button onClick={handleFocus}>Focus Input</button> <button onClick={handleGetValue}>Get Input Value</button> </div> ); } export default ParentComponent;
Two important hooks from Redux
11. useSelector (from Redux)
The useSelector
hook is used to extract data from the Redux store state. It allows you to subscribe to the Redux store and access its state within functional components.
import React from 'react'; import { useSelector } from 'react-redux'; function Counter() { // Use useSelector to get the count value from the Redux store const count = useSelector((state) => state.counter.value); return ( <div> <p>Count: {count}</p> </div> ); } export default Counter;
12. useDispatch (from Redux)
The useDispatch
hook returns a reference to the dispatch
function from the Redux store. You can use it to dispatch actions to the store.
import React from 'react'; import { useDispatch } from 'react-redux'; import { increment, decrement } from './counterSlice'; // Assuming you have a slice for your counter function CounterControls() { const dispatch = useDispatch(); return ( <div> <button onClick={() => dispatch(increment())}>Increment</button> <button onClick={() => dispatch(decrement())}>Decrement</button> </div> ); } export default CounterControls;
Summary of React Hooks
useState
: Manage state in functional components.useEffect
: Handle side effects like data fetching or subscriptions.useContext
: Access context directly in components.useReducer
: Manage complex state logic.useRef
: Create mutable references to DOM elements.useMemo
: Memoize expensive calculations.useCallback
: Memoize callback functions to prevent re-renders.useLayoutEffect
: Handle layout effects synchronously after DOM mutations.useDebugValue
: Display debug information for custom hooks in DevTools.useImperativeHandle
: Customize the ref instance that is exposed to parent components, allowing them to access specific functions or properties from the child component.useSelector
: Extract data from the Redux store, allowing functional components to subscribe to the Redux state.useDispatch
: Access thedispatch
function from the Redux store, enabling you to dispatch actions to update the state.
These hooks provide powerful tools for building functional components in React, improving code organization and performance. Feel free to ask if you have any questions or need further clarification on any of the hooks!
Custom hooks
Custom hooks in React are a way to extract and reuse logic between components.
Reusability
Custom hooks allow you to reuse logic across different components. Instead of duplicating the same logic (like fetching data) in multiple components, you can create a custom hook that can be shared.
- Example: If multiple components need to fetch data from different APIs, you can use the same
useFetch
hook with different URLs.
Let’s create the Custom Hook to fetch data.
import { useState, useEffect } from 'react'; const useFetch = (url) => { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { const fetchData = async () => { try { const response = await fetch(url); if (!response.ok) { throw new Error(`Error: ${response.status}`); } const result = await response.json(); setData(result); } catch (err) { setError(err.message); } finally { setLoading(false); } }; fetchData(); }, [url]); return { data, loading, error }; }; export default useFetch;
Usage of Custom hook
import React from 'react'; import useFetch from './useFetch'; // Assume it's in the same directory const UsersList = () => { const { data, loading, error } = useFetch('https://jsonplaceholder.typicode.com/users'); if (loading) return <p>Loading...</p>; if (error) return <p>Error: {error}</p>; return ( <div> <h1>Users List</h1> <ul> {data.map(user => ( <li key={user.id}>{user.name}</li> ))} </ul> </div> ); }; export default UsersList;