React Hooks Usestate Useeffect
007 - React Hooks: useState and useEffect
React Hooks are functions that let you "hook into" React state and lifecycle features from functional components. They allow you to use state and other React features without writing a class. The two most fundamental hooks are useState and useEffect.
useState Hook
useState is a Hook that lets you add React state to functional components. It returns a pair of values: the current state, and a function that updates it.
Syntax
const [stateVariable, setStateVariable] = useState(initialValue);stateVariable: The current value of the state.setStateVariable: A function to update thestateVariable. When this function is called, React will re-render the component.initialValue: The initial value for the state. This can be any JavaScript data type (number, string, boolean, object, array, etc.).
Example from src/pages/BlogPostPage.js
// src/pages/BlogPostPage.js const BlogPostPage = () => { // ... const [post, setPost] = useState(null); const [loading, setLoading] = useState(true); const [readingProgress, setReadingProgress] = useState(0); const [isAtTop, setIsAtTop] = useState(true); // New state for tracking if at top const [isModalOpen, setIsModalToOpen] = useState(false); const [modalContent, setModalContent] = useState(''); // ... };In BlogPostPage:
[post, setPost] = useState(null):postwill hold the blog post data (attributes, body, series posts). It's initialized tonullbecause the data is fetched asynchronously.[loading, setLoading] = useState(true):loadingis a boolean that indicates whether the post data is currently being fetched. It starts astrue.[readingProgress, setReadingProgress] = useState(0):readingProgressstores the user's scroll progress on the page, initialized to0.[isAtTop, setIsAtTop] = useState(true): Tracks if the user is at the top of the page.[isModalOpen, setIsModalToOpen] = useState(false): Controls the visibility of a modal, initialized tofalse(closed).[modalContent, setModalContent] = useState(''): Stores the content to be displayed inside the modal.
Example from src/components/Layout.js
// src/components/Layout.js const Layout = ({ children }) => { const [isSidebarOpen, setIsSidebarOpen] = useState(window.innerWidth > 768); // ... };In Layout:
[isSidebarOpen, setIsSidebarOpen] = useState(window.innerWidth > 768):isSidebarOpencontrols the visibility of the sidebar. Its initial value depends on the window width, making the sidebar open by default on larger screens.
useEffect Hook
useEffect is a Hook that lets you perform side effects in functional components. Side effects include data fetching, subscriptions, manually changing the DOM, and other operations that interact with the outside world. It runs after every render of the component by default, but you can control when it runs using its dependency array.
Syntax
useEffect(() => { // Side effect code here return () => { // Cleanup function (optional) }; }, [dependency1, dependency2]); // Dependency array (optional)- First argument (function): This is where you put your side effect code. It can optionally return a cleanup function.
- Second argument (dependency array): This array controls when the effect re-runs.
- If omitted, the effect runs after every render.
- If an empty array
[], the effect runs only once after the initial render (likecomponentDidMount). The cleanup runs on unmount (likecomponentWillUnmount). - If it contains variables (e.g.,
[prop1, state1]), the effect runs after the initial render and whenever any of the variables in the array change.
Example from src/pages/BlogPostPage.js (Data Fetching)
// src/pages/BlogPostPage.js useEffect(() => { const fetchPost = async () => { setLoading(true); // ... data fetching logic using fetch API ... setLoading(false); }; fetchPost(); }, [currentSlug]); // Effect re-runs when currentSlug changesThis useEffect hook is responsible for fetching the blog post data.
- It defines an
asyncfunctionfetchPostto handle the asynchronous data retrieval. setLoading(true)is called at the start to show a loading indicator.- The
fetchAPI is used to get the.txtcontent andshownPosts.jsonmetadata. - Crucially, the dependency array
[currentSlug]ensures that this effect runs only when thecurrentSlug(derived from the URL parameters) changes. This prevents unnecessary re-fetches and ensures the correct post is loaded when navigating between posts.
Example from src/pages/BlogPostPage.js (Scroll Event Listener)
// src/pages/BlogPostPage.js useEffect(() => { const handleScroll = () => { if (contentRef.current) { const { scrollTop, scrollHeight, clientHeight } = document.documentElement; const totalHeight = scrollHeight - clientHeight; const currentProgress = (scrollTop / totalHeight) * 100; setReadingProgress(currentProgress); setIsAtTop(scrollTop === 0); // Update isAtTop based on scroll position } }; window.addEventListener('scroll', handleScroll); return () => window.removeEventListener('scroll', handleScroll); }, [post]); // Re-attach scroll listener if post changesThis useEffect manages a scroll event listener to calculate reading progress and determine if the user is at the top of the page.
- It adds an event listener to the
windowwhen the component mounts or when thepoststate changes. - The
return () => { ... }part is a cleanup function. This function runs when the component unmounts or before the effect re-runs due to a dependency change. It's essential here to remove the event listener to prevent memory leaks and unexpected behavior. - The dependency array
[post]means the effect (and its cleanup) will re-run if thepostobject changes, ensuring the scroll listener is correctly attached to the relevant content.
Example from src/components/Layout.js (Window Resize Listener)
// src/components/Layout.js useEffect(() => { const handleResize = () => { if (window.innerWidth <= 768) { setIsSidebarOpen(false); } }; window.addEventListener('resize', handleResize); return () => { window.removeEventListener('resize', handleResize); }; }, []); // Empty dependency array: runs once on mount, cleans up on unmountThis useEffect in Layout.js handles the sidebar's initial state and responsiveness.
- It adds a
resizeevent listener to the window. - The
handleResizefunction closes the sidebar if the window width drops below 768 pixels. - The empty dependency array
[]ensures that this effect runs only once after the initial render and its cleanup function runs only when the component unmounts. This is perfect for setting up global event listeners that don't need to be re-initialized unless the component is completely removed from the DOM.
Summary
useState and useEffect are powerful tools that bring state management and side effect handling to functional components, making them as capable as class components while often being more concise and easier to reason about. Understanding their usage, especially the role of the dependency array in useEffect, is fundamental to building robust React applications.