AI News Hub Logo

AI News Hub

useEffect in React — the invisible engine behind real apps

DEV Community
Kathirvel S

In Part 1 of “Let’s Master React Hooks Together” we focused on useState — how React remembers things, updates UI, and keeps everything in sync. You learned how to control your app. But then comes the next question: What happens when your app needs to talk to the outside world? You open Instagram, blink once… and boom — your feed is alive. Posts load. Stories appear. Notifications sneak in. Everything feels instant. But here’s what most beginners don’t realize: 👉 React didn’t do that. Everything after that — fetching data, syncing with APIs, setting up listeners… That’s where useEffect steps in. This is where React stops being just about state… And once you truly understand this hook: You stop just managing state… and start thinking like React. useEffect… really? Let’s skip the robotic definition for a second. 👉 useEffect lets your component do things after it renders. Now the official version (just so you’re covered): “The Effect Hook lets you perform side effects in function components.” Cool. But what does that actually mean? Relax. It’s simpler than it sounds. A side effect is anything your component does that is not just returning UI. Think about real apps: Fetching data from a server Updating the page title Listening to scroll or clicks Running a timer Saving something in local storage 👉 These don’t directly render JSX — but they make your app feel alive. useEffect even exist? Because React is strict about one thing: Rendering must stay pure. That means: Same input → same UI output No hidden behavior No randomness during render But real apps? Total chaos Let’s bring back the Instagram example: Open app → fetch posts Like a post → send data to server Scroll → load more content 👉 None of this is “rendering UI.” So React needed a safe place for this “messy” stuff. And it basically said: “Do whatever you want… just don’t do it during render.” That safe place = useEffect. Imagine writing this: function Feed() { const data = fetchPosts(); // ❌ dangerous return {data}; } Looks innocent. But here’s what really happens: → Component renders fetchPosts() runs You’ve just created a loop. Now watch the same thing done properly: function Feed() { const [posts, setPosts] = useState([]); useEffect(() => { fetchPosts().then(setPosts); }, []); return {posts.length} posts; } First render → UI shows empty state useEffect runs → fetch starts 👉 No chaos. No loops. Just control. Forget technical jargon. Think like this: “What should happen after the screen updates?” That answer = your useEffect. useEffect Here’s the shape: useEffect(() => { // effect logic return () => { // cleanup logic }; }, [dependencies]); Now let’s feel what each part does. useEffect(() => { console.log("Rendering happened"); }); Render → effect runs 👉 No control. Just chaos checking everything. useEffect(() => { console.log("Component loaded"); }, []); Component appears → effect runs once 👉 Perfect for API calls, initial setup. useEffect(() => { console.log("User changed"); }, [user]); Initial render → runs user changes → runs again 👉 This is where real apps live. useEffect(() => { const interval = setInterval(() => { console.log("Checking notifications..."); }, 3000); return () => { clearInterval(interval); }; }, []); Component loads → interval starts Now imagine leaving the page… Without cleanup: With cleanup: 👉 Cleanup runs: Before the effect runs again When the component disappears Forget the textbook words. Here’s what actually happens: 🟢 Component shows up useEffect runs 🟡 Something changes useEffect checks dependencies 🔴 Component goes away That’s the whole story. useEffect Use it when your app needs to: ✔ Fetch data (feeds, dashboards, profiles) Bad pattern: useEffect(() => { setFullName(first + last); }, [first, last]); This creates unnecessary re-renders. Better: const fullName = first + last; Nothing async 👉 Then you don’t need useEffect. function Stories({ userId }) { const [stories, setStories] = useState([]); useEffect(() => { let active = true; fetch(`/api/stories/${userId}`) .then(res => res.json()) .then(data => { if (active) setStories(data); }); return () => { active = false; }; }, [userId]); return ( {stories.map(story => ( ))} ); } Component appears → empty stories render Now if user switches quickly: 👉 This is how real apps avoid subtle bugs. Most beginners ask: “How do I use useEffect?” But better developers ask: “Should this even be an effect?” That one question will level you up faster than anything else. Whether it’s Instagram, YouTube, or anything dynamic… Ask yourself: What ran after the UI loaded? What triggered that update? What got cleaned up when I left? That curiosity turns confusion into clarity. So in this second part of “Let’s Master React Hooks Together” you moved beyond useState… …and stepped into useEffect. Not just a hook — but a mindset shift. Because useEffect is the bridge between: React’s clean, predictable state world When you understand that bridge: You stop writing “just code.” You know when things should run. And that’s when React starts to feel less like magic… In the next part, we’ll keep building on this — making your hooks cleaner, smarter, and more predictable.