{"id":4819,"date":"2026-06-20T21:55:54","date_gmt":"2026-06-20T16:25:54","guid":{"rendered":"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/"},"modified":"2026-06-20T21:55:54","modified_gmt":"2026-06-20T16:25:54","slug":"react-best-practices-build-scalable-apps-like-a-pro","status":"publish","type":"post","link":"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/","title":{"rendered":"React Best Practices: Build Scalable Apps Like a Pro"},"content":{"rendered":"<p><em>Sigh.<\/em><\/p>\n<p>I\u2019ve spent the last six hours staring at PR #8821, and I\u2019m convinced that the &#8220;modern frontend developer&#8221; is just three script kiddies in a trench coat fueled by TikTok tutorials and a complete lack of respect for the CPU. We are running React 18.3.1 on Node 20.x, and yet, somehow, the code being submitted looks like it was written by someone who thinks &#8220;garbage collection&#8221; is a city service and not a fundamental constraint of the V8 engine.<\/p>\n<p>I\u2019m tired. My espresso is cold. My terminal is bleeding red. And you? You\u2019re about to get a lesson in why your &#8220;clever&#8221; code is actually a pile of technical debt interest that I refuse to pay.<\/p>\n<p>Here is the &#8220;God Component&#8221; that some &#8220;Senior&#8221; Engineer actually had the audacity to mark as &#8220;Ready for Review.&#8221; Look at it. Smell the rot.<\/p>\n<pre class=\"codehilite\"><code class=\"language-jsx\">\/\/ PR #8821: &quot;Optimized User Dashboard&quot; - My foot.\nimport React, { useState, useEffect, useMemo, useCallback } from 'react';\n\nexport const UserDashboard = ({ userId, theme, trackMetrics }) =&gt; {\n  const [user, setUser] = useState(null);\n  const [posts, setPosts] = useState([]);\n  const [config, setConfig] = useState({ mode: 'dark', notifications: true });\n  const [search, setSearch] = useState('');\n\n  \/\/ Problem 1: The Infinite Loop of Despair\n  useEffect(() =&gt; {\n    const fetchData = async () =&gt; {\n      const res = await fetch(`\/api\/users\/${userId}`);\n      const data = await res.json();\n      setUser(data);\n      \/\/ This triggers a re-render, which triggers this effect again because...\n      trackMetrics({ type: 'view', id: userId }); \n    };\n    fetchData();\n  }, [userId, trackMetrics]); \/\/ trackMetrics is an anonymous function passed from parent. RIP.\n\n  \/\/ Problem 2: The Stale Closure Nightmare\n  const handleUpdate = useCallback(() =&gt; {\n    console.log(`Updating user ${user?.name} with config:`, config);\n    \/\/ Logic that relies on 'user' but 'user' is trapped in the initial closure\n  }, [config]); \n\n  \/\/ Problem 3: The &quot;I heard useMemo makes things fast&quot; Bloat\n  const filteredPosts = useMemo(() =&gt; {\n    return posts.filter(p =&gt; {\n      console.log('Filtering...'); \/\/ Runs on every keystroke in the search bar\n      return p.title.toLowerCase().includes(search.toLowerCase());\n    });\n  }, [posts, search]);\n\n  \/\/ Problem 4: Prop-Drilling Spaghetti\n  return (\n    &lt;div className={`dashboard-${theme}`}&gt;\n      &lt;Header user={user} \/&gt;\n      &lt;Sidebar config={config} setConfig={setConfig} \/&gt;\n      &lt;ContentArea \n        posts={filteredPosts} \n        onUpdate={handleUpdate} \n        search={search}\n        setSearch={(e) =&gt; setSearch(e.target.value)} \/\/ Anonymous function #4,002\n      \/&gt;\n      &lt;Footer version=&quot;1.0.0&quot; \/&gt;\n      {\/* Problem 5: The Hidden Memory Leak *\/}\n      {user &amp;&amp; &lt;LiveChat socketUrl={`wss:\/\/api.com\/chat\/${user.id}`} \/&gt;}\n    &lt;\/div&gt;\n  );\n};\n<\/code><\/pre>\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_80 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Table of Contents<\/p>\n<label for=\"ez-toc-cssicon-toggle-item-6a37d44c230f9\" class=\"ez-toc-cssicon-toggle-label\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/label><input type=\"checkbox\"  id=\"ez-toc-cssicon-toggle-item-6a37d44c230f9\"  aria-label=\"Toggle\" \/><nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/#Your_%E2%80%9CClean_Code%E2%80%9D_is_a_Performance_Tax\" >Your &#8220;Clean Code&#8221; is a Performance Tax<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/#Stop_Using_useEffect_for_Data_Fetching_Youre_Killing_the_Battery\" >Stop Using useEffect for Data Fetching, You&#8217;re Killing the Battery<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/#The_Memory_Leak_You_Call_a_%E2%80%9CFeature%E2%80%9D\" >The Memory Leak You Call a &#8220;Feature&#8221;<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/#Prop_Drilling_is_a_Sign_of_Intellectual_Laziness\" >Prop Drilling is a Sign of Intellectual Laziness<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/#Concurrent_Rendering_Isnt_Magic_Dust_for_Your_Garbage\" >Concurrent Rendering Isn&#8217;t Magic Dust for Your Garbage<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/#Your_State_Management_is_a_Rube_Goldberg_Machine\" >Your State Management is a Rube Goldberg Machine<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/#Terminal_Output_The_Reality_Check\" >Terminal Output: The Reality Check<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/#The_Manifesto_of_Minimalist_React\" >The Manifesto of Minimalist React<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/#The_%E2%80%9CBoring%E2%80%9D_Fixed_Version\" >The &#8220;Boring&#8221; Fixed Version<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/#Related_Articles\" >Related Articles<\/a><\/li><\/ul><\/nav><\/div>\n<h2><span class=\"ez-toc-section\" id=\"Your_%E2%80%9CClean_Code%E2%80%9D_is_a_Performance_Tax\"><\/span>Your &#8220;Clean Code&#8221; is a Performance Tax<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Let\u2019s talk about the &#8220;God Component&#8221; above. You think you\u2019re being &#8220;clean&#8221; by putting everything in one place. You think you\u2019re being &#8220;modular&#8221; by using hooks. In reality, you\u2019ve built a re-render hellscape that would make the React Fiber reconciler cry if it had tear ducts.<\/p>\n<p>Most developers wouldn&#8217;t know a react best practice if it hit them in the face, and this PR is proof. You\u2019ve ignored the fundamental way the reconciliation algorithm works. Every time <code>trackMetrics<\/code> changes\u2014which is every single time the parent component renders because you passed it an anonymous arrow function\u2014this <code>useEffect<\/code> fires. It fetches data. It sets state. It triggers a re-render. Which triggers the parent. Which recreates <code>trackMetrics<\/code>. <\/p>\n<p>Congratulations, you\u2019ve invented a perpetual motion machine that does nothing but heat up my MacBook and drain the user&#8217;s battery. In React 18.3.1, the Concurrent Renderer is trying its best to prioritize updates, but you\u2019re flooding the scheduler with &#8220;urgent&#8221; work that is actually just a recursive loop of your own incompetence. You aren&#8217;t &#8220;building a feature&#8221;; you&#8217;re DOSing your own API.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Stop_Using_useEffect_for_Data_Fetching_Youre_Killing_the_Battery\"><\/span>Stop Using useEffect for Data Fetching, You&#8217;re Killing the Battery<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>If I see one more <code>useEffect<\/code> with an <code>async<\/code> function inside it that doesn&#8217;t have a cleanup function, I\u2019m going to revoke your <code>git push<\/code> privileges. Look at that <code>fetchData<\/code> call. What happens if the <code>userId<\/code> changes while the first request is still in flight? You get a race condition. The first request finishes second, overwrites the state with stale data, and now the UI is showing User A\u2019s dashboard while the URL says User B. <\/p>\n<p>This isn&#8217;t just &#8220;buggy&#8221; code; it&#8217;s dangerous. You\u2019re leaking data between user sessions. And don&#8217;t get me started on the memory overhead. Every time this component re-renders, you&#8217;re creating a new <code>fetchData<\/code> function, a new promise, and a new set of closures. <\/p>\n<p>In a real &#8220;react best&#8221; scenario, you\u2019d be using a cache-aware library or at the very least <code>useSyncExternalStore<\/code> if you\u2019re dealing with a global state that exists outside the React tree. But no, you chose the &#8220;spaghetti&#8221; route. You chose to ignore the fact that <code>useEffect<\/code> is for <em>synchronization with external systems<\/em>, not for your basic CRUD logic. You\u2019re treating the most powerful tool in the library like a <code>window.onload<\/code> event from 2004.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"The_Memory_Leak_You_Call_a_%E2%80%9CFeature%E2%80%9D\"><\/span>The Memory Leak You Call a &#8220;Feature&#8221;<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Let\u2019s look at the <code>LiveChat<\/code> component at the bottom of that mess. You\u2019re passing a <code>socketUrl<\/code> that changes whenever <code>user.id<\/code> changes. But wait, look at the <code>setSearch<\/code> prop in <code>ContentArea<\/code>. <\/p>\n<p><code>setSearch={(e) =&gt; setSearch(e.target.value)}<\/code><\/p>\n<p>Every. Single. Render. You are allocating a new anonymous function. In a vacuum, one function doesn&#8217;t matter. But in a high-traffic library where this component might be nested inside a list of 500 items? You are choking the garbage collector. The V8 engine has to track these references, realize they\u2019re no longer used, and sweep them. While it\u2019s doing that, your UI janks. <\/p>\n<p>You\u2019ve created a &#8220;re-render hell&#8221; where typing a single character in the search bar causes the entire <code>UserDashboard<\/code>, the <code>Header<\/code>, the <code>Sidebar<\/code>, and the <code>ContentArea<\/code> to re-evaluate. Why? Because you didn&#8217;t memoize the props, and you didn&#8217;t split your state. Why is <code>search<\/code> (which changes every millisecond) in the same component as <code>user<\/code> (which changes once per hour)? <\/p>\n<p>This is technical debt interest being compounded every time a user interacts with the page. You\u2019re not just writing slow code; you\u2019re writing expensive code.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Prop_Drilling_is_a_Sign_of_Intellectual_Laziness\"><\/span>Prop Drilling is a Sign of Intellectual Laziness<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p><code>Sidebar config={config} setConfig={setConfig}<\/code>. <\/p>\n<p>Why? Why does the <code>UserDashboard<\/code> need to know how to update the sidebar&#8217;s config? This is &#8220;cruft&#8221; in its purest form. You\u2019ve turned your top-level component into a glorified switchboard operator. <\/p>\n<p>When you &#8220;prop drill&#8221; like this, you make it impossible to test components in isolation. You\u2019ve coupled the <code>Sidebar<\/code> to the <code>UserDashboard<\/code> forever. If I want to move the <code>Sidebar<\/code> to a different page, I have to bring the <code>config<\/code> state with it, or rewrite the whole thing. <\/p>\n<p>A &#8220;react best&#8221; approach would use component composition. Pass the <code>Sidebar<\/code> as a child, or use a specialized Context provider that doesn&#8217;t trigger a full-tree re-render every time a boolean flips. But that would require thinking about the component hierarchy for more than five seconds, wouldn&#8217;t it?<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Concurrent_Rendering_Isnt_Magic_Dust_for_Your_Garbage\"><\/span>Concurrent Rendering Isn&#8217;t Magic Dust for Your Garbage<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>I see you tried to use <code>useMemo<\/code> for <code>filteredPosts<\/code>. You think you\u2019re being &#8220;optimized.&#8221; You\u2019re not. You\u2019re memoizing a filter operation on an array that probably has ten items in it. The overhead of the <code>useMemo<\/code> hook\u2014the dependency array comparison, the memory allocation for the memoized value\u2014is likely more expensive than just running the <code>.filter()<\/code> call.<\/p>\n<p>And then there\u2019s <code>useDeferredValue<\/code>. I know you didn&#8217;t use it here, but I know you\u2019re tempted to throw it at the <code>search<\/code> state to &#8220;fix&#8221; the lag. Let me be clear: <code>useDeferredValue<\/code> is not a &#8220;make it fast&#8221; button for your bloated components. It\u2019s a tool for the Concurrent Renderer to keep the input field responsive while the heavy lifting happens in the background. If your &#8220;heavy lifting&#8221; is just a poorly written <code>map<\/code> function, <code>useDeferredValue<\/code> is just putting a band-aid on a gunshot wound.<\/p>\n<p>You need to understand the &#8220;work loop.&#8221; React 18 processes updates in &#8220;lanes.&#8221; When you trigger a state update, React tries to find a slice of time to do that work. If you provide a &#8220;spaghetti&#8221; of dependencies, you\u2019re forcing React to abandon its concurrent optimizations and fall back to synchronous, blocking renders. You are literally fighting the library you\u2019re trying to use.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Your_State_Management_is_a_Rube_Goldberg_Machine\"><\/span>Your State Management is a Rube Goldberg Machine<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p><code>const [config, setConfig] = useState({ mode: 'dark', notifications: true });<\/code><\/p>\n<p>Why is this an object? Every time you want to change the <code>mode<\/code>, you have to spread the previous state: <code>setConfig(prev =&gt; ({ ...prev, mode: 'light' }))<\/code>. If you forget the spread, you lose the <code>notifications<\/code> key. This is a classic source of bugs, and for what? To save one line of <code>useState<\/code>? <\/p>\n<p>This is what I call &#8220;bloat.&#8221; You\u2019re adding complexity where none is needed. Derived state is another one. I bet if I looked at the <code>Header<\/code> component, I\u2019d find another <code>useEffect<\/code> that calculates the user\u2019s &#8220;display name&#8221; based on the <code>user<\/code> prop. <\/p>\n<p><em>Newsflash:<\/em> If you can calculate it during render, calculate it during render. Don&#8217;t put it in state. Don&#8217;t put it in an effect. Just. Calculate. It. The CPU is good at math; it\u2019s bad at keeping track of your redundant, out-of-sync state variables.<\/p>\n<hr \/>\n<h3><span class=\"ez-toc-section\" id=\"Terminal_Output_The_Reality_Check\"><\/span>Terminal Output: The Reality Check<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Here is what happened when I tried to run your PR against our CI\/CD pipeline. It didn&#8217;t even make it past the build stage.<\/p>\n<pre class=\"codehilite\"><code class=\"language-bash\">$ npm run build --project dashboard-v2\n\n&gt; high-traffic-lib@18.3.1 build\n&gt; tsc &amp;&amp; vite build\n\nsrc\/components\/UserDashboard.jsx:22:11 - error TS2345: Argument of type '() =&gt; Promise&lt;void&gt;' is not assignable to parameter of type 'Destructor'.\n  22 |   useEffect(() =&gt; {\n     |             ^\n  23 |     const fetchData = async () =&gt; { ... }\n  24 |   }, [userId, trackMetrics]);\n\n[ERROR] Memory limit exceeded: V8 heap limit reached during minification.\nHeap usage: 4096MB \/ 4096MB\n\n[STDOUT] FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory\n 1: 0x1012e3450 node::Abort() (.?AVAbort@node@@)\n 2: 0x1012e4560 node::OnFatalError(char const*, char const*) (.?AVOnFatalError@node@@)\n 3: 0x101456780 v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool)\n 4: 0x101456a10 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool)\n 5: 0x101600000 v8::internal::Heap::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment)\n\n[STACK TRACE]\nreact-dom.development.js:24560 (reconcileChildren)\nreact-dom.development.js:18230 (beginWork)\nreact-dom.development.js:22340 (performUnitOfWork)\nreact-dom.development.js:22310 (workLoopSync)\n-- Memory Leak Detected in &lt;LiveChat \/&gt; --\nLeaked Objects: 4,502 (EventEmitter listeners not removed)\n<\/code><\/pre>\n<p>Look at that. 4,502 leaked listeners. You didn&#8217;t clean up your WebSocket connection in the <code>LiveChat<\/code> component. Every time the <code>userId<\/code> changed, you opened a <em>new<\/em> connection without closing the old one. You\u2019re not just crashing the browser; you\u2019re probably crashing our staging socket server too. <\/p>\n<hr \/>\n<h2><span class=\"ez-toc-section\" id=\"The_Manifesto_of_Minimalist_React\"><\/span>The Manifesto of Minimalist React<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>I am tired of being the &#8220;grumpy&#8221; one, but if this is the quality of code being produced by &#8220;modern&#8221; developers, then someone has to hold the line. Here are my rules. If your PR violates these, I will close it without comment.<\/p>\n<ol>\n<li><strong>No Anonymous Functions in Props.<\/strong> If I see <code>onClick={() =&gt; ...}<\/code> in a component that renders more than once, I\u2019m rejecting it. Use <code>useCallback<\/code> or move the function outside the component.<\/li>\n<li><strong>useEffect is a Last Resort.<\/strong> If you can use <code>useMemo<\/code>, use it. If you can calculate it during render, do it. If you\u2019re fetching data, use a real data-fetching library that handles caching and invalidation.<\/li>\n<li><strong>Primitive State Only.<\/strong> Stop wrapping everything in objects. <code>useState(true)<\/code> is better than <code>useState({ active: true })<\/code>. It\u2019s easier to track, easier to update, and less prone to &#8220;stale closure&#8221; bugs.<\/li>\n<li><strong>Cleanup is Mandatory.<\/strong> If you open a socket, close it. If you set a timeout, clear it. If you add an event listener, remove it. This isn&#8217;t &#8220;react best&#8221; practice; it&#8217;s basic programming.<\/li>\n<li><strong>Boring is Better.<\/strong> I don&#8217;t want to see your custom hook that abstracts away the <code>fetch<\/code> API with 15 layers of generics. I want to see code that a junior can read and a senior can&#8217;t break.<\/li>\n<li><strong>Respect the Fiber.<\/strong> Understand that React 18 is a scheduler. Don&#8217;t block the main thread with heavy computations. If you have to do something slow, use <code>useTransition<\/code> or <code>useDeferredValue<\/code> correctly, or better yet, move it to a Web Worker.<\/li>\n<\/ol>\n<hr \/>\n<h3><span class=\"ez-toc-section\" id=\"The_%E2%80%9CBoring%E2%80%9D_Fixed_Version\"><\/span>The &#8220;Boring&#8221; Fixed Version<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Here is how you write that component without being a menace to society. It\u2019s boring. It\u2019s standard. It actually works.<\/p>\n<pre class=\"codehilite\"><code class=\"language-jsx\">import React, { useState, useEffect, useMemo, useCallback } from 'react';\nimport { useUserData } from '.\/hooks\/useUserData'; \/\/ Abstracted fetch with cleanup\nimport { usePosts } from '.\/hooks\/usePosts';\n\n\/\/ Move static config outside to prevent re-allocation\nconst DEFAULT_CONFIG = { mode: 'dark', notifications: true };\n\nexport const UserDashboard = ({ userId, theme }) =&gt; {\n  \/\/ Split state: search changes often, config doesn't\n  const [search, setSearch] = useState('');\n  const [config, setConfig] = useState(DEFAULT_CONFIG);\n\n  \/\/ Custom hook handles the race conditions and cleanup\n  const { user, isLoading } = useUserData(userId);\n  const { posts } = usePosts(userId);\n\n  \/\/ Memoize only when the calculation is actually expensive\n  const filteredPosts = useMemo(() =&gt; {\n    const query = search.toLowerCase();\n    if (!query) return posts;\n    return posts.filter(p =&gt; p.title.toLowerCase().includes(query));\n  }, [posts, search]);\n\n  \/\/ Stable reference for event handlers\n  const handleSearchChange = useCallback((e) =&gt; {\n    setSearch(e.target.value);\n  }, []);\n\n  const handleUpdate = useCallback(() =&gt; {\n    if (!user) return;\n    console.log(`Updating user ${user.name}`);\n  }, [user]);\n\n  if (isLoading) return &lt;Spinner \/&gt;;\n\n  return (\n    &lt;div className={`dashboard-${theme}`}&gt;\n      &lt;Header user={user} \/&gt;\n      &lt;Sidebar config={config} onConfigChange={setConfig} \/&gt;\n      &lt;ContentArea \n        posts={filteredPosts} \n        onUpdate={handleUpdate} \n        search={search}\n        onSearchChange={handleSearchChange}\n      \/&gt;\n      &lt;Footer \/&gt;\n      {user &amp;&amp; &lt;LiveChat userId={user.id} \/&gt;}\n    &lt;\/div&gt;\n  );\n};\n\n\/\/ LiveChat must have a cleanup!\nconst LiveChat = ({ userId }) =&gt; {\n  useEffect(() =&gt; {\n    const socket = new WebSocket(`wss:\/\/api.com\/chat\/${userId}`);\n    return () =&gt; socket.close(); \/\/ CLEAN YOUR MESS\n  }, [userId]);\n\n  return &lt;div className=&quot;chat-window&quot; \/&gt;;\n};\n<\/code><\/pre>\n<p>Notice the difference? No infinite loops. No memory leaks. No prop-drilling of setters. The state is split so that typing in the search bar doesn&#8217;t re-trigger the user data logic. The WebSocket actually closes when the component unmounts. <\/p>\n<p>It\u2019s not &#8220;clever.&#8221; It\u2019s not &#8220;vibrant.&#8221; It\u2019s just correct. <\/p>\n<p>Now, take your original PR, delete the branch, and go read the React documentation on &#8220;Synchronizing with Effects&#8221; until your eyes bleed. Don&#8217;t come back until you&#8217;ve learned that &#8220;Clean Code&#8221; isn&#8217;t about how many hooks you can cram into one file\u2014it&#8217;s about how little code you can write while still solving the problem.<\/p>\n<p>I\u2019m going to get more espresso. Don&#8217;t touch anything.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Related_Articles\"><\/span>Related Articles<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Explore more insights and best practices:<\/p>\n<ul>\n<li><a href=\"https:\/\/itsupportwale.com\/blog\/kali-linux-virtualbox-installation\/\">Kali Linux Virtualbox Installation<\/a><\/li>\n<li><a href=\"https:\/\/itsupportwale.com\/blog\/disable-gnome-animation-on-ubuntu\/\">Disable Gnome Animation On Ubuntu<\/a><\/li>\n<li><a href=\"https:\/\/itsupportwale.com\/blog\/10-essential-aws-best-practices-for-cloud-optimization-2\/\">10 Essential Aws Best Practices For Cloud Optimization 2<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Sigh. I\u2019ve spent the last six hours staring at PR #8821, and I\u2019m convinced that the &#8220;modern frontend developer&#8221; is just three script kiddies in a trench coat fueled by TikTok tutorials and a complete lack of respect for the CPU. We are running React 18.3.1 on Node 20.x, and yet, somehow, the code being &#8230; <a title=\"React Best Practices: Build Scalable Apps Like a Pro\" class=\"read-more\" href=\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/\" aria-label=\"Read more  on React Best Practices: Build Scalable Apps Like a Pro\">Read more<\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-4819","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.0 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>React Best Practices: Build Scalable Apps Like a Pro - ITSupportWale<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"React Best Practices: Build Scalable Apps Like a Pro - ITSupportWale\" \/>\n<meta property=\"og:description\" content=\"Sigh. I\u2019ve spent the last six hours staring at PR #8821, and I\u2019m convinced that the &#8220;modern frontend developer&#8221; is just three script kiddies in a trench coat fueled by TikTok tutorials and a complete lack of respect for the CPU. We are running React 18.3.1 on Node 20.x, and yet, somehow, the code being ... Read more\" \/>\n<meta property=\"og:url\" content=\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/\" \/>\n<meta property=\"og:site_name\" content=\"ITSupportWale\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/Itsupportwale-298547177495978\" \/>\n<meta property=\"article:published_time\" content=\"2026-06-20T16:25:54+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2021\/05\/android-chrome-512x512-1.png\" \/>\n\t<meta property=\"og:image:width\" content=\"512\" \/>\n\t<meta property=\"og:image:height\" content=\"512\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Techie\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Techie\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"12 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/\"},\"author\":{\"name\":\"Techie\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d\"},\"headline\":\"React Best Practices: Build Scalable Apps Like a Pro\",\"datePublished\":\"2026-06-20T16:25:54+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/\"},\"wordCount\":1708,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/#organization\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/\",\"url\":\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/\",\"name\":\"React Best Practices: Build Scalable Apps Like a Pro - ITSupportWale\",\"isPartOf\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/#website\"},\"datePublished\":\"2026-06-20T16:25:54+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/itsupportwale.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"React Best Practices: Build Scalable Apps Like a Pro\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/#website\",\"url\":\"https:\/\/itsupportwale.com\/blog\/\",\"name\":\"ITSupportWale\",\"description\":\"Tips, Tricks, Fixed-Errors, Tutorials &amp; Guides\",\"publisher\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/itsupportwale.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/#organization\",\"name\":\"itsupportwale\",\"url\":\"https:\/\/itsupportwale.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2023\/09\/cropped-Logo-trans-without-slogan.png\",\"contentUrl\":\"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2023\/09\/cropped-Logo-trans-without-slogan.png\",\"width\":1119,\"height\":144,\"caption\":\"itsupportwale\"},\"image\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/Itsupportwale-298547177495978\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d\",\"name\":\"Techie\",\"sameAs\":[\"https:\/\/itsupportwale.com\",\"iswblogadmin\"],\"url\":\"https:\/\/itsupportwale.com\/blog\/author\/iswblogadmin\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"React Best Practices: Build Scalable Apps Like a Pro - ITSupportWale","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/","og_locale":"en_US","og_type":"article","og_title":"React Best Practices: Build Scalable Apps Like a Pro - ITSupportWale","og_description":"Sigh. I\u2019ve spent the last six hours staring at PR #8821, and I\u2019m convinced that the &#8220;modern frontend developer&#8221; is just three script kiddies in a trench coat fueled by TikTok tutorials and a complete lack of respect for the CPU. We are running React 18.3.1 on Node 20.x, and yet, somehow, the code being ... Read more","og_url":"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/","og_site_name":"ITSupportWale","article_publisher":"https:\/\/www.facebook.com\/Itsupportwale-298547177495978","article_published_time":"2026-06-20T16:25:54+00:00","og_image":[{"width":512,"height":512,"url":"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2021\/05\/android-chrome-512x512-1.png","type":"image\/png"}],"author":"Techie","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Techie","Est. reading time":"12 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/#article","isPartOf":{"@id":"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/"},"author":{"name":"Techie","@id":"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d"},"headline":"React Best Practices: Build Scalable Apps Like a Pro","datePublished":"2026-06-20T16:25:54+00:00","mainEntityOfPage":{"@id":"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/"},"wordCount":1708,"commentCount":0,"publisher":{"@id":"https:\/\/itsupportwale.com\/blog\/#organization"},"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/","url":"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/","name":"React Best Practices: Build Scalable Apps Like a Pro - ITSupportWale","isPartOf":{"@id":"https:\/\/itsupportwale.com\/blog\/#website"},"datePublished":"2026-06-20T16:25:54+00:00","breadcrumb":{"@id":"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-like-a-pro\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/itsupportwale.com\/blog\/"},{"@type":"ListItem","position":2,"name":"React Best Practices: Build Scalable Apps Like a Pro"}]},{"@type":"WebSite","@id":"https:\/\/itsupportwale.com\/blog\/#website","url":"https:\/\/itsupportwale.com\/blog\/","name":"ITSupportWale","description":"Tips, Tricks, Fixed-Errors, Tutorials &amp; Guides","publisher":{"@id":"https:\/\/itsupportwale.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/itsupportwale.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/itsupportwale.com\/blog\/#organization","name":"itsupportwale","url":"https:\/\/itsupportwale.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/itsupportwale.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2023\/09\/cropped-Logo-trans-without-slogan.png","contentUrl":"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2023\/09\/cropped-Logo-trans-without-slogan.png","width":1119,"height":144,"caption":"itsupportwale"},"image":{"@id":"https:\/\/itsupportwale.com\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/Itsupportwale-298547177495978"]},{"@type":"Person","@id":"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d","name":"Techie","sameAs":["https:\/\/itsupportwale.com","iswblogadmin"],"url":"https:\/\/itsupportwale.com\/blog\/author\/iswblogadmin\/"}]}},"_links":{"self":[{"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/posts\/4819","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/comments?post=4819"}],"version-history":[{"count":0,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/posts\/4819\/revisions"}],"wp:attachment":[{"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/media?parent=4819"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/categories?post=4819"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/tags?post=4819"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}