<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Abhijit Paul</title>
    <description>The latest articles on DEV Community by Abhijit Paul (@thexeromin).</description>
    <link>https://dev.to/thexeromin</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3865918%2F0f3bf8c7-17a8-4ae1-97c9-98a894d359a1.jpg</url>
      <title>DEV Community: Abhijit Paul</title>
      <link>https://dev.to/thexeromin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/thexeromin"/>
    <language>en</language>
    <item>
      <title>I Thought Normalized State Would Fix My Re-render Problem. It Didn't.</title>
      <dc:creator>Abhijit Paul</dc:creator>
      <pubDate>Tue, 07 Apr 2026 13:24:10 +0000</pubDate>
      <link>https://dev.to/thexeromin/i-thought-normalized-state-would-fix-my-re-render-problem-it-didnt-516d</link>
      <guid>https://dev.to/thexeromin/i-thought-normalized-state-would-fix-my-re-render-problem-it-didnt-516d</guid>
      <description>&lt;p&gt;I was building a Kanban board called &lt;strong&gt;Kantoo&lt;/strong&gt; and hit a classic React performance problem. This is what I tried, what didn't work, and what actually fixed it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built First
&lt;/h2&gt;

&lt;p&gt;I went with a normalized state architecture managed through &lt;code&gt;useReducer&lt;/code&gt;. The idea was simple: instead of deeply nested state, keep everything flat. Each column and card lives in its own lookup object, and you reference them by ID.&lt;/p&gt;

&lt;p&gt;It felt clean.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Even with normalized state, two things were going wrong.&lt;/p&gt;

&lt;p&gt;A single card update was causing every component on the board to re-render, including columns that had nothing to do with that card. I could see it clearly in React DevTools Profiler. The render time for a single card move was sitting at around 55ms.&lt;/p&gt;

&lt;p&gt;I was also passing data to components that didn't need it. Some components were receiving data purely so they could pass it further down, even though they didn't use it themselves. Just middlemen adding to the problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  My First Idea: React Context
&lt;/h2&gt;

&lt;p&gt;My first thought was to reach for Context. Put state in a provider and read only what each component needs inside a custom hook.&lt;/p&gt;

&lt;p&gt;Sounds good in theory. But here's the catch: even if you only read one value from context, React still re-renders the component whenever anything in that context changes. The component is subscribed to the entire context object under the hood.&lt;/p&gt;

&lt;p&gt;So Context would just move the problem, not solve it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Actually Fixed It: Zustand with Selective Subscriptions
&lt;/h2&gt;

&lt;p&gt;The real fix is a library that supports selective subscriptions, where a component only re-renders when the specific piece of state it cares about changes. Zustand does exactly this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This component only re-renders when cards changes&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cards&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useStore&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cards&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once I migrated Kantoo to Zustand and combined it with the normalized state architecture I already had, something clicked. Untouched cards stopped re-rendering entirely. A card move now only wakes up the components actually involved.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Results
&lt;/h2&gt;

&lt;p&gt;I measured both before and after using React DevTools Profiler.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0flz766cjb7fwpj7us0o.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0flz766cjb7fwpj7us0o.jpg" alt="React DevTools Profiler - useReducer, 55ms render time" width="800" height="261"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With useReducer and prop drilling, a single card move produced a render time of around &lt;strong&gt;55ms&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1whiu9msr5rxxdsx5c9q.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1whiu9msr5rxxdsx5c9q.jpg" alt="React DevTools Profiler - Zustand selective subscriptions, 15ms render time" width="800" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After migrating to Zustand with selective subscriptions, the same card move commits in around &lt;strong&gt;15ms&lt;/strong&gt;. That is a ~73% reduction, not from micro-optimizations but from rethinking how state flows through the tree.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Memoization Alone Was Not Enough
&lt;/h2&gt;

&lt;p&gt;This tripped me up early. &lt;code&gt;React.memo&lt;/code&gt; only prevents re-renders when props do not change. But when state lives at the top and gets passed down, the parent re-renders on any update and passes new references down, so memo never gets a chance to bail out.&lt;/p&gt;

&lt;p&gt;Zustand's subscriptions bypass this entirely. Components pull what they need straight from the store and ignore everything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Taught Me
&lt;/h2&gt;

&lt;p&gt;Once you understand the difference it changes how you think about state:&lt;/p&gt;

&lt;p&gt;Context is great for things that rarely change like a theme or current user. Zustand is better when you have frequently updated state that different components care about independently.&lt;/p&gt;

&lt;p&gt;The key phrase is granular subscriptions. Components only pay attention to exactly what they need, nothing more.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Check out &lt;a href="https://github.com/thexeromin/kantoo" rel="noopener noreferrer"&gt;Kantoo on GitHub&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>zustand</category>
    </item>
  </channel>
</rss>
