<?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: Peter Ogbonna</title>
    <description>The latest articles on DEV Community by Peter Ogbonna (@peterintech).</description>
    <link>https://dev.to/peterintech</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F859035%2F1b02f72b-8261-45be-bf32-722c45b62255.png</url>
      <title>DEV Community: Peter Ogbonna</title>
      <link>https://dev.to/peterintech</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/peterintech"/>
    <language>en</language>
    <item>
      <title>Go + templ + HTMX + Tailwind CSS v4 setup in 2026</title>
      <dc:creator>Peter Ogbonna</dc:creator>
      <pubDate>Thu, 03 Sep 2026 13:40:50 +0000</pubDate>
      <link>https://dev.to/peterintech/go-templ-htmx-tailwind-css-v4-setup-in-2026-1ghj</link>
      <guid>https://dev.to/peterintech/go-templ-htmx-tailwind-css-v4-setup-in-2026-1ghj</guid>
      <description>&lt;p&gt;I only wanted to style a Go application.&lt;/p&gt;

&lt;p&gt;Somehow, I ended up reading tutorials that asked me to run commands Tailwind no longer supports, create configuration files Tailwind v4 no longer needs, and install Node.js for a project that otherwise had nothing to do with Node.js.&lt;/p&gt;

&lt;p&gt;After enough dead ends, I found a setup I actually enjoy: Go renders the application, &lt;a href="https://templ.guide/" rel="noopener noreferrer"&gt;templ&lt;/a&gt; gives me typed HTML components, &lt;a href="https://htmx.org/" rel="noopener noreferrer"&gt;HTMX&lt;/a&gt; adds focused interactions, and Tailwind's standalone executable compiles the CSS. No frontend framework and no &lt;code&gt;node_modules&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;This is the guide I wish I had found.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Why this boring stack is interesting&lt;/li&gt;
&lt;li&gt;Where templ fits&lt;/li&gt;
&lt;li&gt;A tiny HTMX example&lt;/li&gt;
&lt;li&gt;Three ways to add Tailwind&lt;/li&gt;
&lt;li&gt;The standalone setup&lt;/li&gt;
&lt;li&gt;The commands you will actually use&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/peterintech/go-htmx-tailwindcss-setup-2026" rel="noopener noreferrer"&gt;Example repo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;References&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why this boring stack is interesting
&lt;/h2&gt;

&lt;p&gt;I mean &lt;code&gt;boring&lt;/code&gt; affectionately :)&lt;/p&gt;

&lt;p&gt;The browser sends an HTTP request. Go runs ordinary application code. A templ component renders HTML. HTMX can place a returned fragment into the page. Tailwind compiles the utility classes it finds into a static stylesheet.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw6nay04odu29nw3wyso5.png" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw6nay04odu29nw3wyso5.png" alt="The flow" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is one main programming language, one server, and HTML remains the application boundary. You do not need a client-side state store just to submit a form or refresh a list.&lt;/p&gt;

&lt;p&gt;This stack is especially pleasant for content sites, dashboards, admin tools, CRUD applications, and small products where most interactions naturally map to HTTP requests.&lt;/p&gt;

&lt;p&gt;It is not the answer to everything. A browser-based design tool or highly offline application may benefit from a substantial client-side runtime. But many applications are much closer to “request data, change data, render HTML” than their architecture admits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where templ fits
&lt;/h2&gt;

&lt;p&gt;Go already has &lt;a href="https://pkg.go.dev/html/template" rel="noopener noreferrer"&gt;&lt;code&gt;html/template&lt;/code&gt;&lt;/a&gt;, and it is good. It is in the standard library, widely understood, and performs contextual escaping to produce HTML safely. You do not need templ because the standard library is broken.&lt;/p&gt;

&lt;p&gt;I use templ because I prefer its development model for component-heavy interfaces and type system.&lt;/p&gt;

&lt;p&gt;Here is a small component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;package views

