React Hooks were introduced in React 16.8 to provide functional components with features previously exclusive to class components, such as state and lifecycle methods. Hooks allow for cleaner, more modular code, making it easier to share logic across components without relying on higher-order components or render props.
Basic Hooks
useState
useState is a Hook that allows you to add state to functional components. It returns a state variable and a function to update it.
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>
);
}
export default Counter;
useEffect
useEffect is a Hook for performing side effects in functional components, such as fetching data, directly updating the DOM, and setting up subscriptions. It runs after the render by default.
Example:
import React, { useState, useEffect } from 'react';
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCount(count + 1);
}, 1000);
return () => clearInterval(interval); // Cleanup on unmount
}, [count]); // Dependency array
return <div>{count}</div>;
}
export default Timer;
Advanced Hooks
useContext
useContext allows you to consume context in a functional component, providing a way to share values like themes or authenticated users across the component tree without prop drilling.
Example:
import React, { useContext, createContext } from 'react';
const ThemeContext = createContext('light');
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button style={{ background: theme === 'dark' ? '#333' : '#CCC' }}>I am a {theme} button</button>;
}
function App() {
return (
<ThemeContext.Provider value="dark">
<ThemedButton />
</ThemeContext.Provider>
);
}
export default App;
useReducer
useReducer is used for state management in more complex scenarios, similar to Redux. It is useful for managing state transitions.
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:
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>
);
}
export default Counter;
useRef
useRef returns a mutable ref object whose .current property is initialized to the passed argument. It can persist across renders.
Example:
import React, { useRef } from 'react';
function TextInput() {
const inputEl = useRef(null);
const handleClick = () => {
inputEl.current.focus();
};
return (
<div>
<input ref={inputEl} type="text" />
<button onClick={handleClick}>Focus the input</button>
</div>
);
}
export default TextInput;
useMemo
useMemo is used to memoize expensive calculations, returning a memoized value only if the dependencies have changed.
Example:
import React, { useState, useMemo } from 'react';
function ExpensiveCalculationComponent({ num }) {
const calculate = (num) => {
console.log('Calculating...');
return num * 2;
};
const memoizedValue = useMemo(() => calculate(num), [num]);
return <div>The result is: {memoizedValue}</div>;
}
function App() {
const [num, setNum] = useState(1);
return (
<div>
<input type="number" value={num} onChange={(e) => setNum(e.target.value)} />
<ExpensiveCalculationComponent num={num} />
</div>
);
}
export default App;
useCallback
useCallback returns a memoized callback function that only changes if one of the dependencies changes. It is useful for preventing unnecessary re-renders of child components.
Example:
import React, { useState, useCallback } from 'react';
function Button({ handleClick }) {
return <button onClick={handleClick}>Click me</button>;
}
function App() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount(count + 1);
}, [count]);
return (
<div>
<p>Count: {count}</p>
<Button handleClick={handleClick} />
</div>
);
}
export default App;
Custom Hooks
Custom Hooks are JavaScript functions that start with "use" and can call other Hooks. They enable the reuse of stateful logic between components.
Example:
import React, { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchData() {
const response = await fetch(url);
const result = await response.json();
setData(result);
setLoading(false);
}
fetchData();
}, [url]);
return { data, loading };
}
function DataComponent({ url }) {
const { data, loading } = useFetch(url);
if (loading) return <div>Loading...</div>;
return <div>{JSON.stringify(data)}</div>;
}
function App() {
return <DataComponent url="https://api.example.com/data" />;
}
export default App;
Conclusion
React Hooks provide a powerful and flexible way to manage state and side effects in functional components. By understanding and leveraging the various hooks, such as useState, useEffect, useContext, useReducer, useRef, useMemo, and useCallback, as well as creating custom hooks, React developers can write cleaner, more maintainable code. This approach fosters better code reuse and simplifies complex component logic, enhancing the overall development experience.
Comentarios