Hello , Fellow Developers! ๐
Welcome to Engineer's Tutorial! ๐ I'm thrilled to have you here. Whether you're just starting out or looking to sharpen your skills, you're in the right place. Let's dive into the world of React Hooks together! ๐ปโจ
What Are React Hooks?
React Hooks are built-in functions that let you use state and other features in your functional components. Before Hooks, you needed to write class components to do this, but now you can use simple functions, making your code easier to write and understand.
Why Should You Use Hooks?
- Simplify Your Code: Hooks let you add state and side effects to your components without writing a lot of code.
- Reuse Logic Easily: With custom hooks, you can extract reusable logic from your components and share it across your app.
- No More Classes: You don't need to worry about confusing
this
keyword or lifecycle methods in class components.
Table of Hooks and Use Cases:
Hooks | Use Cases |
---|---|
useState | Managing component state, like form inputs or toggling UI elements. |
useEffect | Handling side effects, like data fetching or subscriptions. |
useContext | Accessing global data without prop drilling. |
useReducer | Managing complex state logic, like multiple actions on a counter. |
useCallback | Memoizing functions to prevent unnecessary re-renders. |
useMemo | Optimizing performance by memoizing computed values. |
useRef | Accessing DOM elements directly, storing mutable values without re-renders. |
useLayoutEffect | Running effects after DOM updates but before painting on screen. |
useImperativeHandle | Customizing instance values when using `ref` in parent components. |
useDebugValue | Adding custom labels to display in React DevTools. |
Getting Started with Basic Hooks
Let's understand detailed Description of Each Hook with Example:
useState
Description: useState
is used to declare state variables in functional components.
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>
);
}
useEffect
Description: useEffect
handles side effects like fetching data, directly replacing lifecycle methods such as componentDidMount
.
Example:
import React, { useState, useEffect } from 'react';
function DataFetcher() {
const [data, setData] = useState(null);
useEffect(() => {
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => setData(data));
}, []); // Run once after the first render
return <div>{data ? <pre>{JSON.stringify(data, null, 2)}</pre> : "Loading..."}</div>;
}
useContext
Description: useContext
allows you to use values from a context without manually passing props through every component.
Example:
import React, { useContext } from 'react';
const ThemeContext = React.createContext();
function ThemeButton() {
const theme = useContext(ThemeContext);
return <button style={{ background: theme }}>Click me</button>;
}
useReducer
Description: `useReducer` is useful for managing complex state logic, similar to `redux`.
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>
Count: {state.count}
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
</div>
);
}
useCallback
Description: useCallback
is used to memoize functions so that they donโt get re-created on every render, which can be important for performance optimization.
Example:
import React, { useState, useCallback } from 'react';
function Button({ handleClick, children }) {
return <button onClick={handleClick}>{children}</button>;
}
function Counter() {
const [count, setCount] = useState(0);
const increment = useCallback(() => {
setCount((prevCount) => prevCount + 1);
}, []);
return (
<div>
<p>Count: {count}</p>
<Button handleClick={increment}>Increment</Button>
</div>
);
}
export default Counter;
useMemo
Description: useMemo
is used to memoize expensive calculations so that they are only recalculated when necessary.
Example:
import React, { useState, useMemo } from 'react';
function ExpensiveCalculation({ num }) {
const computeFactorial = (n) => {
if (n === 0) return 1;
return n * computeFactorial(n - 1);
};
const factorial = useMemo(() => computeFactorial(num), [num]);
return <p>Factorial of {num} is {factorial}</p>;
}
function App() {
const [number, setNumber] = useState(5);
return (
<div>
<ExpensiveCalculation num={number} />
<button onClick={() => setNumber(number + 1)}>Increment Number</button>
</div>
);
}
export default App;
useRef
Description: useRef
is used to access DOM elements directly or to persist values across renders without causing a re-render.
Example:
import React, { useRef } from 'react';
function TextInput() {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current.focus();
};
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={focusInput}>Focus Input</button>
</div>
);
}
export default TextInput;
useLayoutEffect
Description: useLayoutEffect
is similar to useEffect
, but it runs synchronously after all DOM mutations and before the browser has a chance to paint, making it ideal for measurements or adjustments to the DOM.
Example:
import React, { useState, useLayoutEffect, useRef } from 'react';
function MeasureComponent() {
const [height, setHeight] = useState(0);
const divRef = useRef();
useLayoutEffect(() => {
setHeight(divRef.current.getBoundingClientRect().height);
}, []);
return (
<div ref={divRef} style={{ padding: '20px', backgroundColor: 'lightblue' }}>
<p>The height of this div is {height}px.</p>
</div>
);
}
export default MeasureComponent;
useImperativeHandle
Description: useImperativeHandle
customizes the instance value that is exposed when using ref
in parent components, allowing you to control what is accessible to the parent.
Example:
import React, { useRef, useImperativeHandle, forwardRef } from 'react';
const FancyInput = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
scrollToTop: () => {
window.scrollTo(0, 0);
}
}));
return <input ref={inputRef} type="text" />;
});
function ParentComponent() {
const fancyInputRef = useRef();
return (
<div>
<FancyInput ref={fancyInputRef} />
<button onClick={() => fancyInputRef.current.focus()}>Focus Input</button>
<button onClick={() => fancyInputRef.current.scrollToTop()}>Scroll to Top</button>
</div>
);
}
export default ParentComponent;
useDebugValue
Description: useDebugValue
is used to display a label in React DevTools for custom hooks, aiding in debugging.
Example:
import React, { useState, useDebugValue } from 'react';
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(false);
useDebugValue(isOnline ? 'Online' : 'Offline');
// Simulate a subscription to a friend's status
useEffect(() => {
const handleStatusChange = (status) => {
setIsOnline(status.isOnline);
};
// Fake API call to subscribe to friend's status
fakeAPI.subscribe(friendID, handleStatusChange);
return () => {
fakeAPI.unsubscribe(friendID, handleStatusChange);
};
}, [friendID]);
return isOnline;
}
function FriendStatus({ friendID }) {
const isOnline = useFriendStatus(friendID);
return <div>{isOnline ? 'Online' : 'Offline'}</div>;
}
export default FriendStatus;
Achieving Different Lifecycle Methods Using Hooks:
Lifecycle Method | Equivalent Hook |
---|---|
componentDidMount |
|
componentDidUpdate |
|
componentWillUnmount |
|
getDerivedStateFromProps | Combined use of |
shouldComponentUpdate | Optimized using |
componentWillMount | Not directly supported, handled with other techniques |
Creating Your Own Hooks
Custom hooks are a great way to reuse logic across your components. If you find yourself writing the same code in multiple places, you can create a custom hook to make your code more DRY (Don't Repeat Yourself).
useFetch
- Custom Hook should start with "use"
Example:
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url)
.then(response => response.json())
.then(data => {
setData(data);
setLoading(false);
});
}, [url]);
return { data, loading };
}
Do's, Don'ts, and Points to Remember:
Do's: | Don'ts: |
---|---|
Use hooks in functional components only. | Donโt use hooks inside loops, conditions, or nested functions. |
Keep your components small and focused. | Avoid overusing hooks if they donโt simplify your code. |
Always include dependencies in useEffect when needed. | Donโt forget to clean up effects in useEffect when necessary. |
Ref: React official website, Mdn web docs
Happy coding! ๐๐๐