{"id":4813,"date":"2026-06-11T23:54:29","date_gmt":"2026-06-11T18:24:29","guid":{"rendered":"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-faster\/"},"modified":"2026-06-11T23:54:29","modified_gmt":"2026-06-11T18:24:29","slug":"react-best-practices-build-scalable-apps-faster","status":"publish","type":"post","link":"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-faster\/","title":{"rendered":"React Best Practices: Build Scalable Apps Faster"},"content":{"rendered":"<p>text<br \/>\n[2024-05-22T03:14:02.891Z] SEVERE: [MainThread] Uncaught RangeError: Maximum update depth exceeded.<br \/>\n    at Object.updateContainer (react-dom.development.js:25331:7)<br \/>\n    at Object.scheduleUpdateOnFiber (react-dom.development.js:21835:3)<br \/>\n    at dispatchSetState (react-dom.development.js:15853:7)<br \/>\n    at onAuthChange (AuthContext.tsx:42:5)<br \/>\n[2024-05-22T03:14:03.112Z] FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed &#8211; JavaScript heap out of memory<br \/>\n[2024-05-22T03:14:03.445Z] Process exited with code 137 (OOM)<\/p>\n<pre class=\"codehilite\"><code>My eyes are vibrating. Not from the four double-shots of espresso I\u2019ve downed since Tuesday, but from the blue light of a 32-inch monitor that has been screaming &quot;OOM&quot; at me for three days. The office smells like ozone and despair. Somewhere in the distance, a janitor is buffing the floors, oblivious to the fact that our entire checkout pipeline just melted into a puddle of unhandled promises and infinite re-renders.\n\nThis wasn't a &quot;bug.&quot; It was a massacre. A $50 million refactor, led by a &quot;clever&quot; junior developer who thought he knew **react best** practices because he watched a 15-minute YouTube video on &quot;Clean Architecture.&quot; He decided to &quot;decouple&quot; our state. He decided to &quot;abstract&quot; our hooks. He decided to turn our codebase into a recursive nightmare that even the V8 engine couldn't optimize its way out of.\n\nI\u2019ve spent the last 72 hours gutting his &quot;elegant&quot; abstractions and replacing them with code that actually works. If you\u2019re looking for a &quot;vibrant&quot; discussion on the &quot;tapestry&quot; of modern UI, leave. This is a post-mortem. This is how you actually write React when your company\u2019s survival depends on it.\n\n## The 3 AM Incident Report: The Infinite Re-render Loop\n\nThe PagerDuty alert hit at 3:14 AM. By 3:20 AM, our SREs were reporting that the frontend was consuming 4GB of RAM per tab before crashing. The culprit? A &quot;clever&quot; implementation of a global `AuthContext` that triggered a state update every time a user moved their mouse, because &quot;we might need to track engagement metrics in real-time.&quot;\n\nThe junior\u2014let's call him Kevin\u2014had implemented a &quot;reactive&quot; observer pattern inside a `useEffect` that lacked a proper dependency array. He thought he was being &quot;efficient&quot; by not &quot;hardcoding&quot; dependencies.\n\n**The Failure:**\n```typescript\n\/\/ Kevin's &quot;Clever&quot; Auth Provider\nexport const AuthProvider = ({ children }) =&gt; {\n  const [user, setUser] = useState(null);\n  const [metrics, setMetrics] = useState({});\n\n  \/\/ This &quot;react best&quot; practice according to Kevin\n  useEffect(() =&gt; {\n    const unsubscribe = api.onAuthStateChanged((u) =&gt; {\n      setUser(u);\n      \/\/ Logic that triggers a re-render on EVERY auth change\n      \/\/ which in turn re-runs this effect because the \n      \/\/ anonymous function is recreated every render.\n    });\n    return unsubscribe;\n  }); \/\/ Look Ma, no dependency array!\n\n  return (\n    &lt;AuthContext.Provider value={{ user, metrics, setMetrics }}&gt;\n      {children}\n    &lt;\/AuthContext.Provider&gt;\n  );\n};\n<\/code><\/pre>\n<p>Because the <code>useEffect<\/code> lacked a dependency array, it ran on every render. Because it called <code>setUser<\/code>, it triggered a re-render. Because it triggered a re-render, the <code>useEffect<\/code> ran again. <strong>Maximum update depth exceeded.<\/strong> The browser didn&#8217;t just hang; it choked.<\/p>\n<p><strong>The &#8220;react best&#8221; Fix:<\/strong><br \/>\nWe had to strip the &#8220;engagement tracking&#8221; out of the Auth provider entirely. State that changes frequently (like mouse position or metrics) should <strong>never<\/strong> live in the same context as state that is globally critical (like authentication).<\/p>\n<pre class=\"codehilite\"><code class=\"language-typescript\">\/\/ The Hardened Fix (React 18.3.1)\nimport { memo, useMemo, useCallback, useEffect, useState } from 'react';\n\nexport const AuthProvider = memo(({ children }: { children: React.ReactNode }) =&gt; {\n  const [user, setUser] = useState&lt;User | null&gt;(null);\n  const [status, setStatus] = useState&lt;'loading' | 'auth' | 'unauth'&gt;('loading');\n\n  const handleAuthChange = useCallback((u: User | null) =&gt; {\n    setUser(u);\n    setStatus(u ? 'auth' : 'unauth');\n  }, []);\n\n  useEffect(() =&gt; {\n    let isMounted = true;\n    const unsubscribe = api.onAuthStateChanged((u) =&gt; {\n      if (isMounted) {\n        handleAuthChange(u);\n      }\n    });\n\n    return () =&gt; {\n      isMounted = false;\n      unsubscribe();\n    };\n  }, [handleAuthChange]);\n\n  const value = useMemo(() =&gt; ({ user, status }), [user, status]);\n\n  return (\n    &lt;AuthContext.Provider value={value}&gt;\n      {children}\n    &lt;\/AuthContext.Provider&gt;\n  );\n});\n<\/code><\/pre>\n<p>We added <code>isMounted<\/code> flags to prevent state updates on unmounted components\u2014a classic memory leak source. We used <code>useMemo<\/code> for the context value to prevent every consumer from re-rendering unless the <code>user<\/code> or <code>status<\/code> actually changed. This is survival, not &#8220;clean code.&#8221;<\/p>\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-6a2b201f3be53\" 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-6a2b201f3be53\"  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-faster\/#The_%E2%80%9CProp_Drilling%E2%80%9D_Graveyard_Why_Our_State_Management_Killed_the_LCP\" >The &#8220;Prop Drilling&#8221; Graveyard: Why Our State Management Killed the LCP<\/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-faster\/#useEffect_is_Not_a_Lifecycle_Hook_Tearing_Down_the_Misuse_of_Synchronization\" >useEffect is Not a Lifecycle Hook: Tearing Down the Misuse of Synchronization<\/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-faster\/#The_Memoization_Myth_When_useMemo_Actually_Made_Our_App_Slower\" >The Memoization Myth: When useMemo Actually Made Our App Slower<\/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-faster\/#Fetching_Follies_and_the_%E2%80%9Creact_best%E2%80%9D_Way_to_Handle_Server_State\" >Fetching Follies and the &#8220;react best&#8221; Way to Handle Server State<\/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-faster\/#The_Final_Deployment_Script_The_Hardened_Configuration\" >The Final Deployment Script: The Hardened Configuration<\/a><\/li><\/ul><\/nav><\/div>\n<h2><span class=\"ez-toc-section\" id=\"The_%E2%80%9CProp_Drilling%E2%80%9D_Graveyard_Why_Our_State_Management_Killed_the_LCP\"><\/span>The &#8220;Prop Drilling&#8221; Graveyard: Why Our State Management Killed the LCP<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Kevin hated Redux. He said it was &#8220;boilerplate-heavy.&#8221; So he decided to use the Context API for <em>everything<\/em>. By the time I looked at the component tree, we had 24 nested Providers. Every time a user typed a single character in a search bar, the entire application tree\u2014from the navigation bar to the footer\u2014was being diffed by React.<\/p>\n<p>Our Largest Contentful Paint (LCP) went from 1.2s to 6.5s. On a mobile device over 4G, the app was unusable. The &#8220;react best&#8221; approach isn&#8217;t to use Context for everything; it&#8217;s to use the right tool for the frequency of the update.<\/p>\n<p><strong>The Failure:<\/strong><br \/>\nKevin had a <code>GlobalStateContext<\/code> that held:<br \/>\n1. User profile data (Static-ish)<br \/>\n2. Shopping cart items (Frequent)<br \/>\n3. Search input text (High frequency)<br \/>\n4. Theme settings (Static)<br \/>\n5. Real-time websocket notifications (Extreme frequency)<\/p>\n<p>Every time a notification came in via the websocket, the <code>SearchInput<\/code> component would re-render. Why? Because it was consuming the <code>GlobalStateContext<\/code>, and React Context does not have a built-in way to &#8220;select&#8221; only a portion of the state. If the value object changes, every consumer re-renders. Period.<\/p>\n<p><strong>The &#8220;react best&#8221; Fix:<\/strong><br \/>\nWe moved high-frequency state to <strong>Zustand<\/strong>. It\u2019s small, it\u2019s fast, and it allows for selector-based subscriptions that don&#8217;t trigger the &#8220;Context Hell&#8221; re-render loop.<\/p>\n<pre class=\"codehilite\"><code class=\"language-typescript\">\/\/ zustand\/store.ts\nimport { create } from 'zustand';\n\ninterface AppState {\n  searchQuery: string;\n  setSearchQuery: (query: string) =&gt; void;\n  notifications: Notification[];\n  addNotification: (n: Notification) =&gt; void;\n}\n\nexport const useStore = create&lt;AppState&gt;((set) =&gt; ({\n  searchQuery: '',\n  notifications: [],\n  setSearchQuery: (query) =&gt; set({ searchQuery: query }),\n  addNotification: (n) =&gt; set((state) =&gt; ({ \n    notifications: [...state.notifications, n] \n  })),\n}));\n\n\/\/ In the component:\nconst SearchInput = () =&gt; {\n  \/\/ ONLY re-renders when searchQuery changes.\n  const searchQuery = useStore((state) =&gt; state.searchQuery);\n  const setSearchQuery = useStore((state) =&gt; state.setSearchQuery);\n\n  return (\n    &lt;input \n      value={searchQuery} \n      onChange={(e) =&gt; setSearchQuery(e.target.value)} \n      placeholder=&quot;Search...&quot;\n    \/&gt;\n  );\n};\n<\/code><\/pre>\n<p><strong>Stop using Context for high-frequency updates.<\/strong> It\u2019s not what it was designed for. If you\u2019re building a $50M refactor, don&#8217;t use a hammer to perform heart surgery.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"useEffect_is_Not_a_Lifecycle_Hook_Tearing_Down_the_Misuse_of_Synchronization\"><\/span>useEffect is Not a Lifecycle Hook: Tearing Down the Misuse of Synchronization<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>If I see one more <code>useEffect<\/code> used to derive state, I\u2019m going to throw my MacBook into the Hudson River. Kevin had this habit of &#8220;syncing&#8221; state variables. He\u2019d have <code>firstName<\/code> and <code>lastName<\/code> in state, and then a <code>useEffect<\/code> to update <code>fullName<\/code>.<\/p>\n<p><strong>The Failure:<\/strong><\/p>\n<pre class=\"codehilite\"><code class=\"language-typescript\">const [firstName, setFirstName] = useState('');\nconst [lastName, setLastName] = useState('');\nconst [fullName, setFullName] = useState('');\n\nuseEffect(() =&gt; {\n  setFullName(`${firstName} ${lastName}`);\n}, [firstName, lastName]);\n<\/code><\/pre>\n<p>This is a disaster. It triggers an <strong>extra render cycle<\/strong>.<br \/>\n1. User types in <code>firstName<\/code>.<br \/>\n2. Component renders with new <code>firstName<\/code>.<br \/>\n3. <code>useEffect<\/code> runs.<br \/>\n4. <code>setFullName<\/code> is called.<br \/>\n5. Component renders <strong>again<\/strong> with new <code>fullName<\/code>.<\/p>\n<p>In a complex form with 30 fields, this &#8220;pattern&#8221; creates a laggy, stuttering UI that makes the user feel like they&#8217;re typing through molasses.<\/p>\n<p><strong>The &#8220;react best&#8221; Fix:<\/strong><br \/>\nCalculate values during the render phase. If the calculation is expensive, use <code>useMemo<\/code>. If it\u2019s just string concatenation, just do it.<\/p>\n<pre class=\"codehilite\"><code class=\"language-typescript\">const [firstName, setFirstName] = useState('');\nconst [lastName, setLastName] = useState('');\n\n\/\/ No extra render. No useEffect. No bullshit.\nconst fullName = `${firstName} ${lastName}`; \n\n\/\/ If it were actually expensive:\nconst expensiveValue = useMemo(() =&gt; {\n  return performHeavyCalculation(firstName, lastName);\n}, [firstName, lastName]);\n<\/code><\/pre>\n<p><code>useEffect<\/code> is for <strong>synchronizing with external systems<\/strong> (APIs, WebSockets, manual DOM manipulation). It is not a place to hide your business logic. We spent 12 hours just removing redundant <code>useEffect<\/code> calls that were causing &#8220;flickering&#8221; in the UI where old data would show for one frame before the effect kicked in.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"The_Memoization_Myth_When_useMemo_Actually_Made_Our_App_Slower\"><\/span>The Memoization Myth: When useMemo Actually Made Our App Slower<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Kevin went on a &#8220;performance optimization&#8221; spree. He wrapped every single component in <code>memo()<\/code> and every function in <code>useCallback()<\/code>. He thought he was being a &#8220;react best&#8221; practitioner. He was actually just adding overhead to the garbage collector.<\/p>\n<p>Memoization isn&#8217;t free. You\u2019re trading memory and CPU cycles (to compare dependency arrays) for the avoidance of a render. If the render is cheap (like a button or a small text component), the comparison is often <strong>more expensive<\/strong> than the render itself.<\/p>\n<p><strong>The Failure:<\/strong><\/p>\n<pre class=\"codehilite\"><code class=\"language-typescript\">const MyButton = memo(({ onClick, label }) =&gt; {\n  console.log(&quot;Button render&quot;);\n  return &lt;button onClick={onClick}&gt;{label}&lt;\/button&gt;;\n});\n\nconst Parent = () =&gt; {\n  const handleClick = useCallback(() =&gt; {\n    console.log(&quot;Clicked&quot;);\n  }, []); \/\/ Memoizing a function that does nothing\n\n  return &lt;MyButton onClick={handleClick} label=&quot;Click Me&quot; \/&gt;;\n};\n<\/code><\/pre>\n<p>In this case, <code>MyButton<\/code> is so simple that the React Fiber reconciler would have handled it in microseconds. By adding <code>memo<\/code> and <code>useCallback<\/code>, we forced React to:<br \/>\n1. Store the function in memory across renders.<br \/>\n2. Run a shallow comparison on the props object every single time <code>Parent<\/code> renders.<br \/>\n3. Check the dependency array of <code>useCallback<\/code>.<\/p>\n<p>When Kevin did this to 400 components in our dashboard, the &#8220;dependency check&#8221; overhead became a measurable bottleneck in our flame graphs.<\/p>\n<p><strong>The &#8220;react best&#8221; Fix:<\/strong><br \/>\nWe removed <code>memo<\/code> from 90% of the components. We only kept it for:<br \/>\n1. Components that render massive lists (100+ items).<br \/>\n2. Components that perform heavy SVG or Canvas rendering.<br \/>\n3. Components that are forced to re-render by a parent they have no relation to.<\/p>\n<p><strong>Rule of thumb:<\/strong> Don&#8217;t optimize until you\u2019ve measured. Use the React DevTools Profiler. If a component renders in under 1ms, leave it the hell alone.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Fetching_Follies_and_the_%E2%80%9Creact_best%E2%80%9D_Way_to_Handle_Server_State\"><\/span>Fetching Follies and the &#8220;react best&#8221; Way to Handle Server State<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>The &#8220;clever&#8221; refactor included a custom-built data fetching hook. Kevin didn&#8217;t want to &#8220;bloat&#8221; our bundle with TanStack Query (React Query). So he wrote his own. It didn&#8217;t handle cache invalidation. It didn&#8217;t handle race conditions. It didn&#8217;t handle request cancellation.<\/p>\n<p><strong>The Failure:<\/strong><\/p>\n<pre class=\"codehilite\"><code class=\"language-typescript\">\/\/ Kevin's &quot;Lightweight&quot; Fetcher\nconst useData = (url) =&gt; {\n  const [data, setData] = useState(null);\n  useEffect(() =&gt; {\n    fetch(url).then(res =&gt; res.json()).then(setData);\n  }, [url]);\n  return data;\n};\n<\/code><\/pre>\n<p>Here\u2019s what happened: A user clicked &#8220;Profile,&#8221; then immediately clicked &#8220;Settings.&#8221; Two fetch requests were fired. The &#8220;Settings&#8221; request finished first. Then the &#8220;Profile&#8221; request finished and called <code>setData<\/code>. The user was looking at the Settings page but seeing Profile data. <strong>Race condition.<\/strong><\/p>\n<p><strong>The &#8220;react best&#8221; Fix:<\/strong><br \/>\nWe deleted 2,000 lines of Kevin\u2019s &#8220;custom hooks&#8221; and installed <code>@tanstack\/react-query<\/code>. It\u2019s not &#8220;bloat&#8221;; it\u2019s the infrastructure required to run a professional application.<\/p>\n<pre class=\"codehilite\"><code class=\"language-typescript\">import { useQuery } from '@tanstack\/react-query';\n\nconst fetchUser = async (id: string) =&gt; {\n  const response = await fetch(`\/api\/users\/${id}`);\n  if (!response.ok) throw new Error('Network response was not ok');\n  return response.json();\n};\n\nexport const UserProfile = ({ userId }: { userId: string }) =&gt; {\n  const { data, error, isLoading } = useQuery({\n    queryKey: ['user', userId],\n    queryFn: () =&gt; fetchUser(userId),\n    staleTime: 1000 * 60 * 5, \/\/ 5 minutes\n    retry: 3,\n  });\n\n  if (isLoading) return &lt;LoadingSpinner \/&gt;;\n  if (error) return &lt;ErrorMessage error={error} \/&gt;;\n\n  return &lt;div&gt;{data.name}&lt;\/div&gt;;\n};\n<\/code><\/pre>\n<p>React Query handles the race conditions. It handles the loading states. It handles the caching. It handles the &#8220;I closed the tab so stop the request&#8221; logic via <code>AbortController<\/code>. This is how you prevent $50M outages. You don&#8217;t reinvent the wheel when the wheel is already optimized for Formula 1.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"The_Final_Deployment_Script_The_Hardened_Configuration\"><\/span>The Final Deployment Script: The Hardened Configuration<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>After 72 hours, we finally pushed the fix. We didn&#8217;t just fix the code; we fixed the environment. We locked down the versions. No more <code>^<\/code> or <code>~<\/code> in the <code>package.json<\/code> that allows minor versions to break our build in CI.<\/p>\n<p><strong>The Hardened <code>package.json<\/code>:<\/strong><\/p>\n<pre class=\"codehilite\"><code class=\"language-json\">{\n  &quot;name&quot;: &quot;enterprise-checkout-system&quot;,\n  &quot;version&quot;: &quot;2.4.1&quot;,\n  &quot;private&quot;: true,\n  &quot;dependencies&quot;: {\n    &quot;react&quot;: &quot;18.3.1&quot;,\n    &quot;react-dom&quot;: &quot;18.3.1&quot;,\n    &quot;@tanstack\/react-query&quot;: &quot;5.28.0&quot;,\n    &quot;zustand&quot;: &quot;4.5.2&quot;,\n    &quot;zod&quot;: &quot;3.22.4&quot;,\n    &quot;lucide-react&quot;: &quot;0.363.0&quot;,\n    &quot;clsx&quot;: &quot;2.1.0&quot;,\n    &quot;tailwind-merge&quot;: &quot;2.2.2&quot;\n  },\n  &quot;devDependencies&quot;: {\n    &quot;@types\/react&quot;: &quot;18.2.66&quot;,\n    &quot;@types\/react-dom&quot;: &quot;18.2.22&quot;,\n    &quot;@vitejs\/plugin-react&quot;: &quot;4.2.1&quot;,\n    &quot;typescript&quot;: &quot;5.4.2&quot;,\n    &quot;vite&quot;: &quot;5.4.0&quot;,\n    &quot;eslint&quot;: &quot;8.57.0&quot;\n  },\n  &quot;engines&quot;: {\n    &quot;node&quot;: &quot;&gt;=20.11.0&quot;\n  }\n}\n<\/code><\/pre>\n<p>We also implemented a strict <code>vite.config.ts<\/code> that forces the browser to clear old assets and ensures that our chunks are optimized for HTTP\/2.<\/p>\n<pre class=\"codehilite\"><code class=\"language-typescript\">\/\/ vite.config.ts\nimport { defineConfig } from 'vite';\nimport react from '@vitejs\/plugin-react';\n\nexport default defineConfig({\n  plugins: [react()],\n  build: {\n    target: 'esnext',\n    minify: 'terser',\n    cssCodeSplit: true,\n    rollupOptions: {\n      output: {\n        manualChunks: {\n          vendor: ['react', 'react-dom', 'zustand'],\n          utils: ['zod', 'clsx', 'tailwind-merge'],\n        },\n      },\n    },\n  },\n  server: {\n    headers: {\n      'Cache-Control': 'no-store',\n    },\n  },\n});\n<\/code><\/pre>\n<p><strong>The Aftermath:<\/strong><br \/>\nThe CPU usage on our production pods dropped by 70%. The memory leak is gone. The &#8220;Maximum update depth exceeded&#8221; error is a ghost of the past. Kevin is currently &#8220;re-evaluating his career goals&#8221; in a meeting with HR, and I am going to sleep for the next 48 hours.<\/p>\n<p>But let\u2019s be real. In six months, some new framework will come out. Some new &#8220;influencer&#8221; will post a thread about how &#8220;React is dead&#8221; and we should all move to a signal-based, server-component-only, zero-JS-on-the-client monstrosity. And some other junior will read it, get &#8220;clever,&#8221; and I\u2019ll be right back here, drinking stale coffee at 3 AM, fixing another $50M mistake.<\/p>\n<p><strong>React best<\/strong> practices aren&#8217;t about following the latest trend. They are about understanding the underlying engine. They are about knowing that every line of code you write has a cost\u2014in memory, in CPU, and in human sanity.<\/p>\n<p>If you want to write &#8220;clean code,&#8221; go write a <a href=\"https:\/\/itsupportwale.com\/blog\/\" title=\"Read more about blog\">blog<\/a> post. If you want to write production code, keep it simple, keep it measurable, and for the love of God, put a dependency array on your effects.<\/p>\n<p>Sign-off,<br \/>\nSenior Lead Engineer (Current Caffeine Level: Lethal)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>text [2024-05-22T03:14:02.891Z] SEVERE: [MainThread] Uncaught RangeError: Maximum update depth exceeded. at Object.updateContainer (react-dom.development.js:25331:7) at Object.scheduleUpdateOnFiber (react-dom.development.js:21835:3) at dispatchSetState (react-dom.development.js:15853:7) at onAuthChange (AuthContext.tsx:42:5) [2024-05-22T03:14:03.112Z] FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed &#8211; JavaScript heap out of memory [2024-05-22T03:14:03.445Z] Process exited with code 137 (OOM) My eyes are vibrating. Not from the four double-shots of &#8230; <a title=\"React Best Practices: Build Scalable Apps Faster\" class=\"read-more\" href=\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-faster\/\" aria-label=\"Read more  on React Best Practices: Build Scalable Apps Faster\">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-4813","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 Faster - 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-faster\/\" \/>\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 Faster - ITSupportWale\" \/>\n<meta property=\"og:description\" content=\"text [2024-05-22T03:14:02.891Z] SEVERE: [MainThread] Uncaught RangeError: Maximum update depth exceeded. at Object.updateContainer (react-dom.development.js:25331:7) at Object.scheduleUpdateOnFiber (react-dom.development.js:21835:3) at dispatchSetState (react-dom.development.js:15853:7) at onAuthChange (AuthContext.tsx:42:5) [2024-05-22T03:14:03.112Z] FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed &#8211; JavaScript heap out of memory [2024-05-22T03:14:03.445Z] Process exited with code 137 (OOM) My eyes are vibrating. Not from the four double-shots of ... Read more\" \/>\n<meta property=\"og:url\" content=\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-faster\/\" \/>\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-11T18:24:29+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=\"11 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-faster\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-faster\/\"},\"author\":{\"name\":\"Techie\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d\"},\"headline\":\"React Best Practices: Build Scalable Apps Faster\",\"datePublished\":\"2026-06-11T18:24:29+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-faster\/\"},\"wordCount\":1284,\"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-faster\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-faster\/\",\"url\":\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-faster\/\",\"name\":\"React Best Practices: Build Scalable Apps Faster - ITSupportWale\",\"isPartOf\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/#website\"},\"datePublished\":\"2026-06-11T18:24:29+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-faster\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-faster\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-faster\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/itsupportwale.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"React Best Practices: Build Scalable Apps Faster\"}]},{\"@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 Faster - 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-faster\/","og_locale":"en_US","og_type":"article","og_title":"React Best Practices: Build Scalable Apps Faster - ITSupportWale","og_description":"text [2024-05-22T03:14:02.891Z] SEVERE: [MainThread] Uncaught RangeError: Maximum update depth exceeded. at Object.updateContainer (react-dom.development.js:25331:7) at Object.scheduleUpdateOnFiber (react-dom.development.js:21835:3) at dispatchSetState (react-dom.development.js:15853:7) at onAuthChange (AuthContext.tsx:42:5) [2024-05-22T03:14:03.112Z] FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed &#8211; JavaScript heap out of memory [2024-05-22T03:14:03.445Z] Process exited with code 137 (OOM) My eyes are vibrating. Not from the four double-shots of ... Read more","og_url":"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-faster\/","og_site_name":"ITSupportWale","article_publisher":"https:\/\/www.facebook.com\/Itsupportwale-298547177495978","article_published_time":"2026-06-11T18:24:29+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":"11 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-faster\/#article","isPartOf":{"@id":"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-faster\/"},"author":{"name":"Techie","@id":"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d"},"headline":"React Best Practices: Build Scalable Apps Faster","datePublished":"2026-06-11T18:24:29+00:00","mainEntityOfPage":{"@id":"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-faster\/"},"wordCount":1284,"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-faster\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-faster\/","url":"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-faster\/","name":"React Best Practices: Build Scalable Apps Faster - ITSupportWale","isPartOf":{"@id":"https:\/\/itsupportwale.com\/blog\/#website"},"datePublished":"2026-06-11T18:24:29+00:00","breadcrumb":{"@id":"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-faster\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-faster\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/itsupportwale.com\/blog\/react-best-practices-build-scalable-apps-faster\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/itsupportwale.com\/blog\/"},{"@type":"ListItem","position":2,"name":"React Best Practices: Build Scalable Apps Faster"}]},{"@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\/4813","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=4813"}],"version-history":[{"count":0,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/posts\/4813\/revisions"}],"wp:attachment":[{"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/media?parent=4813"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/categories?post=4813"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/tags?post=4813"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}