React Hooks are functions that allow you to use state and other React features without writing a class. Introduced in React 16.8, they provide a powerful and simpler way to manage state, handle side effects, and reuse logic across components.
In this article, we’ll dive into the most commonly used hooks such as : useState
, useEffect
, useContext
, useReducer
, and useRef
. We’ll also walk through how to create a custom hook with an example for managing form data, which is useful when sending data to an API.
useState
– Managing State in Functional Components- The
useState
hook is the simplest way to manage state in a functional component. It allows you to add state variables to your component, and its syntax is straightforward:
- The
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 setCount
is used to update the state. Each time the button is clicked, the count increases by 1.
useEffect
– Handling Side EffectsuseEffect
allows you to perform side effects in your components, such as fetching data, updating the DOM, or setting up subscriptions.- useEffect allows mainly the following life cycle methods to be achieved
- componentDidUpdate
- componentDidMount
- componentWillUnmount
import React, { useState, useEffect } from 'react'; function Timer() { const [seconds, setSeconds] = useState(0); useEffect(() => { const interval = setInterval(() => { setSeconds(prevSeconds => prevSeconds + 1); }, 1000); return () => clearInterval(interval); // Cleanup on component unmount }, []); return <div>Timer: {seconds} seconds</div>; }
Here, useEffect
sets up a timer that updates every second. The cleanup function inside useEffect
ensures that the interval is cleared when the component unmounts.
useContext
– Sharing State Between ComponentsuseContext
is used to share state across components without having to pass props manually at every level. It’s often used with the React Context API.
import React, { useContext } from 'react'; const ThemeContext = React.createContext('light'); function ThemedButton() { const theme = useContext(ThemeContext); return <button className={theme}>I'm a {theme} themed button</button>; } function App() { return ( <ThemeContext.Provider value="dark"> <ThemedButton /> </ThemeContext.Provider> ); }
In this example, ThemedButton
uses useContext
to consume the theme value from ThemeContext
. The theme is provided by the ThemeContext.Provider
in the App
component.
useReducer
– Complex State LogicuseReducer
is useful when you have complex state logic that involves multiple sub-values or 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 ( <div> <p>Count: {state.count}</p> <button onClick={() => dispatch({ type: 'increment' })}>+</button> <button onClick={() => dispatch({ type: 'decrement' })}>-</button> </div> ); }
Here, useReducer
is used to manage the count state with more complex logic than useState
. The reducer
function handles actions to update the state
useRef
– Accessing DOM Elements and Persisting ValuesuseRef
provides a way to access DOM elements directly or persist values across renders without causing re-renders.
import React, { useRef } from 'react'; function TextInputWithFocusButton() { const inputRef = useRef(null); const handleClick = () => { inputRef.current.focus(); }; return ( <div> <input ref={inputRef} type="text" /> <button onClick={handleClick}>Focus the input</button> </div> ); }
In this example, useRef
is used to create a reference to the input element, allowing us to focus it when the button is clicked.
- Custom Hook for Form Data
- Custom hooks allow you to extract and reuse logic across multiple components. Let’s create a custom hook to manage form data, which we’ll send to an API.
import { useState } from 'react'; function useFormData(initialValues) { const [formData, setFormData] = useState(initialValues); const handleChange = (event) => { const { name, value } = event.target; setFormData(prevData => ({ ...prevData, [name]: value })); }; const resetForm = () => { setFormData(initialValues); }; return { formData, handleChange, resetForm }; } function ContactForm() { const { formData, handleChange, resetForm } = useFormData({ name: '', email: '' }); const handleSubmit = async (event) => { event.preventDefault(); // Here, you would typically send formData to an API console.log(formData); resetForm(); }; return ( <form onSubmit={handleSubmit}> <label> Name: <input type="text" name="name" value={formData.name} onChange={handleChange} /> </label> <br /> <label> Email: <input type="email" name="email" value={formData.email} onChange={handleChange} /> </label> <br /> <button type="submit">Submit</button> </form> ); }
This custom hook useFormData
manages form state, handling input changes and resetting the form. It’s reusable and keeps your component logic clean.
React Hooks provide a powerful and intuitive way to manage state, handle side effects, and encapsulate reusable logic in your components. By understanding and using hooks like useState
, useEffect
, useContext
, useReducer
, and useRef
, you can write more concise and maintainable code.