templ Greeting(name string) {
    &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"rounded-xl border border-slate-200 p-6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-2xl font-bold"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Hello, { name }.&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-2 text-slate-600"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Your page came from Go.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;templ turns this into Go code. The component has a typed parameter, can call other components, and can use familiar Go control flow. Misspell a field or pass the wrong type and the compiler can catch the mistake before the page reaches a user.&lt;/p&gt;

&lt;p&gt;The trade-off is code generation: after changing a &lt;code&gt;.templ&lt;/code&gt; file, you generate its corresponding Go source.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go tool templ generate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Go 1.24 and newer, I prefer tracking the generator as a project tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go get github.com/a-h/templ
go get &lt;span class="nt"&gt;-tool&lt;/span&gt; github.com/a-h/templ/cmd/templ@latest
go tool templ generate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or you can just install globally and use everytime:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go &lt;span class="nb"&gt;install &lt;/span&gt;github.com/a-h/templ/cmd/templ@latest
templ generate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A tiny HTMX example
&lt;/h2&gt;

&lt;p&gt;templ renders HTML, HTMX decides when a part of the page should ask the server for new HTML.&lt;br&gt;
visit the &lt;a href="https://htmx.org/docs" rel="noopener noreferrer"&gt;setup&lt;/a&gt;, download the &lt;code&gt;htmx.min.js&lt;/code&gt; and put in your &lt;code&gt;public&lt;/code&gt; folder&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;templ GreetingPanel() {
    &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"greeting"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt;
            &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"rounded-lg bg-emerald-800 px-4 py-2 text-white"&lt;/span&gt;
            &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"/greeting"&lt;/span&gt;
            &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#greeting"&lt;/span&gt;
            &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"outerHTML"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            Greet me
        &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
}

templ Greeting() {
    &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"greeting"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"rounded-xl bg-emerald-50 p-5"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"font-semibold text-emerald-950"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Hello from the server.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The button means: send &lt;code&gt;GET /greeting&lt;/code&gt;, take the HTML response and replace the element identified by &lt;code&gt;#greeting&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GET /greeting"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;views&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Greeting&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"could not render greeting"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusInternalServerError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no JSON response to decode and no duplicate client-side component to keep in sync. The server returns the next piece of UI.&lt;/p&gt;

&lt;p&gt;That is the useful boundary in this stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go owns application state and decisions.&lt;/li&gt;
&lt;li&gt;templ owns reusable HTML.&lt;/li&gt;
&lt;li&gt;HTMX requests and swaps HTML fragments.&lt;/li&gt;
&lt;li&gt;Tailwind owns presentation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Three ways to add Tailwind
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Path 1: the Play CDN
&lt;/h3&gt;

&lt;p&gt;The fastest possible experiment is Tailwind's browser package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add that to the page and Tailwind classes work without a build command.&lt;/p&gt;

&lt;p&gt;While the CDN approach is extremely simple, you may want to consider &lt;a href="https://blog.wesleyac.com/posts/why-not-javascript-cdn" rel="noopener noreferrer"&gt;not using CDNs in production&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That article discusses broader privacy, security, reliability, and performance trade-offs of third-party JavaScript CDNs. Tailwind's own documentation is more direct about this particular package stating that the Play CDN is designed for development, not production.&lt;/p&gt;

&lt;h3&gt;
  
  
  Path 2: npm or pnpm
&lt;/h3&gt;

&lt;p&gt;The official package-manager route is entirely valid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; tailwindcss @tailwindcss/cli
npx @tailwindcss/cli &lt;span class="nt"&gt;-i&lt;/span&gt; ./views/css/styles.css &lt;span class="nt"&gt;-o&lt;/span&gt; ./public/styles.css &lt;span class="nt"&gt;--watch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or with pnpm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm add &lt;span class="nt"&gt;-D&lt;/span&gt; tailwindcss @tailwindcss/cli
pnpm &lt;span class="nb"&gt;exec &lt;/span&gt;tailwindcss &lt;span class="nt"&gt;-i&lt;/span&gt; ./views/css/styles.css &lt;span class="nt"&gt;-o&lt;/span&gt; ./public/styles.css &lt;span class="nt"&gt;--watch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you normal dependency declarations and a lockfile, and updates fit the JavaScript package-management workflow. If the project already uses npm for other frontend tooling, this is a fair choice.&lt;/p&gt;

&lt;p&gt;The downside in a Go only project is that Tailwind introduces an additional toolchain: Node.js, a &lt;code&gt;package.json&lt;/code&gt;, a lockfile, install commands in CI, and a local &lt;code&gt;node_modules&lt;/code&gt; directory. You normally ignore &lt;code&gt;node_modules&lt;/code&gt; rather than commit it, but it still exists on each development machine and build environment.&lt;/p&gt;

&lt;p&gt;None of that is catastrophic, it was simply machinery I did not otherwise need.&lt;/p&gt;

&lt;h3&gt;
  
  
  Path 3: The standalone executable (recommended)
&lt;/h3&gt;

&lt;p&gt;Tailwind also publishes a self-contained CLI for Linux, macOS, and Windows. It performs the build ahead of time and emits an ordinary CSS file, without requiring Node.js or npm on your machine.&lt;/p&gt;

&lt;p&gt;That is the path I use.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Standalone Setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Download the correct binary
&lt;/h3&gt;

&lt;p&gt;Open the official &lt;a href="https://github.com/tailwindlabs/tailwindcss/releases/latest" rel="noopener noreferrer"&gt;Tailwind CSS releases page&lt;/a&gt; and choose the executable for your operating system and CPU and download.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Put it somewhere sensible
&lt;/h3&gt;

&lt;p&gt;You have two good options.&lt;/p&gt;

&lt;p&gt;Keep it inside the project, for example in &lt;code&gt;./bin/tailwindcss&lt;/code&gt;, and invoke that exact path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./bin/tailwindcss &lt;span class="nt"&gt;-i&lt;/span&gt; ./views/css/styles.css &lt;span class="nt"&gt;-o&lt;/span&gt; ./public/styles.css &lt;span class="nt"&gt;--watch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or put it in a directory already on your &lt;code&gt;PATH&lt;/code&gt;, so you can run &lt;code&gt;tailwindcss&lt;/code&gt; anywhere.&lt;/p&gt;

&lt;p&gt;Go developers often already have their Go tool directory on &lt;code&gt;PATH&lt;/code&gt;. Find the location with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go &lt;span class="nb"&gt;env &lt;/span&gt;GOPATH
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Linux users that becomes &lt;code&gt;$HOME/go/bin&lt;/code&gt; and &lt;code&gt;%USERPROFILE%\\bin&lt;/code&gt; for windows users. Copying Tailwind executable there saves a separate &lt;code&gt;PATH&lt;/code&gt; change only if that directory is already on your &lt;code&gt;PATH&lt;/code&gt;, else add the folder to &lt;code&gt;PATH&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Create the Tailwind entry file
&lt;/h3&gt;

&lt;p&gt;Here is the entire minimum Tailwind v4 entrypoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* views/css/styles.css */&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;"tailwindcss"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It does not require &lt;code&gt;tailwind.config.js&lt;/code&gt; or &lt;code&gt;tailwindcss init&lt;/code&gt; for the basic setup. Tailwind v4 moved toward CSS-first configuration.&lt;/p&gt;

&lt;p&gt;If you execute the build from your repository root, automatic source detection will usually find the class names in your &lt;code&gt;.templ&lt;/code&gt; files and generate the plain css which you'll hook up to your &lt;code&gt;&amp;lt;link /&amp;gt;&lt;/code&gt; tag&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Compile the stylesheet
&lt;/h3&gt;

&lt;p&gt;During development:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tailwindcss &lt;span class="nt"&gt;-i&lt;/span&gt; ./views/css/styles.css &lt;span class="nt"&gt;-o&lt;/span&gt; ./public/styles.css &lt;span class="nt"&gt;--watch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a production build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tailwindcss &lt;span class="nt"&gt;-i&lt;/span&gt; ./views/css/styles.css &lt;span class="nt"&gt;-o&lt;/span&gt; ./public/styles.css &lt;span class="nt"&gt;--minify&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser receives &lt;code&gt;/public/styles.css&lt;/code&gt;; it never knows or cares how that file was produced.&lt;/p&gt;

&lt;h2&gt;
  
  
  The commands you will actually use
&lt;/h2&gt;

&lt;p&gt;Once everything is installed, the working loop is small:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Generate Go code from .templ files once&lt;/span&gt;
go tool templ generate

&lt;span class="c"&gt;# Regenerate templates as they change&lt;/span&gt;
go tool templ generate &lt;span class="nt"&gt;--watch&lt;/span&gt;

&lt;span class="c"&gt;# Rebuild CSS as Tailwind classes change&lt;/span&gt;
tailwindcss &lt;span class="nt"&gt;-i&lt;/span&gt; ./views/css/styles.css &lt;span class="nt"&gt;-o&lt;/span&gt; ./public/styles.css &lt;span class="nt"&gt;--watch&lt;/span&gt;

&lt;span class="c"&gt;# Run the Go application&lt;/span&gt;
go run &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Run the watchers in separate terminals, or let a task runner coordinate them, a small Makefile is enough&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Tailwind watcher rebuilds CSS and it does not restart your Go process, templ provides its own reload workflow.&lt;/p&gt;

&lt;p&gt;templ/tailwind rebuild, server restart, and browser reload are three different jobs.&lt;/p&gt;

&lt;p&gt;I also put together a complete example repository containing this setup:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/peterintech/go-htmx-tailwindcss-setup-2026" rel="noopener noreferrer"&gt;Browse the example Go + templ + HTMX + Tailwind v4 repository&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;TailwindCss&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://templ.guide/quick-start/" rel="noopener noreferrer"&gt;templ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://htmx.org/docs/" rel="noopener noreferrer"&gt;HTMX documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pkg.go.dev/html/template" rel="noopener noreferrer"&gt;Go &lt;code&gt;html/template&lt;/code&gt; documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.wesleyac.com/posts/why-not-javascript-cdn" rel="noopener noreferrer"&gt;Reasons to avoid JavaScript CDNs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>go</category>
      <category>htmx</category>
      <category>tailwindcss</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Manage State like a Pro: Frontend Edition</title>
      <dc:creator>Peter Ogbonna</dc:creator>
      <pubDate>Thu, 20 Aug 2026 16:32:06 +0000</pubDate>
      <link>https://dev.to/peterintech/manage-state-like-a-pro-frontend-edition-1id0</link>
      <guid>https://dev.to/peterintech/manage-state-like-a-pro-frontend-edition-1id0</guid>
      <description>&lt;p&gt;AI models write code better and faster than devs, but what AI cannot do reliably for your specific product is make the fundamental &lt;em&gt;system design decisions like&lt;/em&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Where should this piece of data actually live, who owns it, and what happens to the user experience when the network fails, the user refreshes, or a link is shared?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The modern frontend engineer’s primary value has shifted from &lt;em&gt;writing code&lt;/em&gt; to &lt;em&gt;evaluating trade-offs&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;If you put every piece of data into a global Redux store or local &lt;code&gt;useState&lt;/code&gt;, you aren't just writing messy code, you are making a system design error. To build fast, resilient interfaces, you need to treat state management as a distribution problem across three distinct domains: &lt;strong&gt;Server State&lt;/strong&gt;, &lt;strong&gt;Client State&lt;/strong&gt;, and &lt;strong&gt;URL State&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Take a look at this example
&lt;/h2&gt;

&lt;p&gt;You must have come across this type of code before...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Wrong: Blending 3 different types of state into one&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setProducts&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt; &lt;span class="c1"&gt;// Server Data&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;searchQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSearchQuery&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// URL/Navigation Intent&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isFilterModalOpen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsFilterModalOpen&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Local UI&lt;/span&gt;

&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Syncing server state based on local client variables&lt;/span&gt;
  &lt;span class="nf"&gt;fetchProducts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;searchQuery&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;setProducts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;searchQuery&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you mix these together, your application breaks down in subtle, frustrating ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Stale Data:&lt;/strong&gt; You manually fetch server data, cache it in React memory, and forget to revalidate it when the database changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State Blackout:&lt;/strong&gt; The user customizes their search view, hits refresh, and gets reset to page one because the view state only lived in ephemeral browser memory&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bloated Memory:&lt;/strong&gt; Temporary UI toggles (like "is dropdown open") leak into global stores, making the app harder to debug and test.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To fix this, we need to categorize data by &lt;strong&gt;ownership and lifecycle&lt;/strong&gt;, not just syntax.&lt;/p&gt;




&lt;h2&gt;
  
  
  The 3 Pillars of Frontend State Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────┬───────────────────────────────┬──────────────────────────────────┐
│ State Category  │ What It Really Is             │ Best Tooling / Pattern           │
├─────────────────┼───────────────────────────────┼──────────────────────────────────┤
│ 1. Server State │ External data owned by the DB │ Next.js Cache, TanStack Query,   │
│                 │ (Async, cached, shared)       │ RTK Query, SWR                   │
├─────────────────┼───────────────────────────────┼──────────────────────────────────┤
│ 2. URL State    │ Navigation intent             │ `searchParams`, React Router,    │
│                 │ (Shareable, persistent)       │ `window.location`                │
├─────────────────┼───────────────────────────────┼──────────────────────────────────┤
│ 3. Client State │ Ephemeral UI memory           │ `useState`, `useReducer`,        │
│                 │ (Local, temporary, interactive)│ Zustand, React Context            │
└─────────────────┴───────────────────────────────┴──────────────────────────────────┘

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  1. Server State: You Don't Own It
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;The Mental Model:&lt;/em&gt; Server state is data that lives on a remote database (user profiles, product lists, checkout carts). You do &lt;em&gt;not&lt;/em&gt; own this data on the client - you're merely borrowing a temporary snapshot of it.&lt;/p&gt;

&lt;p&gt;Because server state is asynchronous and shared across multiple users, its main challenges are &lt;strong&gt;staleness, caching, deduplication, and revalidation.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;When to use it:&lt;/strong&gt; Any data that originates from a database or external API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How to manage it:&lt;/strong&gt; Stop putting API response data into local &lt;code&gt;useState&lt;/code&gt; or traditional Redux stores. Use dedicated server-state tools like &lt;em&gt;TanStack Query&lt;/em&gt;, &lt;em&gt;SWR&lt;/em&gt;, or &lt;em&gt;Next.js Server Components with Data Caching&lt;/em&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Right: Server state managed by a tool built for caching &amp;amp; revalidation&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLoading&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;queryFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fetchUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;staleTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 5 minutes before re-fetching&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  2. URL State: The Forgotten Source of Truth
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The Mental Model:&lt;/strong&gt; URL state is the input parameter for your entire screen. It represents &lt;strong&gt;navigation intent&lt;/strong&gt;, what specific view, page, or filtered slice of data the user wants to see right now.&lt;/p&gt;

&lt;p&gt;If a user configures a complex search filter on an e-commerce site, that view should exist as a shareable link. If you trap those filters inside React’s &lt;code&gt;useState&lt;/code&gt;, you strip the web of its best native feature: &lt;strong&gt;the link.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;When to use it:&lt;/strong&gt; Search queries, category filters, pagination numbers, active tabs, sorting directions, and selected view modes (grid vs. list).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How to manage it:&lt;/strong&gt; Sync UI controls directly to URL search parameters (&lt;code&gt;?search=shoes&amp;amp;page=2&lt;/code&gt;).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// URL State as the single source of truth for view parameters&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;CatalogPage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;searchParams&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// The server/API reads directly from the URL context&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getProducts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ProductGrid&lt;/span&gt; &lt;span class="na"&gt;products&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"{products}"&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  3. Client State: Ephemeral UI Memory
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The Mental Model:&lt;/strong&gt; Client state is purely local, temporary UI memory. It does not come from a database, and it doesn't need to survive a page share or a browser refresh.&lt;/p&gt;

&lt;p&gt;If the user closes the tab, this data can safely disappear without ruining their experience.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;When to use it:&lt;/strong&gt; Modal open/close toggles, hover states, active tab animations, form input drafts before submission, dark mode toggles, and multi-step wizard progress.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How to manage it:&lt;/strong&gt; Keep it close to where it's used with &lt;code&gt;useState&lt;/code&gt; or &lt;code&gt;useReducer&lt;/code&gt;. If it needs to be accessed globally across unrelated components (like a sidebar collapse state), use a lightweight client store like &lt;strong&gt;Zustand&lt;/strong&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simple, isolated local client state&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isSidebarOpen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsSidebarOpen&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Evaluating the Trade-offs
&lt;/h2&gt;

&lt;p&gt;When designing a feature, ask yourself these three sequential questions to determine where state belongs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                     [New Piece of State]
                              │
              Does it come from an API/Database?
             ┌────────────────┴────────────────┐
            YES                                NO
             │                                 │
     (1. SERVER STATE)              Should this view be shareable 
  Use: Next.js / TanStack           or survive a page refresh?
                                   ┌───────────┴───────────┐
                                  YES                      NO
                                   │                       │
                            (2. URL STATE)          (3. CLIENT STATE)
                          Use: searchParams        Use: useState / Zustand

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Practical Example - an E-Commerce Product Page
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Product List Data:&lt;/strong&gt; This is &lt;em&gt;server state&lt;/em&gt;. It should be fetched and cached via server components or a query hook so it can revalidate in the background.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Active Category &amp;amp; Search Term:&lt;/strong&gt; This is &lt;em&gt;URL state&lt;/em&gt; (&lt;code&gt;/products?category=shoes&amp;amp;q=nike&lt;/code&gt;). If the user emails this link to a friend, the friend sees the exact same Nike shoes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The "Filter Drawer Open" Toggle:&lt;/strong&gt; This is &lt;em&gt;client state&lt;/em&gt;  The friend doesn't need the filter drawer to automatically pop open just because the sender had it open.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Writing syntax is rapidly becoming an automated commodity. You can prompt an AI tool to write a complex reducer or a fetch handler in seconds.&lt;/p&gt;

&lt;p&gt;However, choosing &lt;strong&gt;where state lives&lt;/strong&gt;, &lt;strong&gt;how it expires&lt;/strong&gt;, and &lt;strong&gt;how it travels across the network&lt;/strong&gt; requires engineering judgment.&lt;/p&gt;

&lt;p&gt;By cleanly separating your architecture into &lt;strong&gt;Server State&lt;/strong&gt; (remote truth), &lt;strong&gt;URL State&lt;/strong&gt; (navigation truth), and &lt;strong&gt;Client State&lt;/strong&gt; (interactive truth), you build applications that are inherently faster, far more resilient, and built to scale.&lt;/p&gt;

</description>
      <category>react</category>
      <category>architecture</category>
      <category>frontend</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Understanding Next.js Cache (Part 5)</title>
      <dc:creator>Peter Ogbonna</dc:creator>
      <pubDate>Sat, 28 Mar 2026 12:24:28 +0000</pubDate>
      <link>https://dev.to/peterintech/understanding-nextjs-cache-part-5-3lok</link>
      <guid>https://dev.to/peterintech/understanding-nextjs-cache-part-5-3lok</guid>
      <description>&lt;p&gt;If you have built an application using the Next.js App Router, there is a 100% chance you've come across the issue of updating the database but the page still showing the old data.&lt;/p&gt;

&lt;p&gt;Welcome to the &lt;em&gt;Server-Side Caching&lt;/em&gt; paradigm. &lt;/p&gt;

&lt;p&gt;In the React SPA world, if a user hits refresh, the cache is wiped clean. Next.js flipped the script. It caches data on the &lt;em&gt;Server&lt;/em&gt;, and by default, it caches everything, aggressively. &lt;/p&gt;

&lt;p&gt;To understand why your app is behaving the way it is we'll look at some concepts to understand how the Next.js cache actually works:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The Bakery Analogy (API Data vs Pages)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Think of your Next.js application as a Bakery. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;The API Data (The Data Cache)&lt;/em&gt; will be our raw ingredients (flour, sugar, eggs) fetched from your database or external APIs. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;The Pages (The Full Route Cache)&lt;/em&gt; will be the fully baked cake sitting in the display window.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next.js doesn't just cache the finished HTML (cake), it also caches the raw JSON (ingredients). This means if 50 users from different location visit your site at the exact same time, Next.js doesn't overwhelm your database with 50 queries. It uses the cached ingredients to serve everyone instantly.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The 4 Layers of Caching&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The most common point of confusion is what happens when a user hits the refresh button. Let's break down the four layers of the Next.js cache using our kitchen&lt;br&gt;
This now leads us to the 4 layers of caching when dealing with Next.js:&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;1. Request Memoization (The Countertop/Clipboard)&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;If three different React components on the same page all call &lt;code&gt;fetch('/api/user')&lt;/code&gt;, Next.js deduplicates them. It only makes the network request once and shares the result. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE: The countertop/clipboard is wiped clean after every single page render.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;2. Router Cache (The Customer's Plate)&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;As a user clicks around your app, Next.js stores the React Server Component payloads in the browser's memory. This makes clicking the &lt;em&gt;Back&lt;/em&gt; button feel instantaneous.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE: This doesn't survive a refresh either, a hard refresh wipes the browser's memory (washes the plate). &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;3. Data Cache (The Pantry)&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;This is where Next.js stores the raw JSON data returned from your database across &lt;em&gt;multiple&lt;/em&gt; user sessions. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE: This survives a refresh, if you refresh the page, the browser asks the server for new data, but the server just pulls the same cached data from the pantry. &lt;br&gt;
This is why refreshing often doesn't "fix" stale data!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;4. Full Route Cache (The Display Window)&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Next.js takes the cached data and your components, rendering them into pure, static HTML at build time. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE: This survives a refresh as it serves the pre-rendered HTML file until the underlying &lt;em&gt;data cache&lt;/em&gt; is explicitly invalidated.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Taking Control of the Cache&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Caching is fantastic for performance, but dynamic apps need dynamic data. Here is how you take control and tell Next.js to throw away the old ingredients.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Opting Out Entirely (No Caching)&lt;/strong&gt;&lt;br&gt;
If a piece of data changes constantly (like a live stock ticker or bank account balance), tell Next.js not to cache the &lt;code&gt;fetch&lt;/code&gt; request at all:&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;// Fetched fresh on every single request&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.example.com/data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no-store&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; 
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Time-Based Revalidation (Stale-While-Revalidate)&lt;/strong&gt;&lt;br&gt;
Tell Next.js to keep the cache for a specific amount of time before fetching fresh data in the background:&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;// Revalidate this data every 3600 seconds (1 hour)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.example.com/data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="na"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;revalidate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. On-Demand Revalidation (The "Invalidate" Button)&lt;/strong&gt;&lt;br&gt;
When a user submits a form (like updating their profile), you need to instantly clear the cache for that specific page. You do this inside a Server Action:&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;revalidatePath&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updateProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Tell Next.js to throw away the cached HTML and data for this route&lt;/span&gt;
  &lt;span class="nf"&gt;revalidatePath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/profile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;The Grand Finale&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Over these 5 articles, we went from understanding the theory of "Fresh vs Stale" to mastering HTTP headers, taming React-Query/Redux client state and finally conquering the Next.js server cache. &lt;/p&gt;

&lt;p&gt;Caching is no longer just a backend optimization. It is the beating heart of the frontend User Experience. Master it, and you will build applications that are absolutely instantaneous and save server cost &lt;/p&gt;

&lt;p&gt;Now go build something fast.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>react</category>
      <category>webdev</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Automated Caching with RTK Query (Part 4)</title>
      <dc:creator>Peter Ogbonna</dc:creator>
      <pubDate>Sat, 28 Feb 2026 14:10:23 +0000</pubDate>
      <link>https://dev.to/peterintech/automated-caching-with-rtk-query-part-4-4c5n</link>
      <guid>https://dev.to/peterintech/automated-caching-with-rtk-query-part-4-4c5n</guid>
      <description>&lt;p&gt;In &lt;a href="https://dev.to/peterintech/tanstack-query-changed-everything-part-3-5e6l"&gt;Part 3&lt;/a&gt;, we looked at how Tanstack Query revolutionized caching by moving server state out of our global state managers. But what if you work on a massive enterprise application that is already deeply invested in &lt;a href="https://redux.js.org/" rel="noopener noreferrer"&gt;Redux&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;Do you have to rip out Redux to get good caching? Absolutely not.&lt;/p&gt;

&lt;p&gt;Welcome to &lt;em&gt;Part 4&lt;/em&gt;. Let's talk about the tool that made Redux fun again: &lt;strong&gt;RTK Query&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Introduction&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If you spend enough time on tech Twitter (X), you will inevitably hear people saying things like &lt;em&gt;"Redux is dead."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is not true as redux is powering some of the largest, most complex applications on the internet. However, the way we write Redux has fundamentally changed with the introduction of &lt;a href="https://redux-toolkit.js.org/" rel="noopener noreferrer"&gt;Redux-Toolkit&lt;/a&gt; (RTK)&lt;/p&gt;

&lt;p&gt;In the old days, fetching a list of users in Redux required a mountain of boilerplate:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write an &lt;code&gt;Action&lt;/code&gt; for &lt;code&gt;FETCH_USERS_REQUEST&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Write an &lt;code&gt;Action&lt;/code&gt; for &lt;code&gt;FETCH_USERS_SUCCESS&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Write an &lt;code&gt;Action&lt;/code&gt; for &lt;code&gt;FETCH_USERS_FAILURE&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Write a massive &lt;code&gt;Thunk&lt;/code&gt; to make the actual API call.&lt;/li&gt;
&lt;li&gt;Write a &lt;code&gt;Reducer&lt;/code&gt; to handle the &lt;code&gt;isLoading&lt;/code&gt;, &lt;code&gt;data&lt;/code&gt;, and &lt;code&gt;error&lt;/code&gt; states.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It was exhausting. And worst of all? It didn't even cache the data properly. If you navigated away and came back, your &lt;code&gt;Thunk&lt;/code&gt; would just fetch it all over again.&lt;/p&gt;

&lt;p&gt;In &lt;em&gt;Part 4&lt;/em&gt; of our series, we are looking at &lt;strong&gt;RTK Query&lt;/strong&gt; (Redux Toolkit Query) which was primarily built for data fetching and caching solution integrated directly into Redux.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Architecture: Centralized vs. Decentralized&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/peterintech/tanstack-query-changed-everything-part-3-5e6l"&gt;Part 3&lt;/a&gt;, we looked at React Query. React Query is largely decentralized. You call &lt;code&gt;useQuery&lt;/code&gt; wherever you need it, and it magically syncs up based on a &lt;code&gt;queryKey&lt;/code&gt; (like &lt;code&gt;['users']&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;RTK Query takes a different, highly structured approach. It uses the &lt;em&gt;API Slice&lt;/em&gt; pattern.&lt;/p&gt;

&lt;p&gt;Instead of writing fetch calls scattered across your components, you define your entire API in one central location.&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createApi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fetchBaseQuery&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@reduxjs/toolkit/query/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiSlice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createApi&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;reducerPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;baseQuery&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;fetchBaseQuery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;endpoints&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;getUsers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="na"&gt;updateUser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mutation&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`/users/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PUT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// RTK Query auto-generates custom React hooks for you&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useGetUsersQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useUpdateUserMutation&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;apiSlice&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With just that block of code, RTK Query replaces hundreds of lines of legacy Redux boilerplate. It automatically handles the fetching, the &lt;code&gt;isLoading&lt;/code&gt; state, the deduplication, and the caching.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Solving the Hardest Problem using "Tags"&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/peterintech/the-frontend-caching-handbook-part-1-2g30"&gt;Part 1&lt;/a&gt;, we established that cache invalidation (knowing when to throw old data away) is one of the hardest problems in computer science.&lt;/p&gt;

&lt;p&gt;RTK Query solves this using an incredibly elegant system called &lt;em&gt;Automated Tag-based Invalidation&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Think of a Tag like a sticky note you put on a piece of data.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;providesTags&lt;/code&gt; (Applying the label):&lt;br&gt;
When you fetch the list of users, you tell RTK Query to tag that cached data with &lt;code&gt;['User']&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;invalidatesTags&lt;/code&gt; (Tearing off the label): &lt;br&gt;
When you run a mutation to update a specific user, you tell RTK Query to invalidate the &lt;code&gt;['User']&lt;/code&gt; tag.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Inside your endpoints builder:&lt;/span&gt;
&lt;span class="nx"&gt;getUsers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;providesTags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// Label this data as 'User'&lt;/span&gt;
&lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="nx"&gt;updateUser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mutation&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`/users/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PUT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;invalidatesTags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// Throw away any cache labeled 'User'&lt;/span&gt;
&lt;span class="p"&gt;}),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The moment &lt;code&gt;updateUser&lt;/code&gt; succeeds, RTK Query automatically sees that the &lt;code&gt;['User']&lt;/code&gt; tag was invalidated. It instantly looks at your app, finds any component currently displaying the &lt;code&gt;getUsers&lt;/code&gt; query, and forces a background refetch.&lt;/p&gt;

&lt;p&gt;Your UI updates automatically. No manual dispatching. No messy states.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;React Query vs. RTK Query: Which should you choose?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Since both tools solve the same problem (Server State caching), which one should you use?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Choose React Query:&lt;/strong&gt; If you are starting a new project, you don't need a massive global client-state store, or you prefer a lightweight, flexible, decentralized approach.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose RTK Query:&lt;/strong&gt; If you are already using Redux Toolkit. Adding React Query to an existing Redux app means you now have two separate stores to manage and debug. RTK Query drops right into your existing &lt;code&gt;Redux DevTools&lt;/code&gt;, keeping your architecture clean and unified.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What’s Next?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Up to this point, we have focused entirely on caching data inside the user's browser (Client-Side Caching). But the frontend world is shifting.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Part 5&lt;/strong&gt;, the grand finale of this series, we are crossing the chasm into &lt;strong&gt;Server-Side Caching&lt;/strong&gt;. We will look at Next.js (using App Router) and how it aggressively caches not just data, but entire HTML pages and React Server Components.&lt;/p&gt;

&lt;p&gt;Prepare to have your mental model challenged one last time. See you in Part 5.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>architecture</category>
      <category>redux</category>
      <category>react</category>
    </item>
    <item>
      <title>TanStack Query Changed Everything (Part 3)</title>
      <dc:creator>Peter Ogbonna</dc:creator>
      <pubDate>Wed, 18 Feb 2026 12:00:30 +0000</pubDate>
      <link>https://dev.to/peterintech/tanstack-query-changed-everything-part-3-5e6l</link>
      <guid>https://dev.to/peterintech/tanstack-query-changed-everything-part-3-5e6l</guid>
      <description>&lt;p&gt;In &lt;a href="https://dev.to/peterintech/the-invisible-layer-mastering-http-caching-part-2-31gn"&gt;Part 2&lt;/a&gt;, we learnt about the invisible layer of HTTP caching. Now, we move to the application layer.&lt;/p&gt;

&lt;p&gt;You can relate to the nightmare associated with managing data coming from the server.&lt;/p&gt;

&lt;p&gt;We have all written this code before:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a &lt;code&gt;useState&lt;/code&gt; for &lt;code&gt;data&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;useState&lt;/code&gt; for &lt;code&gt;isLoading&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;useState&lt;/code&gt; for &lt;code&gt;error&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Write a &lt;code&gt;useEffect&lt;/code&gt; to fetch the data.&lt;/li&gt;
&lt;li&gt;Realize we forgot to handle race conditions like what if the component unmounts before the fetch finishes?&lt;/li&gt;
&lt;li&gt;Realize we have no way to &lt;a href="https://www.oracle.com/data-deduplication" rel="noopener noreferrer"&gt;deduplicate&lt;/a&gt; requests if two components need the same data.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As I argued in my &lt;a href="https://dev.to/peterintech/stop-writing-messy-states-why-i-swapped-useeffect-for-react-router-loaders-46mm"&gt;previous article about React Router Loaders&lt;/a&gt;, manual state management looks messy and at times, tend to be where bugs go to hide.&lt;/p&gt;

&lt;p&gt;Today, we look at &lt;em&gt;&lt;a href="https://tanstack.com/query/v4/docs/framework/react/overview" rel="noopener noreferrer"&gt;TanStack Query&lt;/a&gt; (React Query)&lt;/em&gt;*. It isn't just a data fetching library; it is an async state manager that brings the &lt;code&gt;stale-while-revalidate&lt;/code&gt; strategy (discussed in &lt;a href="https://dev.to/peterintech/the-invisible-layer-mastering-http-caching-part-2-31gn"&gt;part 2&lt;/a&gt;) directly into your components.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Philosophy: Server State vs. Client State
&lt;/h3&gt;

&lt;p&gt;The biggest breakthrough TanStack Query brought to the frontend world is the realization that &lt;em&gt;Server State is not the same as Client State.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Client State&lt;/em&gt; asks these questions: Is the modal open? What is the current theme? Is the sidebar collapsed? All these belongs in &lt;code&gt;useState&lt;/code&gt;, &lt;a href="https://react.dev/reference/react/useContext" rel="noopener noreferrer"&gt;Context&lt;/a&gt;, or &lt;a href="https://zustand.docs.pmnd.rs/getting-started/introduction" rel="noopener noreferrer"&gt;Zustand&lt;/a&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For &lt;em&gt;Server State&lt;/em&gt;, we talk about: The list of users, the current blog post.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Server state is "borrowed" as it's data that exists remotely, owned by the server (somebody else), hence can become &lt;code&gt;stale&lt;/code&gt; without you knowing. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;TanStack Query&lt;/em&gt; is designed explicitly to manage &lt;code&gt;server state&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Side-by-Side Comparison
&lt;/h3&gt;

&lt;p&gt;Let's look at the difference.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Old Way&lt;/em&gt; - using &lt;code&gt;useEffect&lt;/code&gt;:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsLoading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;isMounted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isMounted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nf"&gt;setIsLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isMounted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;isMounted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;!&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;The New way&lt;/em&gt; - using &lt;code&gt;TanStack Query&lt;/code&gt;:&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;// UserProfile.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useQuery&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@tanstack/react-query&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;queryFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;!&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It looks similar, but under the hood, TanStack Query is doing something magical, which is &lt;em&gt;Global Caching&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;If you use this &lt;code&gt;&amp;lt;UserProfile /&amp;gt;&lt;/code&gt; component in 5 different places on your screen, React Query will deduplicate the requests. It will only fetch once and serve the result to all 5 components instantly.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Configuration: &lt;code&gt;staleTime&lt;/code&gt; vs &lt;code&gt;gcTime&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;TanStack Query uses the combo of these two to handle caching&lt;/p&gt;

&lt;h4&gt;
  
  
  1. &lt;code&gt;staleTime&lt;/code&gt; (The "Freshness" Timer)
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;This controls &lt;strong&gt;when data is considered old&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Default is &lt;code&gt;0&lt;/code&gt; (Zero).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If &lt;code&gt;staleTime&lt;/code&gt; is &lt;code&gt;5000&lt;/code&gt; (5 seconds), the data remains &lt;em&gt;Fresh&lt;/em&gt; for 5 seconds. During this time, if another component asks for this data, it uses the cache preventing a background refetch.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After 5 seconds, the data becomes &lt;em&gt;Stale&lt;/em&gt;, TanStack Query will still serve it instantly but will trigger a background refetch to update it.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;It's default is zero, because TanStack Query assumes your data is always out of date.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;2. &lt;code&gt;gcTime&lt;/code&gt; (The "Garbage Collection" Timer)&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;Formerly known as &lt;code&gt;cacheTime&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This controls &lt;strong&gt;how long unused data stays in memory&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Default is &lt;code&gt;300000&lt;/code&gt; (5 minutes).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If you navigate away from the &lt;code&gt;UserProfile&lt;/code&gt; page, the data is no longer being used.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;React Query keeps it in memory for 5 minutes in case you come back.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you return within 5 minutes, the data appears instantly else, the data is gone and you get a refetch.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Solving the Hardest Problem (Invalidation)
&lt;/h3&gt;

&lt;p&gt;In &lt;a href="https://dev.to/peterintech/the-frontend-caching-handbook-part-1-2g30"&gt;Part 1&lt;/a&gt;, we said invalidation is the hardest problem in CS. &lt;/p&gt;

&lt;p&gt;TanStack Query solves this with &lt;code&gt;invalidateQueries&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Imagine you have a mutation to update a user's name.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mutation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMutation&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;mutationFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;updateUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;onSuccess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// This tells TanStack Query the 'user' data is now dirty. It uses the 'queryKey' to identify which to invalidate&lt;/span&gt;
    &lt;span class="nx"&gt;queryClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invalidateQueries&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When this runs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The mutation succeeds.&lt;/li&gt;
&lt;li&gt;React Query marks the &lt;code&gt;['user']&lt;/code&gt; cache as &lt;em&gt;Stale&lt;/em&gt; immediately.&lt;/li&gt;
&lt;li&gt;Any component currently displaying the user triggers a background refetch, hence automatically updating the UI!&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  What’s Next?
&lt;/h3&gt;

&lt;p&gt;TanStack Query is the standard for general Frontend applications. But what if you are using Redux? What if you want this caching power but integrated tightly with your global store?&lt;/p&gt;

&lt;p&gt;In &lt;em&gt;Part 4&lt;/em&gt;, we will explore &lt;a href="https://redux-toolkit.js.org/tutorials/rtk-query" rel="noopener noreferrer"&gt;RTK Query&lt;/a&gt; (Redux Toolkit Query). We will see how it takes these same concepts and applies them to the Redux ecosystem.&lt;/p&gt;

&lt;p&gt;See you in Part 4.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>javascript</category>
      <category>performance</category>
    </item>
    <item>
      <title>The Invisible Layer: Mastering HTTP Caching (Part 2)</title>
      <dc:creator>Peter Ogbonna</dc:creator>
      <pubDate>Mon, 16 Feb 2026 22:45:36 +0000</pubDate>
      <link>https://dev.to/peterintech/the-invisible-layer-mastering-http-caching-part-2-31gn</link>
      <guid>https://dev.to/peterintech/the-invisible-layer-mastering-http-caching-part-2-31gn</guid>
      <description>&lt;p&gt;"I updated the data in the database, but the user is still seeing the old version!"&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%2Fvaitme96wljobfph9hzt.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%2Fvaitme96wljobfph9hzt.jpg" alt="Confused dude" width="799" height="572"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you've ever screamed this while frantically hard-refreshing your browser, you have met the &lt;em&gt;HTTP Cache&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;&lt;a href="https://dev.to/peterintech/the-frontend-caching-handbook-part-1-2g30"&gt;Part 1&lt;/a&gt;&lt;/strong&gt;, I discussed the mindset of caching. Today, we look at the mechanics.&lt;/p&gt;

&lt;p&gt;Most frontend engineers don't realize the massive conversation happening in the Network Tab. Conversations in the &lt;em&gt;Response Headers&lt;/em&gt; determines whether your request even reaches the server at all.&lt;/p&gt;

&lt;p&gt;This is why it's important to master the &lt;em&gt;Invisible Layer&lt;/em&gt; to understand caching better and know what happens under the hood, most of the library you use are built on this.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Browser’s Decision Tree
&lt;/h3&gt;

&lt;p&gt;When you call &lt;code&gt;fetch('/api/user')&lt;/code&gt; for example, the browser doesn't immediately go to the internet, it goes through a strict checklist.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Memory/Disk Check:&lt;/em&gt; Do I have a copy of this locally?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Expiration Check:&lt;/em&gt; If yes, is it fresh (based on &lt;code&gt;max-age&lt;/code&gt;)?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;The Short Circuit:&lt;/em&gt; If it is fresh, the browser returns the data immediately. It never talks to the server.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This behavior is controlled by the &lt;code&gt;Cache-Control&lt;/code&gt; header.&lt;/p&gt;




&lt;p&gt;Now these 3 concepts will help you understand how Caching works on the Network (HTTP) layer&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Rules: &lt;code&gt;Cache-Control&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This is the most important header in web performance. It tells the browser exactly how to behave.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;max-age&lt;/code&gt; (The Timer)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Cache-Control: max-age=3600
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells the browser that "This data is good for 1 hour (3600 seconds). Do not ask the server for this data again until that time is up."&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The Trap&lt;/strong&gt;: If you deploy a critical bug fix 5 minutes later, users with the cached version won't see it for another 55 minutes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;no-cache&lt;/code&gt; vs &lt;code&gt;no-store&lt;/code&gt; (The Great Confusion)
&lt;/h4&gt;

&lt;p&gt;This is the most common interview question and production mistake regarding caching.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;no-store&lt;/code&gt; says "Never save this."&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Use this for sensitive data (banking info) or data that changes every millisecond.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;no-cache&lt;/code&gt; says "Go ahead and save it, but you must check with the server before using it."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now this forces the browser to ask the server, "Is this version still good?" every single time.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Read more:&lt;/strong&gt; &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control" rel="noopener noreferrer"&gt;MDN Web Docs: Cache-Control&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. The Receipt System: &lt;code&gt;ETags&lt;/code&gt; and &lt;code&gt;Last-modified (304)&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Imagine you have a large list of 5,000 products. You don't want to download that 2MB file every time, but you also need to know if it changed.&lt;/p&gt;

&lt;p&gt;This is where Conditional Requests come in.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;For the First request&lt;/strong&gt;, server sends the data + an &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag" rel="noopener noreferrer"&gt;ETag&lt;/a&gt; (a unique hash/fingerprint of the file).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;For the Second request&lt;/strong&gt;, The browser sends that ETag back in a header called &lt;code&gt;If-None-Match&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;From the Server’s response&lt;/strong&gt;, If the hash is the same, the server sends a 304 Not Modified.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;The Win: You saved the user from downloading 2MB. The browser just reuses the version it already had.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3. The "Stale-While-Revalidate" Strategy
&lt;/h3&gt;

&lt;p&gt;If you learn one thing from this article, let it be this. It is the gold standard for modern web performance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Cache-Control: max-age=60, stale-while-revalidate=600
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;This tells the browser:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the data is less than 60 seconds old, show it instantly (Fresh).&lt;/p&gt;

&lt;p&gt;If it's between 60 seconds and 600 seconds old, show the old data immediately, but in the background, fetch the new data and update the cache for next time (Stale-while-revalidate).&lt;/p&gt;

&lt;p&gt;This eliminates the "loading spinner" entirely while still keeping the data relatively fresh.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Deep Dive:&lt;/strong&gt; &lt;a href="https://web.dev/articles/stale-while-revalidate" rel="noopener noreferrer"&gt;web.dev: Love your cache (stale-while-revalidate)&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Why This Matters for React Developers&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;You might be thinking, &lt;em&gt;"I'm a frontend dev, I don't configure server headers!"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here is the truth, &lt;em&gt;You can't fix with JavaScript what you broke with HTTP.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If your API sends &lt;code&gt;Cache-Control: no-store&lt;/code&gt;, your fancy React Query setup will struggle to maintain a cache effectively because the browser is fighting against it. If your API sends &lt;code&gt;max-age=31536000&lt;/code&gt; (1 year) for a user profile, your users will never see their profile updates.&lt;/p&gt;

&lt;p&gt;You need to check these headers in the &lt;code&gt;Chrome DevTools&lt;/code&gt; Network Tab.&lt;/p&gt;

&lt;h3&gt;
  
  
  The 3 Key Takeaways:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The Browser is Smart; it tries to save you work by storing files locally, so you need to know how to tell it when to stop using those files.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The "Receipt" System: Instead of downloading a whole file, the browser can just ask the server, "Has this changed?" If the server says "No" (304 Not Modified), you save time and data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;User Experience: Using &lt;code&gt;stale-while-revalidate&lt;/code&gt; allows your app to show old data instantly while it fetches new data in the background, eliminating need for loading spinners.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Deep Dive Resources&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If you want to become a true expert on this layer, I highly recommend reading these specifications:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching" rel="noopener noreferrer"&gt;MDN HTTP Caching Guide&lt;/a&gt;:&lt;/strong&gt; The comprehensive manual on how browsers handle storage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://web.dev/articles/http-cache" rel="noopener noreferrer"&gt;Google's Web.dev Guide&lt;/a&gt;:&lt;/strong&gt; A practical guide on configuring headers for performance (and Lighthouse scores).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.cloudflare.com/learning/cdn/what-is-caching/" rel="noopener noreferrer"&gt;Cloudflare's CDN Learning Center&lt;/a&gt;:&lt;/strong&gt; Excellent for understanding how Edge caching (CDNs) interacts with browser caching.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;What’s Next?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Now that we understand the &lt;strong&gt;Invisible Layer&lt;/strong&gt;, we can finally move to the &lt;strong&gt;Application Layer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;Part 3&lt;/strong&gt;, we will be looking at &lt;strong&gt;React Query (TanStack Query)&lt;/strong&gt;, to see how to implement an efficient caching system with it.&lt;/p&gt;

&lt;p&gt;See you in Part 3.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>frontend</category>
      <category>architecture</category>
    </item>
    <item>
      <title>The Frontend Caching Handbook (Part 1)</title>
      <dc:creator>Peter Ogbonna</dc:creator>
      <pubDate>Thu, 12 Feb 2026 15:35:15 +0000</pubDate>
      <link>https://dev.to/peterintech/the-frontend-caching-handbook-part-1-2g30</link>
      <guid>https://dev.to/peterintech/the-frontend-caching-handbook-part-1-2g30</guid>
      <description>&lt;p&gt;I want you to imagine a scenario.&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%2Fty3idv1ezuinvcygv2qj.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%2Fty3idv1ezuinvcygv2qj.jpg" alt="Barsita and coffee" width="256" height="256"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You walk into a coffee shop and order a Latte, the barista makes it, hands it to you and you take a sip, it tastes perfect. You turn around to grab a napkin, turn back to the counter and the latte is gone...&lt;/p&gt;

&lt;p&gt;You ask for it again. The barista sighs, grinds the beans again, steams the milk again, and hands you a new cup.&lt;/p&gt;

&lt;p&gt;This sounds ridiculous, right? Yet this is exactly how many frontend applications behave today.&lt;/p&gt;

&lt;p&gt;We fetch a list of products. The user navigates to a product detail page, then clicks "Back" only to meet a Spinner because the list is being fetched again.&lt;/p&gt;

&lt;p&gt;Welcome to Part 1 of my series on Frontend Caching. Before we dive into caching with tools like &lt;a href="https://tanstack.com/query/v4/docs/framework/react/overview" rel="noopener noreferrer"&gt;Tanstack Query&lt;/a&gt;, &lt;a href="https://redux-toolkit.js.org/tutorials/rtk-query" rel="noopener noreferrer"&gt;RTK Query&lt;/a&gt;, or &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt; (which we will cover in later articles), we need to fix our mindset.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We need to stop optimizing for "easy coding" and start optimizing for "instant experiences".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why Caching is Not Optional
&lt;/h2&gt;

&lt;p&gt;For a long time, caching was seen as a backend concern (Redis, database caching) or a purely "performance optimization" task you do at the end of a project.&lt;/p&gt;

&lt;p&gt;But that's not the case, Caching affects user experience and cost, so should be taken into consideration at the initial stage of building.&lt;/p&gt;

&lt;p&gt;Caching solves three specific problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Latency (The UX Killer):&lt;/strong&gt; Even on 5G, a network request takes time. Reading from memory takes nanoseconds.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Bandwidth (The Cost Killer):&lt;/strong&gt; Why download the same 500KB JSON payload five times in one session? Those extra requests are costing unnecessary extra money&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Server Load (The Scale Killer):&lt;/strong&gt; Your backend engineers will thank you if you stop hammering the API for data that hasn't changed.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Core Concept: Fresh vs Stale Data
&lt;/h2&gt;

&lt;p&gt;To understand caching, you must understand the concept of Staleness.&lt;/p&gt;

&lt;p&gt;In a perfect world, our frontend would always be perfectly synced with the database. But the moment you fetch data to the client, it is technically &lt;code&gt;stale&lt;/code&gt;. Something could have changed on the server the millisecond after your request finished.&lt;/p&gt;

&lt;p&gt;Hence, Caching is the art of accepting staleness for a defined period.&lt;/p&gt;

&lt;p&gt;This is the mental shift, instead of asking: "Is this data perfectly up to date?" You should ask: "Is this data fresh enough for the user right now?"&lt;/p&gt;

&lt;p&gt;If I’m looking at a list of blog posts, does it matter if a new post was added 2 seconds ago? Probably not! I can show the cached list instantly as that data is &lt;code&gt;stale&lt;/code&gt; but acceptable.&lt;/p&gt;

&lt;p&gt;But If I’m looking at my bank account balance? That better be &lt;code&gt;fresh&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Layers of Caching
&lt;/h2&gt;

&lt;p&gt;As frontend engineers, we usually deal with three distinct layers. Understanding where your data lives is half the battle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The Browser Cache (HTTP)&lt;/strong&gt;&lt;br&gt;
This is the invisible layer. Before your React or Vue code even runs, the browser checks if it already has the resource. This is controlled by HTTP headers (Cache-Control, ETag and the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers" rel="noopener noreferrer"&gt;likes&lt;/a&gt;).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Browser Cache is usually automatic if configured correctly on the server, though hard to manipulate manually from JavaScript.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;2. The CDN / Edge Cache&lt;/strong&gt;&lt;br&gt;
This lives between your user and your server. It stores copies of your content closer to the user geographically.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One advantage is fast delivery of static assets, though if you cache dynamic API responses here, you risk showing different user different data which can be a nightmare to debug&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;3. Application Memory (The Client State)&lt;/strong&gt;&lt;br&gt;
This is where we will focus most of this series. This is using &lt;code&gt;React Query&lt;/code&gt;, &lt;code&gt;RTK Query&lt;/code&gt; and the likes, or even a simple object in your code.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Here you have instant access and complete control over invalidation logic. Though it vanishes when the user refreshes the page (unless you persist it).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Hardest Problem in Computer Science - Invalidation
&lt;/h2&gt;

&lt;p&gt;You’ve likely heard the quote by Phil Karlton:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There are only two hard things in Computer Science: cache invalidation and naming things.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Invalidation is knowing when to delete the cache.&lt;/p&gt;

&lt;p&gt;Imagine you cache a &lt;code&gt;User Profile&lt;/code&gt;. The user updates their name on a &lt;code&gt;Settings&lt;/code&gt; page.&lt;/p&gt;

&lt;p&gt;You send the PUT request to update the name.&lt;/p&gt;

&lt;p&gt;The server confirms it's updated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Trap is this:&lt;/strong&gt; You navigate back to the &lt;code&gt;Profile&lt;/code&gt; page, and your cache still holds the old name now the user thinks the app is broken.&lt;/p&gt;

&lt;p&gt;You have to tell the cache: "Hey, the data associated with &lt;code&gt;User Profile&lt;/code&gt; is now dirty. Fetch it again next time."&lt;/p&gt;

&lt;p&gt;Technology has evolved so well that we have tools that aids us build better performant web applications...&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;In this series, we'll be moving from "No idea" to "Master". We aren't just going to learn how to use the tools; we are going to learn why they work the way they do.&lt;/p&gt;

&lt;p&gt;Here is the roadmap:&lt;/p&gt;

&lt;p&gt;Part 2: The Invisible Layer – Mastering HTTP Headers (because you can't fix with JS what you broke with Headers).&lt;/p&gt;

&lt;p&gt;Part 3: The State Manager Revolution – deep dive into Tanstack Query.&lt;/p&gt;

&lt;p&gt;Part 4: The Redux Powerhouse – Implementation with RTK Query.&lt;/p&gt;

&lt;p&gt;Part 5: The Full Stack – Server-side caching in Next.js.&lt;/p&gt;

&lt;p&gt;See you in Part 2.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>performance</category>
      <category>javascript</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Beyond Clean Code: Mastering Parallel Data Fetching with React Router's defer</title>
      <dc:creator>Peter Ogbonna</dc:creator>
      <pubDate>Tue, 23 Dec 2025 16:04:55 +0000</pubDate>
      <link>https://dev.to/peterintech/beyond-clean-code-mastering-parallel-data-fetching-with-react-routers-defer-4d1b</link>
      <guid>https://dev.to/peterintech/beyond-clean-code-mastering-parallel-data-fetching-with-react-routers-defer-4d1b</guid>
      <description>&lt;p&gt;In my &lt;a href="https://dev.to/peterintech/stop-writing-messy-states-why-i-swapped-useeffect-for-react-router-loaders-46mm"&gt;previous post&lt;/a&gt;, I discussed how to get rid of Messy State by moving from &lt;code&gt;useEffect&lt;/code&gt; to &lt;code&gt;React Router Loaders&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We successfully cleaned up our components, but we introduced a new challenge; The "Waiting Game."&lt;/p&gt;

&lt;p&gt;Because standard Loaders fetch data before the route renders, if your API takes 3 seconds to respond, your user is stuck looking at the old page until that loader finishes. To the user, it feels like the app is frozen or the link is broken.&lt;/p&gt;

&lt;p&gt;Today, we solve that with the &lt;a href="https://reactrouter.com/6.30.2/guides/deferred" rel="noopener noreferrer"&gt;Defer Pattern&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Understanding the Logic: What exactly is "defer"?&lt;/h2&gt;

&lt;p&gt;To understand defer, you have to think about how a normal &lt;code&gt;loader&lt;/code&gt; works. Usually, a &lt;code&gt;loader&lt;/code&gt; is like a waiter who won't leave the kitchen until every single dish in your order is cooked. If the steak takes 20 minutes, you don't even get your water until the steak is done.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;defer&lt;/code&gt; changes the rules. It allows the waiter to bring the water and appetizers (Fast Data) immediately, while the steak (Slow Data) is still cooking.&lt;/p&gt;

&lt;h4&gt;The Magic of the Promise&lt;/h4&gt;

&lt;p&gt;When you use &lt;code&gt;defer&lt;/code&gt;, you aren't returning raw data; you are returning a Promise.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The Loader: Tells React Router, "Here is some data I have now, and here is a 'ticket' (the promise) for data that is coming later.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Component: Renders immediately. It doesn't wait for that "ticket" to resolve.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;The Implementation: v6 vs v7&lt;/h2&gt;

&lt;p&gt;For &lt;code&gt;v6&lt;/code&gt;, to make this work, we need a three-part harmony: the &lt;code&gt;defer&lt;/code&gt; utility, the &lt;code&gt;&amp;lt;Await&amp;gt;&lt;/code&gt; component, and React's built-in &lt;code&gt;&amp;lt;Suspense&amp;gt;&lt;/code&gt;.&lt;br&gt;
However, React Router recently evolved, in &lt;code&gt;v7&lt;/code&gt;, we don't need &lt;code&gt;defer&lt;/code&gt; as the router is smart enough to handle promises automatically.&lt;/p&gt;

&lt;h4&gt;Step 1: Edit the Loader&lt;/h4&gt;

&lt;p&gt;Instead of using await for every fetch, we only await the critical data.&lt;/p&gt;

&lt;p&gt;In React Router v6&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;// for v6.4+ we use the "defer"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-router-dom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// CRITICAL DATA: We `await` this. The page won't load until we have them.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productReq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/api/product/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;productReq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// NON-CRITICAL DATA: No `await` here! We pass the promise directly.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reviewsPromise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/api/product/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/reviews`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;defer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;reviews&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;reviewsPromise&lt;/span&gt; 
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In React Router v7&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;// for v7 we use don't use the "defer"&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// CRITICAL DATA: We `await` this. The page won't load until we have them.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productReq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/api/product/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;productReq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// NON-CRITICAL DATA: No `await` here! We pass the promise directly.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reviewsPromise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/api/product/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/reviews`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;reviews&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;reviewsPromise&lt;/span&gt; 
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;Step 2: The UI (Unwrapping the Promise)&lt;/h4&gt;

&lt;p&gt;Regardless of which version you use, the UI implementation is the same.&lt;/p&gt;

&lt;p&gt;Since &lt;code&gt;reviews&lt;/code&gt; is a Promise, we can't just &lt;code&gt;.map()&lt;/code&gt; over it instantly. We use the &lt;code&gt;&amp;lt;Await&amp;gt;&lt;/code&gt; component to "unwrap" it once it arrives.&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useLoaderData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Await&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-router-dom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Suspense&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProductPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reviews&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useLoaderData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&amp;gt; {/&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;Renders&lt;/span&gt; &lt;span class="nx"&gt;instantly&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Suspense&lt;/span&gt; &lt;span class="nx"&gt;fallback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Loading&lt;/span&gt; &lt;span class="nx"&gt;reviews&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&amp;gt;}&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Await&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;reviews&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;errorElement&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;Error&lt;/span&gt; &lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="nx"&gt;reviews&lt;/span&gt;&lt;span class="o"&gt;!&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&amp;gt;}&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;resolvedReviews&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ul&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;resolvedReviews&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/li&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ul&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="p"&gt;)}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Await&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Suspense&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;Why this is a UX Game Changer&lt;/h2&gt;

&lt;h4&gt;1. Faster Perceived Performance&lt;/h4&gt;

&lt;p&gt;The user sees the "shell" of the page and the primary content almost instantly. They can start reading the product description while the reviews are still "streaming" in over the wire.&lt;/p&gt;

&lt;h4&gt;2. No more "All-or-Nothing" UI&lt;/h4&gt;

&lt;p&gt;In the old &lt;code&gt;useEffect&lt;/code&gt; days, if one API call failed, you often ended up with a giant error screen. With &lt;code&gt;&amp;lt;Await&amp;gt;&lt;/code&gt;, you get an &lt;code&gt;errorElement&lt;/code&gt; property. If the reviews fail, only that section shows an error. The rest of your page stays functional.&lt;/p&gt;

&lt;h4&gt;3. Better than "Global" Loading Spinners&lt;/h4&gt;

&lt;p&gt;Instead of a giant spinner that covers the whole screen, you can use Skeleton Screens in your fallback to reserve space, preventing that annoying "layout shift" when data pops in.&lt;/p&gt;

&lt;h2&gt;When to defer and when not to&lt;/h2&gt;

&lt;p&gt;Since the focus is user experience, wrong use can act against the goal&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Scenario&lt;/th&gt;
      &lt;th&gt;Use Standard await&lt;/th&gt;
      &lt;th&gt;Use defer&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Crucial Data (SEO/Meta tags)&lt;/td&gt;
      &lt;td&gt;✅ Yes&lt;/td&gt;
      &lt;td&gt;❌ No&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Fast APIs (&amp;lt; 200ms)&lt;/td&gt;
      &lt;td&gt;✅ Yes&lt;/td&gt;
      &lt;td&gt;❌ No&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Slow Analytics/Logs&lt;/td&gt;
      &lt;td&gt;❌ No&lt;/td&gt;
      &lt;td&gt;✅ Yes&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Secondary Page Content&lt;/td&gt;
      &lt;td&gt;❌ No&lt;/td&gt;
      &lt;td&gt;✅ Yes&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Introducing &lt;code&gt;Loaders&lt;/code&gt; cleaned up our code in &lt;a href="https://dev.to/peterintech/stop-writing-messy-states-why-i-swapped-useeffect-for-react-router-loaders-46mm"&gt;Part 1&lt;/a&gt;, but &lt;code&gt;defer&lt;/code&gt; makes our apps feel like professional-grade software. By strategically choosing what data to wait for and what data to stream, you give your users a snappier, more reliable experience.&lt;/p&gt;

&lt;p&gt;What’s your favorite way to improve user experience? Let me know in the comments!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>react</category>
      <category>ux</category>
    </item>
    <item>
      <title>Stop Writing Messy States: Why I Swapped useEffect for React Router Loaders</title>
      <dc:creator>Peter Ogbonna</dc:creator>
      <pubDate>Thu, 18 Dec 2025 14:33:49 +0000</pubDate>
      <link>https://dev.to/peterintech/stop-writing-messy-states-why-i-swapped-useeffect-for-react-router-loaders-46mm</link>
      <guid>https://dev.to/peterintech/stop-writing-messy-states-why-i-swapped-useeffect-for-react-router-loaders-46mm</guid>
      <description>&lt;p&gt;We have all been there... You create a new component, and the first thing you do is set up the trinity of data fetching state:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;loading&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;error&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;data&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then, you write the inevitable &lt;code&gt;useEffect&lt;/code&gt; hook. It usually looks something like 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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Network response was not ok&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nf"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Spinner&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ErrorMessage&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Boilerplate&lt;/strong&gt;: We just wrote 20 lines of code just to get data from A to B.&lt;/li&gt;
&lt;/ul&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Render-then-Fetch&lt;/strong&gt;: The component has to mount (render) first before the effect kicks in to start fetching. This causes a &lt;a href="https://remix.run/blog/remixing-react-router" rel="noopener noreferrer"&gt;waterfall effect&lt;/a&gt; and slower perceived performance.&lt;/li&gt;
&lt;/ul&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;State Management Hell&lt;/strong&gt;: You are manually managing loading and error states in every single component that needs data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;The Solution: React Router Loaders introduced in v6&lt;/h2&gt;

&lt;p&gt;React Router v6.4+ introduced a paradigm shift: &lt;strong&gt;Loaders&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Loaders&lt;/strong&gt; are a part of the &lt;a href="https://reactrouter.com/6.30.2/routers/picking-a-router#data-apis" rel="noopener noreferrer"&gt;Data APIs&lt;/a&gt; which lets you handle data fetching, actions/mutations and error handling directly in your route definitions&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The idea is simple: &lt;strong&gt;Fetch-then-Render&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of the component asking for data after it mounts, the Router fetches the data before it renders the component. If the data isn't ready, the component doesn't load. If there is an error, the Router handles it elsewhere.&lt;/p&gt;

&lt;p&gt;Here is how to clean up your code in 3 steps:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It's assumed you're working on a react application with react-router installed&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;Step 1: Setup Router that supports Data APIs&lt;/h3&gt;

&lt;p&gt;This will involve reconfiguring the router setup see &lt;a href="https://reactrouter.com/6.30.2/routers/picking-a-router" rel="noopener noreferrer"&gt;Picking a Router&lt;/a&gt;&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;//App.jsx&lt;/span&gt;

&lt;span class="c1"&gt;// From this&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;BrowserRouter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Routes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-router&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;UserProfile&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;BrowserRouter&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Routes&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserProfile&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Routes&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/BrowserRouter&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//To this&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;RouterProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createBrowserRouter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-router&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;UserProfile&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createBrowserRouter&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserProfile&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;RouterProvider&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;Step 2: Write a loader function&lt;/h3&gt;

&lt;p&gt;You define a function that fetches your data.&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;// utils.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userLoader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.example.com/user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Could not fetch user details&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;Step 3: Connect it to the Route&lt;/h3&gt;

&lt;p&gt;In your Router configuration (using &lt;code&gt;createBrowserRouter&lt;/code&gt;), you plug the loader directly into the route.&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;// App.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createBrowserRouter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;RouterProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-router-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userLoader&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./utils&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;UserProfile&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ErrorPage&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./errorPage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createBrowserRouter&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserProfile&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userLoader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;// &amp;lt;--- The magic happens here&lt;/span&gt;
    &lt;span class="na"&gt;errorElement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ErrorPage&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;--- Handles errors automatically!&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;RouterProvider&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;Step 3: Consume the Data&lt;/h3&gt;

&lt;p&gt;Now, look at how clean your component becomes. No &lt;code&gt;useEffect&lt;/code&gt;, No &lt;code&gt;useState&lt;/code&gt; for loading or errors. Just one hook: &lt;code&gt;useLoaderData&lt;/code&gt;&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;// user.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useLoaderData&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useLoaderData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Data is already here!&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;In the ErrorPage, you can get error message and handle error by grabbing the &lt;code&gt;useRouteError&lt;/code&gt; hook from "react-router", implementation is similar to &lt;code&gt;useLoaderData&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;Why this is Clean Code?&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Separation of Concerns:&lt;/strong&gt; Your UI component is responsible for UI, not fetching logic. The fetching logic lives in the loader.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Automatic Error Handling:&lt;/strong&gt; If the API fails, React Router catches the error and renders the &lt;code&gt;errorElement&lt;/code&gt; defined in the route. Hence, your component doesn't need to check if (error).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Performance:&lt;/strong&gt; The browser starts fetching the data as soon as the user clicks the link, in parallel with loading the code for the new page.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;useEffect&lt;/code&gt; is a powerful tool but using it for data fetching in 2025 is often unnecessary complexity. By switching to Loaders, you delete code, prevent bugs, and make your application feel snappier.&lt;/p&gt;

&lt;p&gt;Give your &lt;code&gt;useEffect&lt;/code&gt; a break and let the Router do the heavy lifting.&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>cleancode</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Understanding APIs and Streamlining Development with Postman</title>
      <dc:creator>Peter Ogbonna</dc:creator>
      <pubDate>Fri, 08 Dec 2023 12:33:47 +0000</pubDate>
      <link>https://dev.to/peterintech/understanding-apis-and-streamlining-development-with-postman-384a</link>
      <guid>https://dev.to/peterintech/understanding-apis-and-streamlining-development-with-postman-384a</guid>
      <description>&lt;p&gt;In the digital age, APIs (Application Programming Interfaces) serve as the backbone of modern software development, enabling seamless communication between different applications, services, and systems. They facilitate the exchange of data and functionalities, allowing developers to create innovative solutions from an already existing structure.&lt;br&gt;
In this blog post, we are going to look at the foundational concepts of APIs, their importance in development and how &lt;a href="https://www.postman.com/" rel="noopener noreferrer"&gt;postman&lt;/a&gt; came in to revolutionize api testing and streamlined development experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is an API?
&lt;/h2&gt;

&lt;p&gt;As the name implies, Application Programming Interface defines the rules and protocols that enable different software applications to communicate with each other. It acts as an intermediary, allowing one piece of software to access and interact with another.&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%2F10kiowfjskeq9m78j3h9.png" 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%2F10kiowfjskeq9m78j3h9.png" alt=" " width="768" height="307"&gt;&lt;/a&gt;&lt;br&gt;
A simple analogy of how apis work is just like a traditional restaurant where you step in and you place your order through the waiter and the waiter gets your order to the kitchen and returns with your order. In this analogy, the waiter is the api, the customer is the client/user and the Kitchen is the server. In other words, apis basically act as an intermediary to allow one piece of software to access and interact with another, they make this possible without revealing the codebase or database of the server, &lt;em&gt;you get what your ask for&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;APIs have a versatile area of applications not limited to softwares alone, we also have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hardware APIs&lt;/strong&gt;: These are software that allow you to interact with hardware components, for example, a printer API lets your software communicate with the printer to produce physical copies&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Software Library APIs&lt;/strong&gt;: These provide pre-built functions and routines that developers can use to perform specific tasks, they embed complex functionalities, this helps to save a bunch of time!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Web APIs:&lt;/strong&gt; This is perhaps the most common type, they enable communication between different web services. They allow you to fetch data from a remote server, like a restaurant offering takeout service from its menu. This will be the focus for today.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;APIs come in various types, including &lt;em&gt;RESTful APIs, SOAP APIs, and GraphQL&lt;/em&gt;, each with its own specifications and purposes. The mostly used is the RESTful API&lt;/p&gt;

&lt;h3&gt;
  
  
  The Importance of APIs in Development
&lt;/h3&gt;

&lt;p&gt;The significance of APIs in modern development cannot be overstated. They offer several key benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Integration:&lt;/strong&gt; APIs allow disparate systems to seamlessly work together, fostering interoperability and enabling the creation of integrated solutions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Efficiency:&lt;/strong&gt; Developers can leverage existing APIs to access functionalities or data, reducing development time and effort.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scalability:&lt;/strong&gt; APIs provide a scalable foundation for applications, allowing them to adapt and grow as requirements change.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Innovation:&lt;/strong&gt; APIs empower developers to build upon existing services and functionalities, encouraging innovation and the creation of new products.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Anatomy of an API call
&lt;/h3&gt;

&lt;p&gt;Understanding an API call involves grasping several key components and considerations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Endpoint:&lt;/strong&gt; The URL that specifies the location and resource being accessed or manipulated. It determines where the API request is sent. Each api has a unique endpoint&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;HTTP Method (HTTP Verb):&lt;/strong&gt; - Indicates the type of action being performed on the resource (GET, POST, PUT, DELETE, etc.).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Headers:&lt;/strong&gt; Additional information sent with the request, including authentication tokens, content types, or metadata.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Parameters:&lt;/strong&gt; Query parameters or request body data (in the case of POST, PUT, PATCH) sent along with the request to provide additional information or modify the request. To learn more, visit: &lt;a href="https://www.restapitutorial.com/lessons/httpmethods.html" rel="noopener noreferrer"&gt;HTTP verbs&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Authentication:&lt;/strong&gt; If required, methods for authenticating the requester's identity, such as API keys, OAuth tokens, or other authentication mechanisms.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt; The data returned by the API call, typically in JSON or XML format. It includes the requested information or an acknowledgment of the action performed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Status Codes:&lt;/strong&gt; Indicate the success or failure of the request, each code has a unique message, be it success or failure (e.g., 200 for success, 404 for not found, 500 for server errors). To learn more about status code, visit: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status" rel="noopener noreferrer"&gt;Status Code MDN&lt;/a&gt;&lt;br&gt;
.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Introducing Postman
&lt;/h2&gt;

&lt;p&gt;Amidst the varieties of tools available for API development and testing, Postman stands out as a powerful and user-friendly option. It offers a comprehensive suite of features that streamline the API development lifecycle.&lt;/p&gt;

&lt;h4&gt;
  
  
  Key Benefits of Postman:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;User-Friendly Interface:&lt;/strong&gt; Postman's intuitive interface simplifies the process of designing, testing, and debugging APIs, making it accessible for both beginners and seasoned developers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Collaboration:&lt;/strong&gt; It facilitates team collaboration by allowing seamless sharing of API collections, environments, and tests, enabling efficient teamwork and knowledge sharing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Automated Testing:&lt;/strong&gt; Postman enables the creation of automated tests, ensuring the reliability and consistency of APIs across different environments.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Documentation:&lt;/strong&gt; With its documentation capabilities, Postman assists in generating comprehensive API documentation, simplifying the process of understanding and consuming APIs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mock Servers:&lt;/strong&gt; Developers can create mock servers to simulate API endpoints, allowing for testing without depending on the actual backend implementation.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;APIs serve as the backbone of interconnected digital ecosystems, enabling the creation of robust and innovative applications. Among the tools available for API development and testing, Postman emerges as a versatile solution, empowering developers to streamline their workflow, enhance collaboration, and build reliable APIs efficiently.&lt;/p&gt;

&lt;p&gt;In conclusion, understanding the importance of APIs and harnessing the capabilities of tools like &lt;a href="https://www.postman.com/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt; can significantly contribute to the success and efficiency of software development endeavors in today's interconnected world.&lt;/p&gt;

</description>
      <category>postman</category>
      <category>postmanunleased</category>
      <category>postmanstudentexpert</category>
      <category>api</category>
    </item>
    <item>
      <title>CRUD Todo App - React and Redux-toolkit</title>
      <dc:creator>Peter Ogbonna</dc:creator>
      <pubDate>Tue, 18 Apr 2023 02:25:09 +0000</pubDate>
      <link>https://dev.to/peterintech/crud-todo-app-react-and-redux-toolkit-2708</link>
      <guid>https://dev.to/peterintech/crud-todo-app-react-and-redux-toolkit-2708</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;We’ll make a simple Todo-list app that performs all four CRUD (Create, Read, Update, Delete) operations- on the front-end, using React and Redux-Toolkit: &lt;a href="https://todo-beige-tau.vercel.app/" rel="noopener noreferrer"&gt;live link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are familiar with Redux you can jump straight to the Implementation &lt;/p&gt;

&lt;p&gt;What to get in this article:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why Redux?&lt;/li&gt;
&lt;li&gt;Redux vs Redux-Toolkit&lt;/li&gt;
&lt;li&gt;How Redux Works&lt;/li&gt;
&lt;li&gt;Implementation&lt;/li&gt;
&lt;li&gt;
Helpful Links &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Redux?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://redux.js.org" rel="noopener noreferrer"&gt;Redux&lt;/a&gt; is a state management library, that helps centralize your application’s state and logic. It comes with a DevTool which makes it easy to trace when, where, why and how your application’s state changed.&lt;br&gt;
It works with any UI layer and popularly used with Reactjs.&lt;br&gt;
And you might be wondering, what’s the difference between Redux and Redux-Toolkit, let’s take a look at that.&lt;/p&gt;
&lt;h2&gt;
  
  
  Redux vs ReduxToolkit
&lt;/h2&gt;

&lt;p&gt;Redux is the core library for managing application state, while Redux-Toolkit is a companion library that provides additional utilities for working with Redux. You can use Redux on its own, but many developers find that Redux-Toolkit makes it even easier and more efficient to work with Redux even the Redux team recommends Redux-Toolkit as the approach for writing Redux logic, because it is a newer library that provides a set of utilities for working with Redux. It provides a simpler API for common tasks, as well as performance optimizations that can help to improve the performance of your application for example, the library uses Immer under the hood to provide immutable updates to the store, which can help to avoid unnecessary re-renders. Redux-Toolkit comes with functions for creating and managing Redux stores, as well as middleware for handling asynchronous actions, this can help to make your code more organized and maintainable. &lt;br&gt;
Simply put, Redux-Toolkit is designed to make it easier and more efficient to write Redux applications&lt;/p&gt;
&lt;h2&gt;
  
  
  How Redux works
&lt;/h2&gt;

&lt;p&gt;In Redux, you basically create an action and dispatch the action using the usedispatch hook.&lt;br&gt;
Redux provides a predictable state container that helps manage the state of your application in a centralized manner, a store. A “store” is a container that retains your application’s global state, each component can access the store without having to send down props from one component to another. &lt;/p&gt;
&lt;h3&gt;
  
  
  The five major terminologies in Redux:
&lt;/h3&gt;

&lt;p&gt;In major Redux applications you’ll find these major components working hand in hand. I will give brief explanation of each of these terminologies, it might not be clear enough, but follow through, while building our app it will become clearer. We’ll briefly discuss the main terminologies in a typical Redux app&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;b&gt;Store:&lt;/b&gt; A single source of truth that holds the entire state tree of your application.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;b&gt;Actions:&lt;/b&gt; Actions are the medium used to send data from your application to your Redux store, they are plain JavaScript objects that represent an intention to change the state, they contain a type property that describes the action as well as a payload (an additional data necessary to carryout the action) to update the state. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;b&gt;Reducers:&lt;/b&gt; Reducers are Pure functions that take the current state and an action and return a new state. They should not modify the current state directly, but instead return a new state object.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p id="dispatch"&gt;&lt;b&gt;Dispatch:&lt;/b&gt; To update the state, an action must be dispatched to the store using the `dispatch` function. This triggers the store to call the corresponding reducer function with the current state and the dispatched action. Just think of the `dispatch` function as `setState`&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p id="selector"&gt;&lt;b&gt;Selector:&lt;/b&gt; To access data from the store. We do this by using the `useSelector` hook from react-redux &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Just think of Dispatch and Selector as&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Grey&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Dispatch (update the state)&lt;/span&gt;
&lt;span class="nf"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Dan&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Selector (get the state)&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;p&gt;To get started, you only need two npm packages&lt;br&gt;
&lt;code&gt;npm install @reduxjs/toolkit react-redux&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Create a Redux Store
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;src/redux/store.js&lt;/code&gt;&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;configureStore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@reduxjs/toolkit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;configureStore&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="na"&gt;reducer&lt;/span&gt;&lt;span class="p"&gt;:{},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add the Redux Store to Index.js parent file
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;src/index.js&lt;/code&gt;&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-redux&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./redux/store&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Provider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When done importing these necessary components, ensure to wrap your  component with the provider and include the store you added recently, this enables your whole app to access the Redux store&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a Slice
&lt;/h3&gt;

&lt;p&gt;Of course, Redux- Toolkit introduced slice. A slice is a part of the application’s state that can be updated independently. It contains a set of actions and a reducer that handles those actions. Think of a slice as a certain feature you want to apply different action to it, on different occasions, now each action in the feature (slice) uses the same state, having initial value of  &lt;code&gt;initialState&lt;/code&gt;. In this app, I have 3 actions running in the same slice. All the actions reside in the reducers object&lt;/p&gt;

&lt;p&gt;&lt;code&gt;src/redux/reducer.js&lt;/code&gt;&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createSlice&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@reduxjs/toolkit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;todoReducer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSlice&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="na"&gt;reducers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reducer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;todoReducer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reducer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this, we can go ahead to add our slice Reducers to the store in &lt;code&gt;src/redux/store.js&lt;/code&gt;&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;configureStore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@reduxjs/toolkit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;reducer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./reducer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;configureStore&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="na"&gt;reducer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;reducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the basic configuration needed using Redux-toolkit, we are ready to go Redux baby! &lt;br&gt;
Let’s go about creating actions in &lt;code&gt;src/redux/reducer.js&lt;/code&gt;&lt;/p&gt;


&lt;h4&gt;
  
  
  To Add Note
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createSlice&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@reduxjs/toolkit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;todoReducer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSlice&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="na"&gt;reducers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="c1"&gt;// Adding new todo&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="na"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;:&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="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;return&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="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;todoReducer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reducer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;todoReducer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reducer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;state&lt;/code&gt; is the current state, with initial value of &lt;code&gt;initialState&lt;/code&gt;, action.payload is the value passed into the addTodo function in &lt;code&gt;src/components/TextField/TextField.jsx&lt;/code&gt;&lt;br&gt;
We use our actions to work with the store with the help of the useDispatch hook from react-redux, this is how we &lt;strong&gt;Dispatch&lt;/strong&gt; as explained earlier&lt;/p&gt;

&lt;p&gt;&lt;code&gt;src/components/TextField/TextField.jsx&lt;/code&gt;&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;addTodo&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../redux/reducer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useDispatch&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-redux&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dispatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useDispatch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;addTodo&lt;/code&gt; action takes in the value passed in, which is an object of two key: value pairs and adds it to the array, hence updating the state. Therefore making the state an array of objects with two keys: id and text.&lt;/p&gt;




&lt;h4&gt;
  
  
  To Delete Note:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createSlice&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@reduxjs/toolkit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;todoReducer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSlice&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="na"&gt;reducers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="c1"&gt;// Adding new todo&lt;/span&gt;
&lt;span class="na"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;:&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="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;return&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="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
 &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="na"&gt;deleteTodo&lt;/span&gt;&lt;span class="p"&gt;:&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="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;deleteTodo&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;todoReducer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reducer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;todoReducer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reducer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;deleteTodo&lt;/code&gt; function checks for a matching id to the one passed in through our dispatch function, to update the state, while removing the matched id from the array&lt;br&gt;
Here, the &lt;code&gt;action.payload&lt;/code&gt; is &lt;code&gt;id&lt;/code&gt;&lt;br&gt;
from &lt;code&gt;src/components/Note/Note.jsx&lt;/code&gt;&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;deleteTodo&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../redux/reducer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useDispatch&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-redux&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dispatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useDispatch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;deleteTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h4&gt;
  
  
  Update Note:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;updateTodo&lt;/span&gt;&lt;span class="p"&gt;:&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="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;updateTodo&lt;/code&gt; function updates the value of a selected item from the array of objects(different list items), then updates it’s &lt;code&gt;item&lt;/code&gt; value by the text.&lt;/p&gt;

&lt;p&gt;This function takes an object of two key value pairs as seen in &lt;code&gt;src/components/Note/Note.jsx&lt;/code&gt;&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;updateTodo&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../redux/reducer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;updateTodo&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;inputRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;id&lt;/code&gt; is a numeric value used to identify an item from the list, the text is a string used to update the text of the identified item.&lt;/p&gt;

&lt;h2&gt;
  
  
  Access the state
&lt;/h2&gt;

&lt;p&gt;This is where &lt;strong&gt;selector&lt;/strong&gt; comes in, as explained earlier we use it by using the &lt;code&gt;useSelector&lt;/code&gt; hook from &lt;code&gt;react-redux&lt;/code&gt;. It helps us extract data from the Redux store state, using a selector function&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useSelector&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-redux&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Note&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../Note/Note&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ShowTodo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSelector&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="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Note&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;))}&lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;ShowTodo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that is how we access data from our store.&lt;br&gt;
Hope you got value!&lt;/p&gt;

&lt;h2&gt;
  
  
  Helpful Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/iampitrus/todo" rel="noopener noreferrer"&gt;Source Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://todo-beige-tau.vercel.app/" rel="noopener noreferrer"&gt;Live Preview&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>redux</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
