<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Karan Singh Bagga on Medium]]></title>
        <description><![CDATA[Stories by Karan Singh Bagga on Medium]]></description>
        <link>https://medium.com/@karan51ngh?source=rss-88cc14533a52------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*iUwDQxGp_5Pr_5Y8aK21uA.png</url>
            <title>Stories by Karan Singh Bagga on Medium</title>
            <link>https://medium.com/@karan51ngh?source=rss-88cc14533a52------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 18:17:33 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@karan51ngh/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[HTTP Just Got a New Method: Why QUERY Changes API Design]]></title>
            <link>https://karan51ngh.medium.com/http-query-method-847280103d35?source=rss-88cc14533a52------2</link>
            <guid isPermaLink="false">https://medium.com/p/847280103d35</guid>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[architecture]]></category>
            <category><![CDATA[system-design-interview]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[software-architecture]]></category>
            <dc:creator><![CDATA[Karan Singh Bagga]]></dc:creator>
            <pubDate>Fri, 10 Jul 2026 07:29:04 GMT</pubDate>
            <atom:updated>2026-07-10T07:29:04.256Z</atom:updated>
            <content:encoded><![CDATA[<p><em>One of API design’s most familiar workarounds may finally have an expiration date.</em></p><p>For years, API designers have quietly made the <strong>same compromise</strong>.</p><p>They start with a clean GET request:</p><pre>GET /products?category=laptop&amp;brand=acme HTTP/1.1<br>Host: api.example.com<br>Accept: application/json</pre><p>Then the product team asks for price ranges, multiple brands, availability rules, nested attributes, full-text search, sorting, pagination, and user-specific constraints.</p><p>The URL begins to look less like an address and more like a programming language:</p><pre>/products<br>└── filters<br>    ├── category = laptop<br>    ├── brands = [acme, contoso, globex]<br>    ├── price<br>    │   ├── minimum = 800<br>    │   └── maximum = 1800<br>    ├── specifications<br>    │   ├── memory &gt;= 32 GB<br>    │   └── storage = SSD<br>    └── sort = rating desc, price asc</pre><p>So the team moves the filters into JSON and sends them with POST:</p><pre>POST /products/search HTTP/1.1<br>Host: api.example.com<br>Content-Type: application/json<br>Accept: application/json<br><br>{<br>  &quot;brands&quot;: [&quot;acme&quot;, &quot;contoso&quot;, &quot;globex&quot;],<br>  &quot;price&quot;: { &quot;min&quot;: 800, &quot;max&quot;: 1800 },<br>  &quot;specifications&quot;: {<br>    &quot;memoryGb&quot;: { &quot;gte&quot;: 32 },<br>    &quot;storageType&quot;: &quot;ssd&quot;<br>  },<br>  &quot;sort&quot;: [&quot;-rating&quot;, &quot;price&quot;]<br>}</pre><p>The endpoint works. The JSON is readable. The server returns products. <strong>But the method is now vague:</strong> without endpoint-specific knowledge, POST could mean a safe search, a payment, a message submission, or some other state-changing command.</p><p>In June 2026, <a href="https://www.rfc-editor.org/rfc/rfc10008.html">RFC 10008</a> <strong>gave that operation its own method:</strong></p><pre>QUERY /products HTTP/1.1<br>Host: api.example.com<br>Content-Type: application/json<br>Accept: application/json<br><br>{<br>  &quot;brands&quot;: [&quot;acme&quot;, &quot;contoso&quot;, &quot;globex&quot;],<br>  &quot;price&quot;: { &quot;min&quot;: 800, &quot;max&quot;: 1800 },<br>  &quot;sort&quot;: [&quot;-rating&quot;, &quot;price&quot;]<br>}</pre><blockquote><em>QUERY gives complex reads request content without giving up explicit read-only, retry, and caching semantics.</em></blockquote><h3>HTTP Methods Are Tiny Words With Large Contracts</h3><p>GET, POST, PUT, and DELETE are method tokens with standardized meanings. Those meanings let clients, servers, caches, proxies, gateways, and monitoring systems reason about a request before they understand the application behind it.</p><p><strong>The method is the first line of the contract:</strong></p><pre>HTTP request<br>├── method: What kind of operation is this?<br>├── target: Which resource is in scope?<br>├── fields: What metadata applies?<br>└── content: What representation accompanies the request?</pre><p>Three properties matter to the <strong>QUERY</strong> story:</p><figure><img alt="A table clarifying three HTTP properties: safe means the client does not request a state change, idempotent means repeating a request has the same intended effect as sending it once, and cacheable means a response may be stored and reused under HTTP caching rules. These properties do not imply that the server performs no auxiliary work, every response is identical, or every CDN caches the response automatically." src="https://cdn-images-1.medium.com/max/1024/1*017CcH9chj-_HfSFG6C3CQ.png" /></figure><p><em>Idempotent does not mean identical responses.</em> GET /products/42 can return $900 today and $850 tomorrow; <strong>GET</strong> remains idempotent because repeated reads request no additional state change. <strong>PUT</strong> and <strong>DELETE</strong> are also idempotent despite changing state. <strong>POST</strong> is not idempotent by definition because replaying it may repeat an action. <strong>PATCH</strong> may be idempotent in a specific use, but HTTP does not guarantee it.</p><h3>The Gap Between GET and POST</h3><p><strong>GET</strong> is an excellent fit when query input fits naturally in the target URI:</p><pre>GET /feed?q=http&amp;limit=10&amp;sort=-published HTTP/1.1<br>Host: example.org</pre><p>It clearly communicates a safe, idempotent retrieval. Its responses work naturally with browsers, shared caches, CDNs, bookmarks, links, and observability tools.</p><p>Complex queries expose four practical limits:</p><ul><li>Clients, proxies, gateways, and servers impose different limits on request targets. <strong><em>RFC 9110</em></strong><em> recommends support for at least 8,000 octets, but your path is only as tolerant as its most restrictive hop.</em></li><li>Nested filters, arrays, ranges, and query languages become awkward once they are flattened via encoding into a query string.</li><li>Request URIs often appear in logs, browser history, bookmarks, referrers, and monitoring tools. Request content can also be logged, but it is less widely exposed by default.</li><li>With <strong>GET</strong>, every input combination becomes a distinct URI. That is useful for small filters and clumsy for large query documents.</li></ul><p><strong>POST</strong> avoids those limits because it expects request content. <em>It also loses the generic signal that the operation is a safe, idempotent query.</em></p><p><strong>That is the gap QUERY fills.</strong></p><figure><img alt="A decision flowchart for choosing an HTTP method: use a state-changing method when the operation changes state; otherwise use GET when the input fits naturally in the URI, QUERY with request content when the full request path supports it, or a documented POST search as a compatibility fallback." src="https://cdn-images-1.medium.com/max/1024/1*jsslRvzAxTDh72_jCzFvSg.png" /></figure><h3>What QUERY Actually Means</h3><p><a href="https://www.rfc-editor.org/rfc/rfc10008.html#section-2">RFC 10008</a> defines <strong>QUERY</strong> as a method that asks the target resource to perform a server-side query within that resource’s scope. The request’s content and Content-Type define the query. The target URI defines the resource being queried.</p><pre>QUERY /contacts HTTP/1.1<br>Host: example.org<br>Content-Type: application/jsonpath<br>Accept: application/json<br><br>$[?(@.country == &quot;India&quot; &amp;&amp; @.status == &quot;active&quot;)]</pre><p>Here, /contacts establishes the scope. application/jsonpath tells the server how to interpret the content. The JSONPath expression describes the query.</p><p>A server <strong>must reject</strong> a <strong>QUERY</strong> request when Content-Type is missing or inconsistent with the content. <em>That requirement is not decorative.</em> The same bytes could have completely different meanings under application/json, application/sql, or application/jsonpath.</p><h3>QUERY is not simply “GET with a body”</h3><p>That phrase is a useful first approximation, but it <strong>hides the semantic difference</strong>.</p><ul><li><strong>GET</strong> asks for a current representation of the resource identified by the target URI.</li><li><strong>QUERY</strong> asks the target resource to perform the operation described by the request content.</li></ul><p>HTTP assigns <em>no generally defined semantics</em> to content in a GET request. Some implementations reject it, some ignore it, and intermediaries cannot safely assume what it means.</p><figure><img alt="A comparison of GET, QUERY, and POST. GET and QUERY are safe and idempotent by definition, and their responses can satisfy later requests using the same method; POST has none of those guarantees. GET suits ordinary retrieval and compact URI filters, QUERY suits complex read-only queries carried in request content, and POST suits submissions and application-defined processing." src="https://cdn-images-1.medium.com/max/1024/1*gYVxoYhTxSRbeFLQ_Bmx0A.png" /></figure><p><strong>POST</strong> responses can be cached under specific conditions, but a stored response generally cannot satisfy a later <strong>POST</strong>. By design, a cached <strong>QUERY</strong> response can satisfy an equivalent later <strong>QUERY</strong>.</p><h3>The Most Important Part: Caching the Content</h3><p>A cache normally starts with the method and target URI when matching requests. <strong>That is insufficient for QUERY.</strong></p><p>These two requests target the same URI but ask different questions:</p><pre>QUERY /products HTTP/1.1<br>Content-Type: application/json<br><br>{&quot;brand&quot;:&quot;acme&quot;}</pre><pre>QUERY /products HTTP/1.1<br>Content-Type: application/json<br><br>{&quot;brand&quot;:&quot;globex&quot;}</pre><p>If a cache considered only /products, it could return Acme results to a client asking for Globex. RFC 10008 therefore requires a QUERY cache key to incorporate the request content and related metadata.</p><figure><img alt="A flowchart of QUERY request caching: the cache combines the method and target URI, complete request content, and relevant metadata into a cache key. It returns a fresh matching response when available; otherwise it forwards the request to the origin, stores an eligible response, and returns the result." src="https://cdn-images-1.medium.com/max/1024/1*gRHMvCZ4F2fS8lOEi3YSbg.png" /></figure><p>A cache may normalize semantically insignificant differences before generating the key. For example, these JSON documents might mean the same thing:</p><pre>{&quot;brand&quot;:&quot;acme&quot;,&quot;inStock&quot;:true}<br><br>{<br>  &quot;inStock&quot;: true,<br>  &quot;brand&quot;: &quot;acme&quot;<br>}</pre><p>But normalization is dangerous when the cache and application disagree about meaning. <strong>A false match can return the wrong data.</strong> The safest initial implementation is often <em>a deterministic query format plus a hash of the exact content and relevant metadata</em>.</p><p>This is why “<strong>QUERY</strong> is cacheable” does not mean “every CDN supports it today.” A compliant QUERY cache must read the full request content and include it in matching.</p><h3>A Query Can Become a GET-able Resource</h3><p>RFC 10008 offers another useful pattern. A server can assign a URI to the query itself, to the result that was just produced, or to both.</p><pre>HTTP/1.1 200 OK<br>Content-Type: application/json<br>Location: /products/stored-queries/42<br>Content-Location: /products/stored-results/17<br>ETag: &quot;result-17&quot;<br><br>[<br>  { &quot;id&quot;: 7, &quot;brand&quot;: &quot;acme&quot;, &quot;price&quot;: 1299 },<br>  { &quot;id&quot;: 9, &quot;brand&quot;: &quot;contoso&quot;, &quot;price&quot;: 1499 }<br>]</pre><p>The two response fields serve different purposes:</p><figure><img alt="A table distinguishing Location from Content-Location in a QUERY response. Location identifies an equivalent resource for the query and its target, so a later GET retrieves the query’s current result; Content-Location identifies the result just returned, so a later GET retrieves that specific result representation." src="https://cdn-images-1.medium.com/max/1024/1*OSM6yhQIMAZRES18J34eHA.png" /></figure><figure><img alt="A sequence diagram showing a client sending a filtered QUERY request to an API and receiving a 200 response with Location and Content-Location. A later GET to the stored query resource returns the query’s current result, while a GET to the stored result resource returns the representation produced earlier." src="https://cdn-images-1.medium.com/max/1024/1*5vZgU3KRLVbnBBXDpHRAaQ.png" /><figcaption>A sequence diagram showing a client sending a filtered QUERY request to an API and receiving a 200 response with Location and Content-Location. A later GET to the stored query resource returns the query’s current result, while a GET to the stored result resource returns the representation produced earlier.</figcaption></figure><p><strong>The distinction matters when the underlying data changes.</strong> A stored query can produce a newer answer. A stored result can represent the answer from a particular execution.</p><p>QUERY also supports conditional requests. Once a client has an ETag or Last-Modified validator, it can ask for results only if they changed. If the server supplies a Location for an equivalent resource, subsequent GET requests can make that flow simpler and more widely compatible.</p><h3>Safe Retries Are a System Design Feature</h3><p>Imagine a client sends a complex search and the connection drops before the response arrives.</p><p>With an application-specific <strong>POST</strong> endpoint, the client cannot infer from the method alone whether retrying might repeat a state change. It needs out-of-band knowledge of that endpoint’s behavior.</p><p><strong>QUERY carries the answer in the protocol.</strong> It is idempotent, so clients and intermediaries can repeat it after a connection failure without introducing a second requested state change.</p><pre>Connection fails<br>├── POST<br>│   └── Retry only with endpoint-specific knowledge or safeguards<br>└── QUERY<br>    └── Retry is allowed by the method&#39;s idempotent semantics</pre><h3>Servers Can Advertise QUERY Support</h3><p>The RFC also defines the Accept-Query response field. A resource can use it to advertise the media types it accepts as query content:</p><pre>HEAD /products HTTP/1.1<br>Host: api.example.com</pre><pre>HTTP/1.1 200 OK<br>Accept-Query: application/json, application/jsonpath</pre><p>This tells the client that the resource supports QUERY with JSON or JSONPath content. Content-Type identifies the query format, while Accept lists the desired response formats.</p><pre>Request format                         Response format<br>Content-Type: application/json         Accept: application/json<br>             │                                  │<br>             └── how to read the query          └── how to return the result</pre><h3>Why You Should Not Replace Every GET Tomorrow</h3><p>QUERY is now standardized. <strong>Standardization and universal deployment are not the same event.</strong></p><p>An HTTP request usually crosses a chain of components:</p><pre>Client<br>  └── SDK or browser<br>      └── corporate proxy<br>          └── CDN or edge cache<br>              └── WAF<br>                  └── API gateway<br>                      └── application server<br>                          └── route handler</pre><p><strong>Every component must either understand QUERY or pass it through correctly.</strong> Before adopting it, verify the boring path:</p><ul><li>The client can send an extension method with content.</li><li>Proxies, firewalls, and gateways do not reject or rewrite it.</li><li>The application server can route it.</li><li>Cache behavior includes content and relevant metadata in the key.</li><li>Authentication and authorization are evaluated before query execution.</li><li>Logs and tracing systems label it correctly without exposing sensitive content.</li><li>Rate limits account for query cost, not merely request count.</li><li>Cross-origin browser calls complete the required CORS preflight.</li></ul><p>There is also a security misconception worth removing:</p><blockquote><em>Request content is less likely than a URI to appear in routine logs. It is not automatically private.</em></blockquote><p>Use HTTPS. Redact sensitive fields. Validate the query language. Apply authorization at the data level. Limit depth, joins, result size, and execution time. A beautifully semantic method can still carry an expensive or malicious query.</p><h3>The Bigger Lesson Is Not About One New Word</h3><p>HTTP methods help distributed systems <strong>communicate intent</strong>. <strong>GET</strong> says, “Give me a representation of this resource.” <strong>POST</strong> says, “Process this content according to this resource’s rules.” <strong>QUERY</strong> now says, “Safely process this query and give me the result.”</p><p><strong>Simple filters should remain GET requests.</strong> Existing <strong>POST</strong> search endpoints will not suddenly stop working. But when a read operation becomes too expressive for a URI and your request path supports it, developers no longer have to choose between <em>readable content</em> and <em>honest HTTP semantics</em>.</p><p>The gap finally has a method.</p><blockquote><strong><em>The one sentence to remember:</em></strong><em> QUERY is a safe, idempotent, content-bearing HTTP method for complex server-side queries.</em></blockquote><h3>References</h3><ul><li><a href="https://www.rfc-editor.org/rfc/rfc10008.html">RFC 10008: The HTTP QUERY Method</a></li><li><a href="https://www.rfc-editor.org/rfc/rfc9110.html">RFC 9110: HTTP Semantics</a></li><li><a href="https://www.rfc-editor.org/rfc/rfc9111.html">RFC 9111: HTTP Caching</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=847280103d35" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[npm vs. npx: Download the Movie or Stream It?]]></title>
            <link>https://karan51ngh.medium.com/npm-vs-npx-download-the-movie-or-stream-it-039f9c55dfe8?source=rss-88cc14533a52------2</link>
            <guid isPermaLink="false">https://medium.com/p/039f9c55dfe8</guid>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[skills]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[Karan Singh Bagga]]></dc:creator>
            <pubDate>Tue, 07 Jul 2026 10:52:53 GMT</pubDate>
            <atom:updated>2026-07-07T10:52:53.113Z</atom:updated>
            <content:encoded><![CDATA[<p><em>The Netflix analogy makes npm and npx easy to remember. Looking under the hood makes them genuinely useful.</em></p><p>Think of <strong>npm as downloading a movie</strong> and <strong>npx as streaming it on Netflix</strong>.</p><p>With npm, you download something, keep it in your collection, and use it whenever your project needs it. With npx, you press play, use the thing right now, and move on without deliberately adding it to your collection.</p><p>In terminal terms:</p><pre># Download and keep a tool in this project<br>npm install --save-dev eslint<br><br># Run a tool directly<br>npx eslint .</pre><p>That analogy is memorable, and it gets the big idea right:</p><blockquote>npm manages packages. npx executes package commands.</blockquote><p>But Netflix does not quite explain why npx sometimes uses a package you already installed, why remote packages remain in a cache, or how a command such as npx skills add knows what add means.</p><p>So let us start with the simple analogy and then go one layer deeper.</p><h3>npm vs. npx in 30 Seconds</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5inZKLK9X4dA2kUzD2zfvA.png" /></figure><p>The distinction is not “<em>npm can never run code</em>” or “<em>npx never downloads code.</em>” npm can run scripts, and npx may fetch a missing package before executing it.</p><p>The real distinction is <strong>managing a dependency</strong> versus <strong>launching a package executable</strong>.</p><h3>npm: Download It, Record It, Keep It</h3><p>npm is the package manager for the <strong>Node.js</strong> ecosystem. Its job is much larger than downloading files. It can install, update, remove, publish, audit, and configure packages, as well as run commands defined by a project.</p><p>Install a runtime dependency:</p><pre>npm install express</pre><p>Install development tools:</p><pre>npm install --save-dev eslint prettier vitest</pre><p>npm places the packages in the project’s dependency tree, records the relationship in package.json, and updates package-lock.json. The project now has an explicit, reviewable dependency on those versions.</p><p>That is the “<em>download the movie</em>” side of the analogy: the package becomes part of the project’s collection.</p><h3>npx: Find the Command and Press Play</h3><p>npx is for executing a command supplied by an npm package:</p><pre>npx eslint .<br>npx prettier --write .<br>npx skills add vercel-labs/agent-skills</pre><p>If a matching package is already installed locally, npx uses that project’s version. If it is missing, npm can fetch it into its cache, expose its executable to the process, and run it.</p><p>This means npx tool does <strong>not</strong> automatically mean “<em>download the latest version.</em>” Local versions take priority when they match the request.</p><h3>What Is npm exec - How Is It Related to npx?</h3><p>The shortest honest answer is:</p><blockquote><em>npm exec is npm&#39;s built-in command for running a package executable.<br>Modern </em><em>npx uses </em><em>npm exec underneath.</em></blockquote><p>They perform the same core job, but calling npx a perfect alias hides a small compatibility detail: the two command forms parse options differently.</p><h3>Why Do Both Commands Exist?</h3><p>npx began as a standalone tool created to solve a common annoyance: developers wanted to run package commands without installing every CLI globally or typing paths such as ./node_modules/.bin/eslint.</p><p>It became popular enough to be <a href="https://github.com/npm/npm/releases/tag/v5.2.0">bundled with npm 5.2 in 2017</a>. Then npm 7 moved that capability into the main npm CLI as npm exec. The standalone npx package was deprecated, and the npx binary was rewritten to use npm exec while preserving familiar behavior for existing users. The <a href="https://docs.npmjs.com/cli/v11/commands/npm-exec/">official npm documentation</a> describes this as a compatibility layer around the same execution machinery.</p><p>Both names remain because npx is short, familiar, and embedded in years of documentation, tutorials, scripts, and developer muscle memory.</p><h3>Same Engine, Slightly Different Argument Parsing</h3><p>For ordinary commands, these express the same intent:</p><pre>npx eslint .<br>npm exec -- eslint .</pre><p>The double dash in the second command means: <strong>“npm, stop parsing options here; pass the rest to the command being executed.”</strong></p><p>Consider TypeScript’s --init flag:</p><pre># With npx, arguments after the package name go to the package command<br>npx tsc --init<br><br># With npm exec, this form makes the boundary explicit<br>npm exec -- tsc --init</pre><p>The placement matters. npm exec tsc -- --init is an easy form to guess, but the documented, unambiguous pattern is npm exec -- tsc --init: put -- before the package command and everything that follows belongs to that command.</p><p>There is no performance prize for choosing one spelling. Use npx when you want the familiar shorthand. Use npm exec when explicit npm syntax, workspaces, --package, or careful argument boundaries make the longer form clearer.</p><h3>What Happens After Either Command?</h3><p>When you run either form, the useful mental model i</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*I3_IJlD6fxEfYujZEQ8XHg.png" /></figure><ol><li>npm makes locally installed package executables available to the command.</li><li>If the requested package is missing locally, npm normally prompts before fetching it in an interactive terminal.</li><li>The missing package is installed into a directory inside <strong>npm’s cache</strong>, and that directory is added to the launched process’s PATH.</li><li>npm infers the executable from the package’s bin field.</li><li>The executable starts and receives the remaining arguments.</li></ol><h3>No, the Download Is Not Immediately Deleted</h3><p>The streaming analogy, “<em>npm downloads the movie, npx streams it</em>” sounds elegant, but it is technically misleading.</p><p>When npx needs a remote package, npm uses its internal package cache. The fetched files can remain there so later executions do not necessarily download everything again. npm provides cache preferences such as prefer-online, prefer-offline, and offline, which is another clue that this is caching, not instant deletion.</p><p>From your project’s perspective, however, the package remains transient:</p><ul><li>It is not added to your project’s dependencies or devDependencies.</li><li>It is not installed into your project’s node_modules tree.</li><li>It is not recorded in your project’s lockfile.</li></ul><p>There is one crucial caveat:</p><blockquote><em>npx may leave your dependency manifest untouched, but the program it executes can still change anything your user account is allowed to change.</em></blockquote><p>npx skills add ... is supposed to write skill files into your workspace. A scaffolder creates an entire project. A formatter rewrites source files. A malicious CLI could do much worse.</p><p>npx is a launcher, not a sandbox.</p><h3>npm, npx, and npm run Are Three Different Ideas</h3><p>Here is the distinction that clears up most confusion.</p><h3>npm install: Put a Package in the Project</h3><pre>npm install --save-dev eslint</pre><p>This downloads ESLint into the project’s dependency tree, records it in devDependencies, and updates the lockfile. npm also creates the platform-appropriate link or command shim for ESLint&#39;s executable.</p><p>Now the project owns an explicit, reviewable relationship with ESLint.</p><h3>npx / npm exec: Launch a Package Executable</h3><pre>npx eslint .<br># equivalent intent<br>npm exec -- eslint .</pre><p>Because ESLint is already installed locally, npm uses the project’s version. It does not need to fetch a second copy.</p><p>That leads to a useful rule: <strong>npx tool does not mean “run the latest tool.”</strong> An unversioned package name can match the version already installed in the project. If you explicitly request tool@1.2.3, a local dependency only matches when its name and version match that request exactly.</p><h3>npm run: Launch a Script Named by Your Project</h3><p>Suppose your package.json contains:</p><pre>{<br>  &quot;scripts&quot;: {<br>    &quot;lint&quot;: &quot;eslint .&quot;,<br>    &quot;lint:fix&quot;: &quot;eslint . --fix&quot;<br>  },<br>  &quot;devDependencies&quot;: {<br>    &quot;eslint&quot;: &quot;^9.0.0&quot;<br>  }<br>}</pre><p>You can run:</p><pre>npm run lint<br>npm run lint:fix</pre><p>npm run looks up lint in <strong>your project&#39;s</strong> scripts object. It also adds node_modules/.bin to the script&#39;s PATH, which is why the script can say eslint instead of ./node_modules/.bin/eslint. This behavior is documented in the official <a href="https://docs.npmjs.com/cli/v11/commands/npm-run/">npm run guide</a>.</p><p>The difference is small but important:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uAZ2ujohHTzU387_MDWDqw.png" /></figure><p>For a command you use repeatedly, an npm script gives the operation a stable name and keeps its flags in version control. Your teammate should not need to remember the exact lint command from Slack history.</p><h3>Case Study: What Does npx skills add Actually Mean?</h3><p>Now we can decode the command that inspired the question:</p><pre>npx    skills    add    vercel-labs/agent-skills<br>│      │         │      │<br>│      │         │      └─ argument: the source to add<br>│      │         └──────── subcommand interpreted by Skills<br>│      └────────────────── npm package and executable name<br>└───────────────────────── find/fetch and launch the executable</pre><p>The easy mistake is to assume that add is an npm script. It is not.</p><p>An npm package becomes a command-line tool when its package.json exposes one or more files through the <a href="https://docs.npmjs.com/cli/v11/configuring-npm/package-json/#bin">bin field</a>. The real <a href="https://www.npmjs.com/package/skills">skills package</a> currently includes this mapping in its <a href="https://github.com/vercel-labs/skills/blob/main/package.json">package.json</a>:</p><pre>{<br>  &quot;name&quot;: &quot;skills&quot;,<br>  &quot;bin&quot;: {<br>    &quot;skills&quot;: &quot;./bin/cli.mjs&quot;,<br>    &quot;add-skill&quot;: &quot;./bin/cli.mjs&quot;<br>  }<br>}</pre><p>npm sees that the package exposes a command named skills and launches ./bin/cli.mjs. Everything after skills is passed to that program.</p><p>Node exposes those values through <a href="https://nodejs.org/api/process.html#processargv">process.argv</a>. A deliberately tiny version of the CLI might read them like this:</p><pre>const [, , command, source] = process.argv;</pre><pre>if (command === &quot;add&quot;) {<br>  console.log(`Adding skills from ${source}`);<br>}</pre><p>The actual Skills CLI is more sophisticated, but the contract is the same:</p><pre>command = &quot;add&quot;<br>source  = &quot;vercel-labs/agent-skills&quot;</pre><p>npx is responsible for making the executable available and starting it. The Skills CLI is responsible for deciding what add means and writing the requested skill files.</p><h3>A Practical Rulebook</h3><p>Use npm install when the package is part of the project:</p><pre>npm install express<br>npm install --save-dev eslint prettier vitest</pre><p>Use npm run for repeatable project commands:</p><pre>npm run lint<br>npm test<br>npm run build</pre><p>Use npx or npm exec for a direct executable, especially when it is already installed locally:</p><pre>npx eslint .<br>npm exec -- prettier --check .</pre><p>Use a one-off remote execution for a generator or diagnostic tool you do not want to keep as a project dependency:</p><pre>npx skills add vercel-labs/agent-skills</pre><p>For project creation, npm also offers npm create, which is an alias of npm init and transforms an initializer into an npm exec operation. For example:</p><pre>npm create vite@latest my-app</pre><p>Under the hood, npm resolves the corresponding create-vite initializer and runs its executable. The <a href="https://docs.npmjs.com/cli/v11/commands/npm-init/">npm init documentation</a> spells out that transformation.</p><p>And if an old tutorial tells you to start every React app with npx create-react-app, note that the React team <a href="https://react.dev/blog/2025/02/14/sunsetting-create-react-app">deprecated Create React App for new apps in February 2025</a>. The npx lesson remains valid; the recommended React tooling has moved on.</p><h3>The Security Footnote That Should Not Be a Footnote</h3><p>This command can download code and execute it:</p><pre>npx some-package</pre><p>A typo can point at a different package. An unpinned package can resolve to a newer release than the one you used last time. The confirmation prompt helps prevent accidents, but clicking y does not make the code trustworthy. In non-interactive or CI environments, npm may proceed without that prompt.</p><p>A safer working habit is:</p><ol><li>Verify the package name, publisher, repository, and command before running it.</li><li>Prefer a locally installed, lockfile-controlled dependency for recurring project work.</li><li>Pin an exact version when repeatability matters:</li></ol><pre>npx some-cli@1.2.3 do-the-thing</pre><ol><li>This pins the requested CLI release, not necessarily its entire transitive dependency tree. For stronger reproducibility, install it as a project dependency, commit the lockfile, and use npm ci in automation.</li><li>Treat -y or --yes as “skip the prompt,” not “make this safe.”</li><li>Never assume npx limits what the launched program can read, write, or send over the network.</li></ol><p>Convenience and trust are separate decisions.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=039f9c55dfe8" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Anatomy of a GenAI Skill: Modular Workflows for AI Native SDLC (2026)]]></title>
            <link>https://karan51ngh.medium.com/anatomy-of-a-genai-skill-modular-workflows-for-the-sdlc-2026-34c02bd325ef?source=rss-88cc14533a52------2</link>
            <guid isPermaLink="false">https://medium.com/p/34c02bd325ef</guid>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[generative-ai-tools]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[skills]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <dc:creator><![CDATA[Karan Singh Bagga]]></dc:creator>
            <pubDate>Sat, 13 Jun 2026 12:18:17 GMT</pubDate>
            <atom:updated>2026-06-27T11:31:13.346Z</atom:updated>
            <content:encoded><![CDATA[<p>Early GenAI software development agents often relied on one overloaded system prompt. Teams stuffed framework rules, coding standards, and CLI commands into the LLM’s context window and hoped it would stay coherent.</p><p>Modern GenAI Software Development Environments (SDEs) such as <em>OpenAI’s Codex, Claude Code, Cursor</em>, and similar tools increasingly use modular guidance and capability packs. These <strong>Agent Skills</strong> are self-contained directories discovered up front and loaded only when relevant. In SDLC terms, they encode repeatable workflows for <em>planning, implementation, testing, review, release, and maintenance</em>.</p><h3>1. The Anatomy: What is a Skill?</h3><p>Skills are a lightweight, open format for extending AI agent capabilities with specialized knowledge and workflows.</p><p>At its core, a skill is a discoverable folder containing a SKILL.md file. In Codex, repo-scoped skills commonly live under .agents/skills, while user-level skills can live under $HOME/.agents/skills. Other SDE hosts may use different locations.</p><p>Here, the <strong>SDE host</strong> is the application or runtime around the model: the IDE, CLI, cloud workspace, or agent platform that discovers skills, manages context, runs tools, and enforces permissions.</p><p>A standard code-generation skill can look like:</p><pre>generate-code-skill/<br>├── SKILL.md          (Required: metadata + instructions)<br>├── scripts/          (Optional: executable Python, Bash, or other code)<br>├── references/       (Optional: documentation and detailed guidance)<br>├── assets/           (Optional: templates, examples, static resources)<br>└── agents/<br>    └── openai.yaml   (Optional: UI metadata, policy, dependencies)</pre><p>For local authoring, a skill can be a plain folder. For distribution or hosted execution, systems may package skills as <em>zip files, plugins, or uploaded bundles</em>. The packaging varies; the core idea is to keep reusable instructions and supporting files together.</p><h3>2. The Trigger: How the AI Decides to Act</h3><p>If an agent has 50 different skills, it should not read all of their instructions at once. That would waste context and dilute the prompt. Skills solve this with <strong>progressive disclosure</strong>.</p><p>The anchor of every skill is the SKILL.md file, which starts with a YAML frontmatter block:</p><pre>---<br>name: auto-linter<br>description: Use when Python files need formatting, lint fixes, or syntax-cleanup checks before tests are run.<br>---</pre><p><strong>The Translation Layer:</strong><br>Models do not browse local folders on their own. The host application acts as the bridge:</p><ol><li>It scans available skills.</li><li>Gives the model a compact inventory of skill names, descriptions, and paths.</li><li>Loads the full instructions only when the model selects a skill.</li></ol><p>Some agent frameworks may represent this inventory as tool metadata or schemas.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HJ5Pe77Nn7-iuP-UmfUE9Q.png" /></figure><p>When you give the agent a prompt like, <em>“Clean up the syntax in </em><em>auth.py&quot;</em>, the model compares your intent with these short descriptions and chooses the most relevant skill.</p><p><em>Developer Note:</em> Write highly distinct descriptions. If one skill says “sends a message” and another says “emails a user,” the model may not have enough signal to distinguish them. This <strong>Tool Collision</strong> can cause the wrong skill to be selected.</p><h3>3. The Brain: The Markdown Body</h3><p>After selection, the host loads the rest of SKILL.md into the active task context. This markdown body becomes a focused instruction layer for that workflow.</p><p>If you are building a generate-python-test skill, the body tells the model <em>how</em> to do the job:</p><ul><li><strong>Framework rules:</strong> “Always use pytest.”</li><li><strong>Constraints:</strong> “Mock all external API calls.”</li><li><strong>Examples:</strong> A few-shot code block showing a perfect, team-approved test.</li></ul><p>This is where teams encode business logic, coding standards, review norms, and output formats.</p><h3>4. The Muscle: Executing Code &amp; Self-Correction</h3><p>A common misconception is that the LLM runs code by itself. It doesn’t. The model handles reasoning and intent; the host application runs commands through tools, shells, or sandboxes and returns the output.</p><p>This brings us to the scripts/ directory. This folder can contain local executables such as lint.sh, run_tests.py, or validate_schema.py. These scripts act as deterministic checks around the model&#39;s work.</p><p><strong>The Self-Correction Loop:</strong><br>What happens if the model writes Python code with a missing quote, and scripts/run_tests.sh fails?</p><ol><li>The script returns an exit code and error output.</li><li>The host application captures the raw stdout / stderr.</li><li>The error is passed back into the model’s active context.</li><li>The model revises the code and tries again, subject to the host’s permissions, limits, and approval flow.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*l2kXWF6o-P0sUw3WLzRSUg.png" /></figure><p>This loop repeats only when the host, workflow, and permissions allow it. Scripts raise the floor by making failures visible and concrete; they do not guarantee perfect code.</p><h3>5. The Library: Providing Context</h3><p>We can’t forget the references/ folder. This holds static files that give the model extra context. It acts as a cheat sheet containing:</p><ul><li>boilerplate_test.py: a blank template file.</li><li>style_guide.md: naming conventions and review rules.</li><li>mock_data.json: pre-made dummy data to reduce hallucinated user information.</li></ul><p>Some skills also include assets/ for output files such as document templates, icons, sample fixtures, or reusable boilerplate.</p><h3>6. Orchestration: The ReAct Loop</h3><p>How does an agent complete a larger ticket like, <em>“Audit my repository for deprecated APIs and draft a migration plan”</em>?</p><p>The work becomes a model-host partnership, often resembling the <strong>ReAct (Reasoning and Acting)</strong> loop. Workflow instructions guide the task, but the agent still chooses actions as new information arrives.</p><ol><li><strong>Analyze &amp; plan:</strong> The model breaks the task into a checklist.</li><li><strong>Match:</strong> It selects the first skill, such as scan-codebase.</li><li><strong>Execute:</strong> The host pauses generation, runs the approved tool or script, and returns the output.</li><li><strong>Review:</strong> The model evaluates the result and decides whether to continue, revise, or use another skill.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*t189XILXM7F6wze6ZHDQHA.png" /></figure><p>The model makes choices on the fly, while the host application manages execution, files, permissions, and tool output.</p><h3>7. Security &amp; Scoping: Keeping the Agent in Check</h3><h3>The Bouncer: Skill Scope and Policy</h3><p>To prevent an agent from using unrelated skills, SDEs scope what the model can see. In a host-agnostic setup, skills are usually discovered from configured locations: the current project, the user’s personal library, organization-provided packs, or system defaults.</p><p>This is where workspace-config.json fits. It is not the skill; it is workspace-level policy that tells the host which skill sources to scan, which skills to enable or disable, whether a skill can be invoked automatically, and which actions need approval.</p><p>Think of the split this way:</p><ul><li>A skill folder says, “Here is a reusable workflow.”</li><li>A workspace config says, “Here is what this workspace allows the agent to see and do.”</li></ul><p>The host decides which skills are available, which metadata enters the prompt, and which actions require approval.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tR8A4-eYJuIpG90l3MCgLA.png" /></figure><h3>The Sandbox: Constrained Execution</h3><p>What happens if an AI agent reads a malicious prompt hidden in an open-source library that tells it to run rm -rf /?</p><p>Modern Hosts combine several controls: sandboxed command execution, workspace boundaries, network restrictions, explicit approvals, and command rules. For example, Codex documents platform-native sandboxing for local execution: macOS uses Seatbelt, Linux/WSL2 can use bubblewrap, and Windows uses native Windows sandboxing.</p><p>The practical rule is: give the agent the smallest useful workspace, restrict network and filesystem access, require approvals for risky actions, and treat third-party skills or scripts like code you need to review.</p><h3>Conclusion</h3><p>The shift from massive system prompts to modular skills makes AI software engineering agents more practical across the SDLC:</p><ol><li><strong>Skills package repeatable workflows:</strong> A skill bundles instructions, scripts, references, and assets for a focused task.</li><li><strong>Progressive disclosure keeps context focused:</strong> The host shows compact metadata first, then loads the full SKILL.md only when needed.</li><li><strong>The host turns reasoning into action:</strong> The model plans and chooses, while the SDE host manages files, tools, command execution, approvals, and feedback loops.</li><li><strong>Supporting files make workflows durable:</strong> scripts/ provide repeatable checks, while references/ and assets/ provide standards, templates, examples, and reusable context.</li><li><strong>Workspace policy controls the blast radius:</strong> A config layer, such as workspace-config.json, decides which skills are visible, what they may access, and which actions need approval.</li><li><strong>Orchestration connects the pieces:</strong> Larger SDLC tasks work through a ReAct-style loop of planning, selecting skills or tools, observing results, and revising next steps.</li><li><strong>Guardrails make the pattern trustworthy:</strong> Scope rules, sandboxing, network limits, and approvals keep agent workflows constrained enough to use safely.</li></ol><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=34c02bd325ef" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building the Ultimate useFetch Hook in React. Part -3 (Complete Code)]]></title>
            <link>https://karan51ngh.medium.com/81e6786b0933?source=rss-88cc14533a52------2</link>
            <guid isPermaLink="false">https://medium.com/p/81e6786b0933</guid>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[front-end-development]]></category>
            <category><![CDATA[typescript]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[react]]></category>
            <dc:creator><![CDATA[Karan Singh Bagga]]></dc:creator>
            <pubDate>Tue, 11 Nov 2025 08:37:44 GMT</pubDate>
            <atom:updated>2025-11-15T06:14:49.022Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*TNcPQLXWvV02un-BuPytAQ.png" /><figcaption>useFetch Hook in React with TypeScript. Part -3</figcaption></figure><blockquote>In the <a href="https://karan51ngh.medium.com/a8d92c1aa6fc">Part -2</a>, we added support for the fetch API’s options object and baked in the ability to refetch data into our hook.</blockquote><h3>Using The Final Implementation of our useFetch Hook</h3><p>Now, whether you’re fetching data or sending it, the component logic remains simple, yet elegant.</p><p>Here is the final, complete code for our <strong>useFetch</strong> hook:</p><pre>import { useState, useEffect, useCallback } from &#39;react&#39;;<br>export function useFetch&lt;T&gt;(url: string, options?: RequestInit) {<br>    const [data, setData] = useState&lt;T | null&gt;(null);<br>    const [loading, setLoading] = useState(false);<br>    const [error, setError] = useState&lt;Error | null&gt;(null);<br><br>    const fetchData = useCallback(async (abortSignal?: AbortSignal) =&gt; {<br><br>        const sleep = (ms: number) =&gt; new Promise(resolve =&gt; setTimeout(resolve, ms));<br>        // const fetchData = async () =&gt; {<br>        setLoading(true);<br>        setError(null);<br>        try {<br>            await sleep(5000);<br>            const response = await fetch(url, {<br>                ...options,<br>                signal: abortSignal || options?.signal,<br>            });<br>            if (!response.ok) {<br>                throw new Error(`HTTP error! Status: ${response.status}`);<br>            }<br>            // Handle cases with no response body<br>            if (response.status !== 200) {<br>                setData(null);<br>            } else {<br>                const result = (await response.json()) as T;<br>                setData(result);<br>            }<br>        } catch (error) {<br>            if (error instanceof Error &amp;&amp; error.name === &#39;AbortError&#39;) {<br>                console.log(&#39;Fetch aborted&#39;);<br>                return;<br>            }<br>            if (error instanceof Error) {<br>                setError(error);<br>            } else {<br>                setError(new Error(&#39;An unknown error occurred&#39;));<br>            }<br>        } finally {<br>            setLoading(false);<br>        }<br>        // We stringify the options object to prevent infinite loops<br>        // eslint-disable-next-line react-hooks/exhaustive-deps<br>    }, [url, JSON.stringify(options || {})])<br>    useEffect(() =&gt; {<br>        const controller = new AbortController();<br>        fetchData(controller.signal);<br>        return () =&gt; {<br>            controller.abort();<br>        };<br>        // eslint-disable-next-line react-hooks/exhaustive-deps<br>    }, [fetchData]);<br>    return { data, loading, error, refetch: fetchData };<br>}</pre><p><strong>Fetching a list of users (</strong><strong>GET):</strong></p><pre>interface User {<br>  id: number;<br>  name: string;<br>}<br>function UserList() {<br>  const { data, loading, error, refetch } = useFetch&lt;User[]&gt;(&#39;/api/users&#39;);<br>  // ... render logic<br>}</pre><p><strong>Creating a new user (</strong><strong>POST):</strong></p><pre>function AddUserForm() {<br>  const newUser = { name: &#39;Jane Doe&#39;, email: &#39;jane@example.com&#39; };<br>  <br>  const options = {<br>    method: &#39;POST&#39;,<br>    headers: { &#39;Content-Type&#39;: &#39;application/json&#39; },<br>    body: JSON.stringify(newUser),<br>  };<br>const { data, loading, error, refetch } = useFetch&lt;User&gt;(&#39;/api/users&#39;, options);<br>  // ... render logic<br>}</pre><h3>Conclusion</h3><p>By building this hook, we’ve achieved the three core benefits of a great abstraction:</p><ul><li><strong>Reusability:</strong> Use one hook for any HTTP request.</li><li><strong>Encapsulation:</strong> The complex logic of fetching, cancellation, and state management is hidden away.</li><li><strong>Type Safety:</strong> Generics give us confidence in our data structures and prevent bugs.</li></ul><p>Happy Coding!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=81e6786b0933" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building the Ultimate useFetch Hook in React. Part -2]]></title>
            <link>https://karan51ngh.medium.com/a8d92c1aa6fc?source=rss-88cc14533a52------2</link>
            <guid isPermaLink="false">https://medium.com/p/a8d92c1aa6fc</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[front-end-development]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[typescript]]></category>
            <category><![CDATA[react]]></category>
            <dc:creator><![CDATA[Karan Singh Bagga]]></dc:creator>
            <pubDate>Sun, 09 Nov 2025 10:49:38 GMT</pubDate>
            <atom:updated>2025-11-09T10:49:38.673Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="useFetch Hook in React with TypeScript. Part -2" src="https://cdn-images-1.medium.com/max/700/1*VsjMa10ReYk0vCnisVYwMg.png" /><figcaption>useFetch Hook in React with TypeScript. Part -2</figcaption></figure><blockquote>In the <a href="https://medium.com/@karan51ngh/17b6007c612a">Part -1</a>, we implemented a very basic useFetch hook, with the ability to make GET calls and handle request cancellations.</blockquote><h3>Chapter 4: Adding Fetch Options</h3><p>Our hook currently supportsGET requests. In order to supportPOST, PUT or sending custom headers we can need to accept the fetch API&#39;s options object.</p><p>TypeScript provides a built-in type for this: RequestInit. We’ll add it as an optional parameter.</p><pre>function useFetch&lt;T&gt;(url: string, options?: RequestInit) {<br>  // ...<br>}</pre><p>However, if we add the options object to our useEffect dependency array, it will cause an infinite loop of API calls, because an object is a new reference on every render.</p><p>The solution is to give the dependency array a stable, primitive value: a string. JSON.stringify() is perfect for this.</p><h3>Chapter 5: Adding a refetch Capability</h3><p>Our hook runs automatically when the component mounts or when its dependencies (url or options) change.</p><p>But what if we want to control the data fetch? For example, implementing a <strong>“Refresh” button</strong> or more importantly, scenario where a user submits new data.</p><p>A refetch function makes this possible. It gives our hook an <em>imperative</em> &quot;remote control&quot; to trigger the fetch whenever we need it.</p><h4>Exposing the Fetch Logic</h4><p>Our fetching logic currently lives inside a function that’s created within useEffect. To make it callable, we need to move it outside. However, if we define it as a plain function inside our hook, it will be recreated on every single render. This would cause our useEffect to run in an infinite loop.</p><p>The solution is: useCallback hook. It will memoize the function, guaranteeing that we get the exact same function instance between renders unless its dependencies change.</p><h4>The Implementation</h4><p>We’ll wrap our fetchData logic in a useCallback hook. The dependencies for this callback will be the same as our old useEffect dependencies: url and the stringified options.</p><p>The useEffect then becomes much simpler: its only job being to call the memoized fetchData function on the initial render and handling the cleanup. Finally, we return our new refetch function from our hook.</p><pre>import { useState, useEffect, useCallback } from &#39;react&#39;;<br><br>function useFetch&lt;T&gt;(url: string, options?: RequestInit) {<br>  // ... (useState declarations for data, loading, error)<br><br>  // 1. Wrap the fetching logic in useCallback<br>  const fetchData = useCallback(async () =&gt; {<br>    setLoading(true);<br>    // ... all the try/catch/finally logic<br>  }, [url, JSON.stringify(options || {})]);<br><br>  // 2. useEffect now just calls the function and handles cleanup<br>  useEffect(() =&gt; {<br>    const controller = new AbortController();<br>    // We pass the signal to a slightly modified fetchData<br>    // (See full code in the final version for this detail)<br>    fetchData(controller.signal);<br><br>    return () =&gt; {<br>      controller.abort();<br>    };<br>  }, [fetchData]); // The dependency is now the stable, memoized function<br><br>  // 3. Return the function so the component can use it<br>  return { data, loading, error, refetch: fetchData };<br>}<br></pre><p>Here’s an example implementation of the <strong>Refresh</strong> button:</p><pre>import { useFetch } from &#39;./useFetch&#39;;<br><br>function UserList() {<br>  // We just grab &#39;refetch&#39; from our hook<br>  const { data, loading, error, refetch } = useFetch&lt;User[]&gt;(&#39;/api/users&#39;);<br><br>  if (error) return &lt;p&gt;Error: {error.message}&lt;/p&gt;;<br><br>  return (<br>    &lt;div&gt;<br>      &lt;h1&gt;User List&lt;/h1&gt;<br>      &lt;button onClick={() =&gt; refetch()} disabled={loading}&gt;<br>        {loading ? &#39;Refreshing...&#39; : &#39;Refresh&#39;}<br>      &lt;/button&gt;<br><br>      &lt;ul&gt;<br>        {/* ... render the list */}<br>      &lt;/ul&gt;<br>    &lt;/div&gt;<br>  );<br>}</pre><p>Our useFetch hook is ready. In <a href="https://karan51ngh.medium.com/81e6786b0933">Part -3</a>, we’ll conclude with the complete implementation and demonstrate how to use it effectively.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a8d92c1aa6fc" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building the Ultimate useFetch Hook in React. Part -1]]></title>
            <link>https://karan51ngh.medium.com/17b6007c612a?source=rss-88cc14533a52------2</link>
            <guid isPermaLink="false">https://medium.com/p/17b6007c612a</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[typescript]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[front-end-development]]></category>
            <dc:creator><![CDATA[Karan Singh Bagga]]></dc:creator>
            <pubDate>Sun, 09 Nov 2025 10:48:53 GMT</pubDate>
            <atom:updated>2025-11-09T10:48:53.544Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="useFetch Hook in React with TypeScript. Part -1" src="https://cdn-images-1.medium.com/max/700/1*Hwrw86bULzCoikYF_FtaHA.png" /><figcaption>useFetch Hook in React with TypeScript. Part -1</figcaption></figure><h4>A step-by-step guide to creating a reusable, and type-safe custom hook for all your data fetching needs using TypeScript.</h4><blockquote>This is a 3-part series. If you want to skip straight to the full code and demo, jump to <a href="https://medium.com/@karan51ngh/81e6786b0933">Part-3</a>.</blockquote><p>If you’ve been working with React for any length of time, you’ve seen this pattern a dozen times. You need to fetch some data, so you reach for useState and useEffect.</p><pre>function UserProfile() {<br>  const [user, setUser] = useState(null);<br>  const [loading, setLoading] = useState(true);<br>  const [error, setError] = useState(null);<br><br>  useEffect(() =&gt; {<br>      fetch(&#39;/api/user/1&#39;)<br>        .then(res =&gt; res.json())<br>        .then(data =&gt; setUser(data))<br>        .catch(err =&gt; setError(err))<br>        .finally(() =&gt; setLoading(false));<br>    }, []);<br>    // ... render logic<br>}</pre><p>It works. But then you need to fetch a list of posts. Then a list of comments. Soon, you’re copying and pasting this same block of logic everywhere, changing only the URL and the state variable.</p><p>Today, we’re going to stop writing boilerplate fetch logic and build one powerful, reusable useFetch hook that will handle almost all of our data-fetching needs. We&#39;ll build it from the ground up, piece by piece.</p><h3>Chapter 1: The Foundation and Managing State</h3><p>Before we can fetch anything, we need to know what information to track. For any given data request, there are three critical pieces of state:</p><ol><li>data: The JSON response when the fetch is successful.</li><li>loading: A boolean to know if the request is in progress.</li><li>error: An error object if the fetch fails.</li></ol><p>The idea here is to make our hook work with <em>any</em> data structure: users, posts, products, etc. while keeping full type safety. This is the perfect job for <strong>Generics</strong>.</p><p>We’ll define our hook with a generic type placeholder, &lt;T&gt;.</p><pre>import { useState } from &#39;react&#39;;<br><br>function useFetch&lt;T&gt;(url: string) {<br>  const [data, setData] = useState&lt;T | null&gt;(null);<br>  const [loading, setLoading] = useState&lt;boolean&gt;(false);<br>  const [error, setError] = useState&lt;Error | null&gt;(null);<br>  // We&#39;ll add the fetch logic next...<br>  return { data, loading, error };<br>}</pre><p>This simple foundation is both flexible and completely type-safe.</p><h3>Chapter 2: making the API Request with useEffect</h3><p>Data fetching is a <em>side effect</em>. It’s an action that reaches outside of React into the browser’s APIs. The useEffect hook is the perfect tool for this.</p><p>The logic follows a clear path:</p><ol><li>Set loading to true and errors to nullbefore the request starts.</li><li>Use a try block to await the fetch and set the data on success.</li><li>Use a catch block to capture any errors.</li><li>Use a finally block to set loading back to false, no matter what.</li></ol><pre>function useFetch&lt;T&gt;(url: string) {<br>  // useState declarations...<br><br>  useEffect(() =&gt; {<br>    const fetchData = async () =&gt; {<br>      setLoading(true);<br>      setError(null); // Clear previous errors<br>      try {<br>        const response = await fetch(url);<br>        if (!response.ok) {<br>          throw new Error(`HTTP error! Status: ${response.status}`);<br>        }<br>        const result = await response.json() as T; // Assert the type<br>        setData(result);<br>      } catch (error) {<br>        if (error instanceof Error) {<br>          setError(error);<br>        } else {<br>          setError(new Error(&#39;An unknown error occurred&#39;));<br>        }<br>      } finally {<br>        setLoading(false);<br>      }<br>    };<br>    fetchData();<br>  }, [url]); // Re-run the effect if the URL changes<br>  return { data, loading, error };<br>}</pre><p>Now we have a fully functional hook. The component that uses it doesn’t need to know about async/await or error handling; it just gets the final state.</p><h3>Chapter 3: Handling Cancellations</h3><p>Our hook has a subtle bug. If the component that uses useFetch unmounts while the request is still in flight, React will show a warning: &quot;<em>Can&#39;t perform a React state update on an unmounted component.</em>&quot; This is a memory leak.</p><p>We need to cancel the request on cleanup. The useEffect hook has a built-in mechanism for this: the <strong>cleanup function</strong>. If you return a function from useEffect, React will run it when the component unmounts.</p><p>We’ll use the browser’s AbortController API to make our fetch cancellable.</p><pre>// Inside useFetch...<br>useEffect(() =&gt; {<br>  // 1. Create a controller for this effect run<br>  const controller = new AbortController();<br><br>const fetchData = async () =&gt; {<br>    setLoading(true);<br>    // ...<br>    try {<br>      // 2. Pass its signal to the fetch options<br>      const response = await fetch(url, { signal: controller.signal });<br>      // ...<br>    } catch (error) {<br>      // 3. IMPORTANT: Ignore the AbortError<br>      if (error instanceof Error &amp;&amp; error.name === &#39;AbortError&#39;) {<br>        console.log(&#39;Fetch aborted&#39;);<br>        return; // Don&#39;t set state, just exit<br>      }<br>      // ... handle other errors<br>    }<br>    // ...<br>  };<br>  fetchData();<br>  // 4. Return the cleanup function to abort the request<br>  return () =&gt; {<br>    controller.abort();<br>  };<br>}, [url]);</pre><p>When controller.abort() is called, the fetch promise rejects with an AbortError. We catch this error but <strong>do not</strong> set our error state. An aborted request isn&#39;t a <em>real</em> error; it&#39;s our code cleaning up as intended. The user has already navigated away, so we don&#39;t want to show them a confusing error message.</p><p>In this part, we built a simple useFetch hook. In <a href="https://karan51ngh.medium.com/a8d92c1aa6fc/">Part -2</a>, we’ll refine it further by adding support for fetch options and a refetch mechanism.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=17b6007c612a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Creating an Infinite Loop in React. Twice. Here’s how not to use useEffect.]]></title>
            <link>https://karan51ngh.medium.com/9e76dfc2d26c?source=rss-88cc14533a52------2</link>
            <guid isPermaLink="false">https://medium.com/p/9e76dfc2d26c</guid>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[typescript]]></category>
            <category><![CDATA[reactjs]]></category>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[Karan Singh Bagga]]></dc:creator>
            <pubDate>Sat, 08 Nov 2025 14:28:49 GMT</pubDate>
            <atom:updated>2025-11-08T14:28:49.292Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*223r_sr_b5JBiNcm853FzA.png" /></figure><h4>A deep dive into the subtle traps of React’s most powerful hook and why one infinite loop can feel very different from another.</h4><p>You build a simple component, add a bit of state, and throw in a useEffect for good measure. You hit save and suddenly the browser slows down, the UI lags, and you’re left wondering what just happened.</p><p>Chances are, you’ve run into one of the classic pitfalls of the useEffect hook. It’s one of React’s most powerful tools, but also one of the easiest to misuse.</p><p>In this article, I’ll walk you through two different ways an infinite loop can sneak into your code and how each behaves in its own unique way.</p><h3>The First Trap: The “Missing” Dependency Array</h3><p>Let’s start with a simple goal: <strong>Generate a random number when a component loads</strong>. The first attempt might look something like this:</p><pre>import React, { useState, useEffect } from &#39;react&#39;;<br><br>const NumberGenerator = () =&gt; {<br>  const [randomNumber, setRandomNumber] = useState&lt;number&gt;(0);<br>  useEffect(() =&gt; {<br>    // Let&#39;s set a random number!<br>    setRandomNumber(Math.floor(Math.random() * 10));<br>  }, ); // Notice the dependency array is missing<br><br>  return &lt;h1&gt;Random Number: {randomNumber}&lt;/h1&gt;;<br>};</pre><p><em>“Will this create a render loop?” </em><strong>Yes. Absolutely.</strong> This is the most common useEffect mistake.</p><h3>But Why?</h3><p>If you don&#39;t provide the second argument (<strong>dependency array</strong>) while defining your useEffect<strong> hook, </strong>the effect function will run <strong>after every single render</strong>.</p><p>Here’s the vicious cycle that ensues:</p><ul><li><strong>Component Renders:</strong> The component renders for the first time.</li><li><strong>useEffect Runs:</strong> Because there is no dependency array.</li><li><strong>State is Updated:</strong> setRandomNumber() is called, which updates the component&#39;s state.</li><li><strong>Re-render is Triggered:</strong> Any state update in React causes the component to re-render.</li><li><strong>Step-1 Again (Loop):</strong> The component re-renders, which causes the effect to run again, which updates the state again, and so on, forever.</li></ul><h3>The Observation: “But it’s not hanging my application!”</h3><p>Here’s where things get interesting. When I ran this code, my app became sluggish and my CPU spiked, but the browser tab didn’t completely freeze. I could still click things, even if they were slow to respond.</p><p>This is because a React render loop isn’t the same as a traditional while (true) {} loop. A while loop blocks the JavaScript main thread entirely. The browser can&#39;t do <em>anything</em> else.</p><p>A React loop, however, is a series of <strong>scheduled updates</strong>. Between each render -&gt; effect -&gt; setState cycle, React yields control back to the browser for a brief moment. The browser gets a millisecond to breathe before React starts the next render. So it&#39;s less of a &quot;freeze&quot; and more of a &quot;hyperactive&quot; state that suffocates the browser with endless work.</p><p><strong>How to fix it:</strong> To run an effect only once when the component mounts, provide an empty dependency array [].</p><pre>useEffect(() =&gt; {<br>  setRandomNumber(Math.floor(Math.random() * 10));<br>}, []); // Empty array implies: &quot;will run only once&quot;</pre><h3>The Second Trap: The Self-Destructive Dependency</h3><p>What if I want the effect to run only when randomNumber itself changes?</p><pre>import React, { useState, useEffect } from &#39;react&#39;;<br><br>const NumberGenerator = () =&gt; {<br>  const [randomNumber, setRandomNumber] = useState&lt;number&gt;(0);<br>  useEffect(() =&gt; {<br>    // Let&#39;s set a new random number!<br>    setRandomNumber(Math.floor(Math.random() * 10));<br>  }, [randomNumber]); // The effect now depends on the state it changes<br><br>  return &lt;h1&gt;Random Number: {randomNumber}&lt;/h1&gt;;<br>};</pre><p>Again, the question arises; <em>“Will this create a render loop?” </em><strong>Yes, </strong>and this one is even more insidious.</p><h3>But Why?</h3><p>You’ve explicitly told React: “Hey, I want you to run this effect whenever randomNumber changes.&quot; But the effect&#39;s one and only job is to <em>change </em><em>randomNumber</em>.</p><p>This creates an inescapable feedback loop:</p><ul><li>The component renders.</li><li>After rendering, useEffect runs for the first time.</li><li>It calls setRandomNumber(), updating the state.</li><li>This triggers a re-render.</li><li>After the re-render, React compares the old randomNumber with the new one. They are different!</li><li>Because a dependency changed, React runs the effect again.</li><li><strong>Go to Step 3.</strong></li></ul><h3>The Observation: “This one hangs my application.”</h3><p>Running this second scenario, my application felt much more frozen. User input was completely ignored. It felt like a true “hang.”</p><p>While both scenarios are infinite loops, the second one creates a tighter, more insistent cycle. The update is <strong>conditional</strong> on the very state it produces. The React scheduler sees this <strong>tight coupling</strong>: a state update <em>immediately</em> invalidating the condition that allowed it to rest and can get locked into a high-priority loop that gives the browser even less time to breathe than the first scenario.</p><p>It’s the difference between a predictable, rhythmic chaos <strong>(Scenario 1)</strong> and a self-validating paradoxical loop that starves the event queue more aggressively <strong>(Scenario 2)</strong>.</p><p>But honestly, this is an academic difference. It’s like debating which of two flat tires is flatter. <strong>Both are critical bugs.</strong></p><h3>The Golden Rules of useEffect</h3><p>This journey taught me two unforgettable rules for working with useEffect.</p><h4>1. The Dependency Array is NOT Optional. It Defines Your Logic.</h4><ul><li><strong>No Array </strong><strong>useEffect(() =&gt; {})</strong>: Runs after <strong>every</strong> render. Use this only if you truly want to react to every single prop or state change.</li><li><strong>Empty Array </strong><strong>useEffect(() =&gt; {}, [])</strong>: Runs <strong>only once</strong> after the initial render. Perfect for one-time setups, data fetching, or subscriptions.</li><li><strong>Populated Array </strong><strong>useEffect(() =&gt; {}, [a, b])</strong>: Runs after the initial render, and then <strong>only if</strong> a or b have changed since the last render. This is the heart of reactive programming in React.</li></ul><h4>2. An Effect Should Not Set a State That Is in Its Own Dependency Array.</h4><p>This is the definition of the self-destructive loop we saw in <strong>Scenario-2</strong>. The job of an effect is to cause a side effect <em>in response to</em> a change, not to cause the change that it responds to.</p><p>Happy coding!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9e76dfc2d26c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Your React.memo is Lying to You. Here’s Why useCallback is the Answer.]]></title>
            <link>https://karan51ngh.medium.com/46fb332edbc8?source=rss-88cc14533a52------2</link>
            <guid isPermaLink="false">https://medium.com/p/46fb332edbc8</guid>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[typescript]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[front-end-development]]></category>
            <category><![CDATA[react]]></category>
            <dc:creator><![CDATA[Karan Singh Bagga]]></dc:creator>
            <pubDate>Sun, 02 Nov 2025 06:50:28 GMT</pubDate>
            <atom:updated>2025-11-08T14:52:42.666Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*YXYLio_tYA1p4HCLcSdO6Q.png" /></figure><p>You have a component that’s re-rendering way too often. You identify an expensive child component, wrap it in React.memo .But then you open the profiler, and it’s still re-rendering every single time the parent does.</p><p>It’s not broken, but it’s being tripped up by one of JavaScript’s fundamental truths: <strong>functions are objects</strong>. And in the world of React, that little detail can cost you dearly in performance.</p><p>Today, we’ll unravel this mystery and show how the useCallback hook is the key to making your optimizations actually work.</p><h3>When React.memo Fails</h3><p>We have a ParentComponent that holds some state, and it passes a function down to a ChildComponent. To prevent the child from re-rendering unnecessarily, we&#39;ve wrapped it in React.memo.</p><p>As a <em>quick refresher</em>, React.memo is a higher-order component that tells React, <strong>&quot;Don&#39;t re-render this component unless its props change.&quot; </strong>It does this by doing a shallow comparison of the old props and the new props.</p><p>Here’s our code:</p><pre>import React, { useState } from &#39;react&#39;;<br><br>// An &quot;expensive&quot; child component we want to protect from re-renders<br>const ChildComponent = React.memo(<br>  ({ onButtonClick }: { onButtonClick: () =&gt; void }) =&gt; {<br>    console.log(&quot;ChildComponent is re-rendering! Why?&quot;);<br><br>    return &lt;button onClick={onButtonClick}&gt;Click Me&lt;/button&gt;;<br>});<br><br>const ParentComponent = () =&gt; {<br>  const [count, setCount] = useState(0);<br><br>  // This function is passed as a prop to the child<br>  const handleButtonClick = () =&gt; {<br>    console.log(&quot;Button was clicked!&quot;);<br>  };<br><br>  return (<br>    &lt;div&gt;<br>      {/* This button&#39;s only job is to cause the Parent to re-render */}<br>      &lt;button onClick={() =&gt; setCount(count + 1)}&gt;<br>        Re-render Parent: {count}<br>      &lt;/button&gt;<br>      &lt;ChildComponent onButtonClick={handleButtonClick} /&gt;<br>    &lt;/div&gt;<br>  );<br>};</pre><p>When you run this and click the <strong>“Re-render Parent”</strong> button, you’ll see ChildComponent is re-rendering! Why? in the console every single time.</p><p>But why? The child’s props haven’t <em>really</em> changed, have they?</p><p>The problem lies in how JavaScript handles equality. Every time ParentComponent re-renders, the handleButtonClick function is <strong>re-created from scratch</strong>.</p><p>It might do the same thing, but it’s a brand new function in memory. For React.memo&#39;s shallow comparison, oldProps.onButtonClick === newProps.onButtonClick is always false. The old function and the new function are two different objects (Different addresses in memory).</p><h3>useCallback to the Rescue</h3><p>This is the exact problem useCallback was designed to solve. It lets us <strong>memoize</strong> a function, giving it a stable identity across renders.</p><p>Let’s fix our ParentComponent:</p><pre>import React, { useState, useCallback } from &#39;react&#39;;<br><br>// ChildComponent remains the same<br>const ChildComponent = React.memo(/* ... */);<br><br>const ParentComponent = () =&gt; {<br>  const [count, setCount] = useState(0);<br><br>  // This function is now MEMOIZED<br>  const handleButtonClick = useCallback(() =&gt; {<br>    console.log(&quot;Button was clicked!&quot;);<br>  }, []); // The dependency array<br><br>  return (<br>    &lt;div&gt;<br>      &lt;button onClick={() =&gt; setCount(count + 1)}&gt;<br>        Re-render Parent: {count}<br>      &lt;/button&gt;<br>      &lt;ChildComponent onButtonClick={handleButtonClick} /&gt;<br>    &lt;/div&gt;<br>  );<br>};</pre><p>By wrapping our function definition in useCallback and providing an empty dependency array [], we&#39;re telling React to create this function once, on the very first render, and then give back that exact same function instance every other time.</p><p>Now, when the parent re-renders, React.memo compares the onButtonClick prop and finds that it is the <em>exact same function instance</em> as before.</p><h3>The Plot Twist: The Dependency Array and Stale Closures</h3><p>If we modify our callback to use the count state.</p><pre>const ParentComponent = () =&gt; {<br>  const [count, setCount] = useState(0);<br><br>// This function now uses the &#39;count&#39; state<br>  const handleButtonClick = useCallback(() =&gt; {<br>    alert(`The current count is: ${count}`);<br>  }, []); // &lt;-- DANGER: Dependency array is empty!<br>  // ...<br>};</pre><p>If you click “Re-render Parent” a few times to get the count to, say, 3, and then click the child&#39;s button, you&#39;ll get an alert saying “The current count is: 0”. This is a classic “stale closure.”</p><p>Our callback was created only once, when count was 0. It closed over that value and will remember it forever. It never gets the updated count.</p><p>The fix is to tell useCallback what it depends on.</p><pre>const handleButtonClick = useCallback(() =&gt; {<br>  alert(`The current count is: ${count}`);<br>}, [count]); // &lt;-- CORRECT: Now it depends on count</pre><p>Now, React knows to give us a new function, but <em>only</em> if count changes. This fixes the stale closure but re-introduces a version of our original problem: a new function is created whenever the count changes, which will cause our memoized child to re-render.</p><h3>The Pro Move: Using the State Updater Pattern</h3><p>There’s a powerful pattern that lets us have the best of both worlds: useState&#39;s updater function pattern. setCount can take a function as an argument. React will pass the most up-to-date state to that function.</p><p>Let’s say our callback needed to increment the count.</p><pre>// The dependent version (works, but creates new functions)<br>const increment = useCallback(() =&gt; {<br>  setCount(count + 1);<br>}, [count]);<br><br>// The independent, stable version<br>const incrementStable = useCallback(() =&gt; {<br>  setCount(prevCount =&gt; prevCount + 1);<br>}, []); // &lt;-- The dependency array is EMPTY</pre><p>By using the updater form setCount(prevCount =&gt; ...), our callback no longer needs to know what the current count is. It just needs the setCount function (which is guaranteed by React to be stable).</p><p>Now, our incrementStable function truly never changes, and we can pass it down to memoized children without breaking their optimization.</p><h3>useCallback vs. useMemo:</h3><p>useMemo, useCallback might seem familiar. In fact useCallback is just syntactic sugar for useMemo.</p><ul><li>useMemo(() =&gt; fn(), deps) <strong>invokes</strong> the function and memoizes its <strong>return value</strong>.</li><li>useCallback(fn, deps) <strong>does not invoke</strong> the function. It memoizes the <strong>function itself</strong>.</li></ul><p>In fact, These two lines are equivalent:</p><pre>const fn1 = useCallback(myFunction, [a, b]);<br>const fn2 = useMemo(() =&gt; myFunction, [a, b]);</pre><h3>When Should You Use It?</h3><p>useCallback is a performance optimization, but premature optimization is a trap. Don&#39;t wrap every function in useCallback. Use it when you have a specific reason.</p><ol><li><strong>Passing callbacks to optimized children:</strong> This is the primary use case. If you are passing a function to a component wrapped in React.memo, you should wrap that function in useCallback.</li><li><strong>As a dependency for another hook:</strong> If a function is used inside a useEffect, useMemo, or another useCallback, it should be wrapped in useCallback to prevent the hook from re-running on every render.</li></ol><p>The next time React.memo seems to be lying to you, you&#39;ll know exactly what to do. Happy Coding!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=46fb332edbc8" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The 2 useState Mistakes I Made Too Often (And How to Fix them)]]></title>
            <link>https://karan51ngh.medium.com/112afdfeb6aa?source=rss-88cc14533a52------2</link>
            <guid isPermaLink="false">https://medium.com/p/112afdfeb6aa</guid>
            <category><![CDATA[typescript]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[react-hook]]></category>
            <category><![CDATA[front-end-development]]></category>
            <dc:creator><![CDATA[Karan Singh Bagga]]></dc:creator>
            <pubDate>Fri, 24 Oct 2025 15:17:00 GMT</pubDate>
            <atom:updated>2025-10-26T19:33:47.988Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*XlaCQktH6meYjmeWFUW60Q.jpeg" /></figure><h4>A Deep Dive into React’s useState Hook, From Basic Syntax to the Bugs That Bite You.</h4><p>We’ve learnt React. We know what a component is. We’ve imported useState and made a counter. It went 0, 1, 2.</p><p>And then we tried to do something more complex.</p><p>We update an object, but the screen doesn’t change. Suddenly, our component isn’t re-rendering. If this sounds familiar, you’re not alone.</p><p>The useState hook is simple to learn but surprisingly easy to misuse. Let’s dissect this fundamental hook and fix the subtle bugs that drive us developers crazy. We’ll go from the absolute basics to the “Pro-League Hacks” that will make your state management bulletproof.</p><h3>The useState 101</h3><p>At its core, useState is a hook that adds a piece of memory (state) to a function component. When you call it, you give it an initial value, and it gives you back an array with two things:</p><ol><li><strong>The current state value</strong> (the “getter”).</li><li><strong>A function to update that value</strong> (the “setter”).</li></ol><p>We almost always use array destructuring to grab them.</p><pre>import React, { useState } from &#39;react&#39;;<br><br>function Counter() {<br>  // 1. Call useState with an initial value (0)<br>  const [count, setCount] = useState&lt;number&gt;(0);<br>  const increment = () =&gt; {<br>    // 2. We call the setter with the new value<br>    setCount(count + 1);<br>  };<br>  return (<br>    &lt;div&gt;<br>      {/* 3. we use the getter to render the value */}<br>      &lt;p&gt;You clicked {count} times&lt;/p&gt;<br>      &lt;button onClick={increment}&gt;<br>        Click me<br>      &lt;/button&gt;<br>    &lt;/div&gt;<br>  );<br>}</pre><p>The first time this component appears, count is 0. When you click the <strong>“increment” </strong>button, the count updates. This update in the state causes the entire component to Re-render.</p><h3>Mistake #1: Mutating object States</h3><p><strong>You must never, ever mutate object states directly.</strong></p><p>React decides whether to re-render by checking if the state variable has changed. If your state is an <strong>object</strong> or an <strong>array</strong> (which is fundamentally an object in javascript), its “value” is actually a <strong>reference</strong>: an address in memory.</p><p>If you just change a property inside that object, the address stays the same. React looks at it, sees the same old address, and detects no change in the state, leading to no re-renders.</p><h4>Updating Objects: Always use The Spread (...) Syntax</h4><p>Let’s say we have a user state.</p><pre>interface User {<br>  username: string;<br>  age: number;<br>}<br>const [user, setUser] = useState&lt;User&gt;({ username: &#39;alex&#39;, age: 25 });</pre><p><strong>The WRONG Way of Mutation:</strong> This code <em>looks</em> like it should work, but it won’t trigger a re-render.</p><pre>const handleBirthday = () =&gt; {<br>  // We are changing the original &#39;user&#39; object<br>  user.age = user.age + 1; <br>  setUser(user); // React sees the same object reference! No update.<br>};</pre><p><strong>The RIGHT Way (Immutable Update):</strong> We create a <strong>brand new object</strong> by copying the old properties (...user) and then overwriting the one we want to change.</p><pre>const handleBirthday = () =&gt; {<br>  // Create a new object with the copied properties and the new age<br>  setUser({ ...user, age: user.age + 1 });<br>};</pre><p>Now, setUser is receiving a completely new object. React detects the change and re-renders your component.</p><h4>Updating Arrays: .map() and .filter() are the best Tools</h4><p>The same rule applies to arrays. You can’t use methods like .push() or .splice() because they mutate the original array.</p><p><strong>Adding an Item</strong> Use the spread syntax to create a new array.</p><pre>const [todos, setTodos] = useState&lt;string[]&gt;([&#39;Learn React&#39;]);<br><br>const addTodo = () =&gt; {<br>  setTodos([...todos, &#39;Learn Hooks&#39;]); // Creates a new array<br>};</pre><p><strong>Removing an Item</strong> The .filter() method is perfect because it returns a brand new array.</p><pre>const removeTodo = (todoToRemove: string) =&gt; {<br>  setTodos(todos.filter(todo =&gt; todo !== todoToRemove));<br>};</pre><p><strong>Updating an Item at an Index</strong> The .map() method is the go-to. It also returns a brand new array.</p><pre>const updateTodo = (indexToUpdate: number, newValue: string) =&gt; {<br>  setTodos(todos.map((todo, index) =&gt; {<br>    return index === indexToUpdate ? newValue : todo;<br>  }));<br>};</pre><h3>Mistake #2: State Updates That Depend on Previous State</h3><p>This is the subtle bug that separates the beginners from the pros. What happens when you want to update state multiple times in a single function?</p><p>Consider this component that tries to add 3 on a single click.</p><h4><strong>The Buggy Way</strong></h4><pre>const handleClick = () =&gt; {<br>  setCount(count + 1); // If count is 0, this queues an update to 1<br>  setCount(count + 1); // &#39;count&#39; is STILL 0, this queues an update to 1<br>  setCount(count + 1); // &#39;count&#39; is STILL 0, this queues an update to 1<br>};<br>// Result: The count only goes up by 1.</pre><p><strong>Why does this fail?</strong> Because state is a <strong>snapshot</strong>. The count variable is a snapshot of the value from the last render. It doesn&#39;t change <em>inside</em> your handleClick function. React &quot;batches&quot; these calls, sees three requests to set the state to 1, and does it.</p><p><strong>How does this Batching work?</strong></p><p>When you call setState, the update doesn’t happen right away. Instead, React quietly collects all those updates into a little to-do list (“batch”).</p><p>Once the event handler (handleClick) is done running, React takes that list, figures out the final state (applying all the updates you made), and only <em>then</em> decides if it needs to re-render your component (if the new state is different from the old one).</p><p>This batching process is a big performance win. Without it, something like calling setCount(count + 1) three times would trigger three separate re-renders .</p><p>So React waits until your function finishes before applying all those updates at once.</p><h4><strong>The Safe Way: The Updater Function</strong></h4><p>To fix this, we pass a <em>function</em> to our setter. This updater function automatically receives the most recent, pending state from React’s Batch queue.</p><pre>const handleClick = () =&gt; {<br>  // Pass a function to guarantee you&#39;re working with the latest state<br>  setCount(prevCount =&gt; prevCount + 1);<br>  setCount(prevCount =&gt; prevCount + 1);<br>  setCount(prevCount =&gt; prevCount + 1);<br>};<br>// Result: The count correctly goes up by 3!</pre><p>React batches/queues these <strong>functions</strong> (instructions), <strong>not final values (</strong>or<strong> </strong>snapshots<strong>)</strong>.</p><ol><li><strong>Instruction 1:</strong> “Take the current state <strong>(0)</strong> and add 1.” -&gt; <strong>returns 1</strong>.</li><li><strong>Instruction 2:</strong> “Take the current state <strong>( </strong>now<strong> 1)</strong> and add 1.” -&gt; <strong>returns 2</strong>.</li><li><strong>Instruction 3:</strong> “Take the current state <strong>( </strong>now<strong> 2)</strong> and add 1.” -&gt; <strong>returns 3</strong>.</li></ol><p>After your event handler finishes, React processes this queue and re-renders <em>once</em> with the final state of 3.</p><p>The rule is simple: <strong>If your new state depends on the previous state, always use the updater function form.</strong></p><h3>Conclusion</h3><p>Mastering useStateis about understanding the principles:</p><ol><li><strong>State is Immutable:</strong> Never change objects or arrays directly. Always create a new one using spread syntax (...), .map(), or .filter().</li><li><strong>State is a Snapshot:</strong> When a new state depends on the old one, don’t trust the state variable from the render. Use the updater function (setCount(prev =&gt; ...)) to get the latest value from React&#39;s queue.</li></ol><p>Happy Coding!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=112afdfeb6aa" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Ultimate Guide to React’s useRef Hook in TypeScript]]></title>
            <link>https://karan51ngh.medium.com/the-ultimate-guide-to-reacts-useref-hook-in-typescript-3789471519bf?source=rss-88cc14533a52------2</link>
            <guid isPermaLink="false">https://medium.com/p/3789471519bf</guid>
            <category><![CDATA[react]]></category>
            <category><![CDATA[react-hook]]></category>
            <category><![CDATA[useref]]></category>
            <category><![CDATA[typescript]]></category>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[Karan Singh Bagga]]></dc:creator>
            <pubDate>Tue, 21 Oct 2025 07:14:14 GMT</pubDate>
            <atom:updated>2025-10-21T07:14:14.750Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="An image showing the React.js and TypeScript logos, with “useRef()” written in the center. A faint illustration of a hook appears in the background, symbolizing the useContext hook in React with TypeScript." src="https://cdn-images-1.medium.com/max/700/1*NlCEUzKlM_ptxeHoP0A9fQ.png" /></figure><p>If you’ve ever needed to “reach out and touch” a DOM element directly, or track a value between renders without causing an infinite loop, you’ve stumbled upon the exact problems that React’s useRef hook was designed to solve.</p><p>Think of useRef as creating a special &quot;box&quot; for a value. This box lives within your component and follows two very important rules:</p><ol><li>The value <em>inside</em> the box persists for the entire lifetime of the component.</li><li>Changing the value <em>inside</em> the box <strong>does not</strong> cause the component to re-render.</li></ol><h3>Part 1: Accessing DOM Elements</h3><p>The most common reason to reach for useRef is to get a direct &quot;wire&quot; to a specific element in the browser&#39;s DOM.</p><p>Let’s say we want to build an input field that is automatically focused as soon as the component loads. We need a way to tell the browser, “Hey, put the user’s cursor in <em>that specific</em> input.”</p><p>Here’s how we set it up.</p><h4>Step 1: Declare the Ref</h4><p>First, we import useRef and declare our ref inside the component.</p><pre>import { useRef, useEffect } from &#39;react&#39;;<br><br>function AutoFocusInput() {<br>  const inputRef = useRef&lt;HTMLInputElement&gt;(null);<br>  // ... rest of the component<br>}</pre><p>Let’s break it down:</p><ul><li><strong>&lt;HTMLInputElement&gt;</strong>: This is a TypeScript generic. We are explicitly telling TypeScript, that The &#39;box&#39; this ref creates is meant to hold an HTML input element.</li><li><strong>(null)</strong>: We initialize the ref&#39;s value to null. This is mandatory for DOM refs. Because when this line of code first runs, the component hasn&#39;t rendered yet, so the &lt;input&gt; element doesn&#39;t exist in the DOM. The ref starts empty.</li></ul><h4>Step 2: Attach the Ref to the Element</h4><p>Next, we use the special ref prop in our JSX (or TSX) to attach our inputRef to the &lt;input&gt; element.</p><pre>&lt;input ref={inputRef} type=&quot;text&quot; placeholder=&quot;I will be focused&quot; /&gt;</pre><p>When React renders this JSX, it sees the ref prop and knows, when &lt;input&gt; in the actual DOM is created, that DOM node needs to place it inside the inputRef.current property.</p><h4>Step 3: Use the Ref</h4><p>The “box” that useRef creates is an object with a single, mutable property: <strong>.current</strong>. To access the DOM node, we use inputRef.current.</p><p>Since we want to focus the input right after it mounts, we use a useEffect hook with an empty dependency array.</p><pre>import React, { useRef, useEffect } from &#39;react&#39;;<br><br>function AutoFocusInput() {<br>  const inputRef = useRef&lt;HTMLInputElement&gt;(null);<br>  useEffect(() =&gt; {<br>    <br>    inputRef.current?.focus();<br>  }, []); <br>  <br>  // Empty array means this runs once, after the component mounts.<br>  return &lt;input ref={inputRef} type=&quot;text&quot; placeholder=&quot;I will be focused&quot; /&gt;;<br>}</pre><p>This flow is crucial:</p><ol><li><strong>During the initial render:</strong> inputRef.current is null.</li><li><strong>After the component mounts:</strong> React updates inputRef.current to be the &lt;input&gt; DOM node.</li><li><strong>useEffect runs:</strong> It can now safely access inputRef.current and call the browser&#39;s .focus() method on it.</li></ol><h3>Part 2: Holding Mutable Values</h3><p>The second key job of useRef is storing any value that you want to change <em>without</em> causing a re-render. This is where it differs fundamentally from useState.</p><ul><li>setState(newValue) <strong>triggers a re-render</strong>.</li><li>myRef.current = newValue <strong>does not trigger a re-render</strong>.</li></ul><p>Let’s illustrate this with a classic problem: counting how many times a component renders. If we try to use useState, we create a disaster.</p><h4>The Infinite Loop Problem with useState</h4><pre>import { useState, useEffect } from &#39;react&#39;;<br><br>function RenderCounter() {<br>  const [renderCount, setRenderCount] = useState(0);<br>  useEffect(() =&gt; {<br>    // This runs after every render<br>    setRenderCount(renderCount + 1);<br>  });<br>  return &lt;div&gt;I have rendered {renderCount} times.&lt;/div&gt;;<br>}</pre><p>This code creates an infinite loop. The component renders, useEffect runs, it updates the state with setRenderCount, which triggers a new render, which runs the useEffect again, and so on, forever.</p><h4>The useRef Solution</h4><p>This is the perfect scenario for useRef. Since updating a ref&#39;s .current property doesn&#39;t trigger a re-render, it breaks the loop.</p><pre>import { useRef, useEffect } from &#39;react&#39;;<br><br>function RenderCounter() {<br>  // Create the ref &quot;box&quot;, typed to hold a number<br>  const renderCountRef = useRef&lt;number&gt;(0);<br>  useEffect(() =&gt; {<br>    // Mutate the .current property. <br>    // This does NOT trigger a re-render.<br>    renderCountRef.current = renderCountRef.current + 1;<br>  });<br>  return &lt;div&gt;I have rendered {renderCountRef.current} times.&lt;/div&gt;;<br>}</pre><p>The ref safely tracks the count across renders without causing more re-renders. So you can choose useRef or useState for a value, depending on whether the UI needs to re-render upon the value’s update.</p><h3>Part 3: useRef’s TypeScript Syntax</h3><p>With TypeScript, the syntax for useRef changes slightly depending on whether you&#39;re storing a value or referencing a DOM element.</p><h4>Syntax for DOM Elements (like our &lt;input&gt;):</h4><p>You provide the HTML element type and <strong>must initialize it with </strong><strong>null</strong>.</p><pre>// .current is of type: HTMLDivElement | null<br>const myDivRef = useRef&lt;HTMLDivElement&gt;(null);</pre><p>If you forget to initialize it with null (useRef&lt;HTMLInputElement&gt;()), its .current property will be undefined. React&#39;s ref prop doesn&#39;t work with undefined, so your code won&#39;t connect the ref correctly. Always use null.</p><h4>Syntax for Values (like our render counter):</h4><p>You provide the type and a non-null starting value. TypeScript knows .current will always have that type.</p><pre>// .current will ALWAYS be a number.<br>const countRef = useRef&lt;number&gt;(0);<br><br>// .current will be a number or null.<br>const intervalIdRef = useRef&lt;number | null&gt;(null);</pre><h3>Conclusion</h3><p>useRef is a versatile and essential hook. By understanding its two primary use cases and the specific TypeScript patterns for each, you can write cleaner, more efficient, and bug-free React components.</p><p>Happy Coding!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3789471519bf" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>