{message}
\\ \\ }\\ \\ ### Three Ways to Write the Dependency Array\\ \\ | Writing | Execution Time | Use Case |\\ | --- | --- | --- |\\ | `useEffect(fn)` | Executes after every render | Rarely used, easily causes infinite loops |\\ | `useEffect(fn, [])` | Executes only once after the first render | Data loading, initializing third-party libraries |\\ | `useEffect(fn, )` | Executes after first render + when dep changes | Listening for specific data changes, performing responsive operations |\\ \\ > The difference between omitting the dependency array vs an empty array: `useEffect(fn)` executes after every render; `useEffect(fn, [])` executes only once after the first render. The formerEasily causes an infinite loop (effect in setState β Trigger render β Re-execute effect β ...οΌοΌAlmost never used.\\ \\ * * *\\ \\ ## Loading JSON Data with fetch\\ \\ Just like Vue3, we put the article data in the `public/posts.json` file.\\ \\ ### Step 1: Create the Data File\\ \\ ## Example\\ \\ // File path: public/posts.json\\ \\ [\\ \\ {\\ \\ "id": 1,\\ \\ "title": "React Getting StartedComplete Guide",\\ \\ "summary": "Learn React Hooks from scratch, covering core concepts like useState, useEffect, etc.",\\ \\ "content": "Why learn React?
React is one of the most popular frontend frameworks today...
",\\ \\ "category": "React",\\ \\ "date": "2024-05-10"\\ \\ },\\ \\ {\\ \\ "id": 2,\\ \\ "title": "JavaScript Asynchronous Programming Explained",\\ \\ "summary": "Understand Promise, async/await, event loop, and microtask queue in one article.",\\ \\ "content": "What is asynchronous?
JS is single-threaded...
",\\ \\ "category": "JavaScript",\\ \\ "date": "2024-05-08"\\ \\ },\\ \\ {\\ \\ "id": 3,\\ \\ "title": "CSS Grid Layout in Practice",\\ \\ "summary": "Use CSS Grid Easily implement complex responsive layouts.",\\ \\ "content": "Grid Getting Started
Grid is a two-dimensional layout system...
",\\ \\ "category": "CSS",\\ \\ "date": "2024-05-05"\\ \\ }\\ \\ ]\\ \\ ### Step 2: Load Data in useEffect\\ \\ ## Example\\ \\ // File path: src/pages/HomePage.jsx\\ \\ import{ useState, useEffect, useMemo } from 'react'\\ \\ import BlogCard from '../components/BlogCard'\\ \\ import CategoryFilter from '../components/CategoryFilter'\\ \\ function HomePage(){\\ \\ const[articles, setArticles]= useState([])// Article data\\ \\ const[isLoading, setIsLoading]= useState(true)// Loading state\\ \\ const[error, setError]= useState(null)// Error message\\ \\ const[activeCategory, setActiveCategory]= useState('All')\\ \\ // useEffect Load data: Empty dependency array = Execute only once after the first render\\ \\ useEffect(()=>{\\ \\ let cancelled =false// Flag to prevent setState after component unmount\\ \\ async function fetchPosts(){\\ \\ setIsLoading(true)\\ \\ setError(null)\\ \\ try{\\ \\ const res = await fetch('/posts.json')\\ \\ if(!res.ok)throw new Error(`HTTP ${res.status}`)\\ \\ const data = await res.json()\\ \\ if(!cancelled){\\ \\ setArticles(data)\\ \\ }\\ \\ }catch(err){\\ \\ if(!cancelled){\\ \\ setError(err.message)\\ \\ }\\ \\ }finally{\\ \\ if(!cancelled){\\ \\ setIsLoading(false)\\ \\ }\\ \\ }\\ \\ }\\ \\ fetchPosts()\\ \\ // Cleanup function: Set cancelled to true on component unmount\\ \\ return()=>{ cancelled =true}\\ \\ },[])\\ \\ const categories = useMemo(()=>{\\ \\ return['All', ...new Set(articles.map(a => a.category))]\\ \\ },)\\ \\ const filteredArticles = useMemo(()=>{\\ \\ if(activeCategory ==='All')return articles\\ \\ return articles.filter(a => a.category=== activeCategory)\\ \\ },[articles, activeCategory])\\ \\ return(\\ \\\\
\\
Latest Posts
\\ \\ {/* Loading */}\\ \\ {isLoading &&LoadingοΌPlease wait...
}\\ \\ {/* Loading error */}\\ \\ {error &&(\\ \\\\
\\
\\
\\
)}\\
\\
{/* Data loading complete */}\\
\\
{!isLoading &&!error &&(\\
\\
Load failed:{error}
\\ \\ \\ \\Total {filteredArticles.length} posts
\\ \\ {filteredArticles.length===0?(\\ \\No Posts in This Category
\\ \\ ):(\\ \\\\
\\
{filteredArticles.map(article =>(\\
\\
))}\\
\\
\\
\\
)}\\
\\
>\\
\\
)}\\
\\
</div>\\
\\
)\\
\\
}\\
\\
export default HomePage\\
\\
> The `cancelled` flag is a common pattern in React. When the fetch is still in progress, if the user leaves the page (component unmounts), subsequent setState calls will cause warning errors. Setting `cancelled = true` in the cleanup function can skip setState after unmounting.\\
\\
* * *\\
\\
## Cleanup Function\\
\\
The callback function of useEffect can return a function, called the cleanup function.\\
\\
It executes at two times: when the component unmounts and before the next effect executes.\\
\\
## Example\\
\\
useEffect(()=>{\\
\\
// Side Effect: Subscribe to events\\
\\
const handleScroll =()=> console.log('Scrolled')\\
\\
window.addEventListener('scroll', handleScroll)\\
\\
// Return cleanup function\\
\\
return()=>{\\
\\
// Automatically remove event listeners on component unmount to prevent memory leaks\\
\\
window.removeEventListener('scroll', handleScroll)\\
\\
}\\
\\
},[])\\
\\
useEffect(()=>{\\
\\
// Side Effect: Set a timer\\
\\
const timer = setInterval(()=>{\\
\\
console.log('Execute every second')\\
\\
},1000)\\
\\
return()=> clearInterval(timer)// Clean up timer\\
\\
},[])\\
\\
| Side Effect | Needs Cleanup? | Cleanup Method |\\
| --- | --- | --- |\\
| fetch requests | Yes | Use cancelled flag or AbortController |\\
| Event listeners | Yes | removeEventListener |\\
| Timers | Yes | clearInterval / clearTimeout |\\
| Restoring after DOM modification | Yes | Restore original state in cleanup |\\
| localStorage writes | No | No cleanup needed |\\
\\
* * *\\
\\
## Handling Four States of Async Requests\\
\\
Any data request should cover these four UI states:\\
\\
| State | Condition | UI |\\
| --- | --- | --- |\\
| Loading | isLoading === true | Loading animation, skeleton screen |\\
| Success | Data has returned | Normal content rendering |\\
| Failure | error !== null | Error message + retry button |\\
| Empty Data | Data is an empty array | Empty state message |\\
\\
* * *\\
\\
## Comparison with Vue3 Lifecycle\\
\\
| Vue3 | React | Description |\\
| --- | --- | --- |\\
| `onMounted` | `useEffect(fn, [])` | Executes after first render |\\
| `watch(keyword, fn)` | `useEffect(fn, )` | Responds to specific data changes |\\
| `onBeforeUnmount` | `useEffect cleanup` | Cleanup before component destruction |\\
| No direct equivalent | `useEffect(() => { fn; return cleanup }, )` | Cleanup first, then fn when dep changes |\\
\\
* * *\\
\\
## Chapter Summary\\
\\
In this chapter, you mastered the core usage of useEffect: the three ways to write dependency arrays, loading data asynchronously with fetch, handling four UI states, and the role of the cleanup function.\\
\\
Now the blog data is dynamically loaded from an external JSON file, no longer hardcoded in JS.
YouTip