<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Omer Davidson</title>
    <description>The latest articles on DEV Community by Omer Davidson (@dodson).</description>
    <link>https://dev.to/dodson</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2299904%2F95fedf81-d671-4563-b336-1be8ce4cbfef.png</url>
      <title>DEV Community: Omer Davidson</title>
      <link>https://dev.to/dodson</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dodson"/>
    <language>en</language>
    <item>
      <title>Let's Design Google Docs</title>
      <dc:creator>Omer Davidson</dc:creator>
      <pubDate>Fri, 14 Aug 2026 15:00:00 +0000</pubDate>
      <link>https://dev.to/dodson/lets-design-google-docs-4468</link>
      <guid>https://dev.to/dodson/lets-design-google-docs-4468</guid>
      <description>&lt;p&gt;Building a Google Docs clone is a great system design exercise. The first version is easy, and then every step after it breaks something we already built.&lt;/p&gt;

&lt;p&gt;The requirement sounds simple. Multiple users editing the same document. Let's see how it goes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The First Version
&lt;/h2&gt;

&lt;p&gt;Our server needs to push. Someone else's keystroke has to show up on our screen without us asking for it, so we are using websockets. That part is obvious and we are not going to spend time on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Happens When We Add a Second Server?
&lt;/h2&gt;

&lt;p&gt;It works fine, until we get some real traffic.&lt;br&gt;
What happens when a single server can't handle the traffic? What if there is an error and the only server we have is down?&lt;/p&gt;

&lt;p&gt;To handle that we deploy multiple servers behind a load balancer.&lt;/p&gt;

&lt;p&gt;Here is our problem. Alice connects and lands on server A. Bob connects and lands on server B. They are both in the same document. Alice types something. Server A looks up who else is in that document, finds only Alice, and forwards her edit to nobody.&lt;/p&gt;

&lt;p&gt;Each server only knows about its own connections, and a document's users are now spread across all of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Redis Pub/Sub
&lt;/h2&gt;

&lt;p&gt;We need our servers to tell each other about edits.&lt;/p&gt;

&lt;p&gt;We could have every server hold a connection to every other server, but then each instance needs to know the current list of instances, and that list changes every time we deploy or autoscale. That is a service discovery problem we don't want to own.&lt;/p&gt;

&lt;p&gt;So instead we put a message bus in the middle, and the usual choice here is Redis Pub/Sub.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Is Redis Pub/Sub?
&lt;/h3&gt;

&lt;p&gt;Redis Pub/Sub is a live broadcast.&lt;/p&gt;

&lt;p&gt;A client subscribes to a channel by name. From that moment, anything anyone publishes to that channel gets pushed down that subscriber's connection. That is the whole feature.&lt;/p&gt;

&lt;p&gt;We let Redis do all the filtering and the server only gets updates that it actually cares about.&lt;/p&gt;

&lt;p&gt;The important part is what happens when nobody is listening. If we publish to a channel with no subscribers, the message is simply gone. Redis does not save it, does not retry it, and does not remember it ever existed. When we publish, all we get back is the number of subscribers that received the message.&lt;/p&gt;

&lt;p&gt;This is called &lt;strong&gt;at most once delivery&lt;/strong&gt;. It is what makes pub/sub fast and simple, since Redis is not writing anything to disk or tracking who acknowledged what. It also means pub/sub can never be our source of truth. It is a way to move messages between our servers, not a place anything is actually kept.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using It for Our Documents
&lt;/h3&gt;

&lt;p&gt;Now we can fix our two server problem. Each document gets it's own Redis pub/sub channel. The server subscribes to the documents it is using. And when an edit comes in it publishes to that channel instead of writing to the local sockets.&lt;/p&gt;

&lt;p&gt;We subscribe when the first local client opens a document and unsubscribe when the last one leaves.&lt;/p&gt;

&lt;p&gt;One thing worth noting. When a server gets an update, it should not deliver it to its own sockets, it should just publish it. Redis will send it right back to the same server (and other subscribed servers) and only then can we start handling it like we handle every update. This saves us from maintaining a handler for local updates and a different handler for external updates.&lt;/p&gt;

&lt;p&gt;Now edits reach everyone, no matter which server they are connected to, and our servers still don't know anything about each other.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two People Typing at the Same Time
&lt;/h2&gt;

&lt;p&gt;We can move messages between any two users now. Unfortunately the messages are wrong.&lt;/p&gt;

&lt;p&gt;Alice and Bob can edit the same text at the same time. Just like in Git, we get a conflict. In Git we give the second editor the choice of how to handle it. Of course we can't do that in a text editor.&lt;/p&gt;

&lt;p&gt;Use a &lt;strong&gt;CRDT&lt;/strong&gt; (Conflict-free Replicated Data Type) and let a library like Yjs or Automerge deal with it.&lt;/p&gt;

&lt;p&gt;What is worth knowing is how that choice changes our architecture. A CRDT gives every character we insert an id that never changes, so an operation becomes "insert this character after that character" instead of "insert at position 5". Those operations &lt;strong&gt;commute&lt;/strong&gt;, which means clients can apply them in any order and still end up with the same document.&lt;/p&gt;

&lt;p&gt;That is a big deal for us. Order is the thing distributed systems usually fight about, and we just stopped caring about it. Our broadcast makes no promises about the order two servers see messages in, and now it doesn't have to. No server has to be the authority on anything, so any server can accept any edit for any document, our fleet stays stateless, and we can deploy and autoscale without thinking about it.&lt;/p&gt;

&lt;p&gt;Commuting does not mean we can lose messages though. Order doesn't matter, but arrival still does. Every copy of the document has to see every operation eventually, and pub/sub is at most once, so sooner or later it drops one. Nobody gets an error when that happens. The two servers just quietly stop agreeing about what the document says. We will fix that in a minute.&lt;/p&gt;

&lt;h2&gt;
  
  
  Saving the Document
&lt;/h2&gt;

&lt;p&gt;Everything so far only exists in memory. Restart a server or lose the last client editing a document and it is gone.&lt;/p&gt;

&lt;p&gt;The obvious fix is to write to our database on every keystroke, but that is not going to work. A fast typist produces five to ten operations a second, and a busy document has a few of those at once. That is a lot of transactional writes for data that gets overwritten again a few milliseconds later.&lt;/p&gt;

&lt;p&gt;What we want is &lt;strong&gt;write behind&lt;/strong&gt;. Edits are applied in memory as they happen, and the expensive database write happens on its own schedule, well behind them.&lt;/p&gt;

&lt;p&gt;So we snapshot the document into our real database every so often. Every N operations, or every few seconds of activity, and also immediately when the last client leaves, since that is the moment the document is about to disappear from memory. Loading is the same thing in reverse. When the first client opens a document nobody else is editing, we read the snapshot back and we are ready to go.&lt;/p&gt;

&lt;p&gt;The tradeoff is right there in the interval. Whatever was typed since the last snapshot only exists in memory, so a crash takes it with it. Snapshot more often and we lose less but write to the database more. It is a number we should pick on purpose instead of ending up with by accident, and we can tighten it for documents that matter and loosen it for scratch pads.&lt;/p&gt;

&lt;p&gt;CRDTs soften this too. Every client is holding its own full copy of the document, so a client that reconnects after a server died can merge what it has back in. The snapshot is our floor, not our only copy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Catching Up
&lt;/h2&gt;

&lt;p&gt;There is still a hole on the way in.&lt;/p&gt;

&lt;p&gt;Carol opens the document and lands on server C. That server has never seen this document before, so its memory is empty, and so is Carol's browser. It subscribes to the channel, which means it now hears every new edit while knowing nothing about the ninety pages that are already there.&lt;/p&gt;

&lt;p&gt;So it reads the snapshot out of the database, and this is where write behind comes back around on us. That snapshot is from the last flush. Alice and Bob have been typing for the five seconds since, and none of that is on disk yet. Carol gets the document as it was five seconds ago, and then live edits start landing on it that were written against text she doesn't have.&lt;/p&gt;

&lt;p&gt;The current version does exist. It is sitting in memory on server A, where Alice is typing. It just hasn't been written down.&lt;/p&gt;

&lt;p&gt;So server C asks for it. It publishes a message on the document's channel saying how much it already has, and server A is subscribed to that channel, so it answers with the rest. No new connections and nothing to discover, we are using the pub/sub that is already there.&lt;/p&gt;

&lt;p&gt;What server C sends is called a &lt;strong&gt;state vector&lt;/strong&gt;, and it is tiny. Each person's operations are numbered in order, so "I have Alice's first four hundred and Bob's first thirty" is a couple of numbers.&lt;/p&gt;

&lt;p&gt;This is not a compressed copy of the document. It is a bookmark. It says how far along we are and nothing whatsoever about what any of those operations were.&lt;/p&gt;

&lt;p&gt;The document itself still has to travel. Server A compares those numbers against its own and sends back the operations that are missing, in full. Cold open and that is the whole document, which was always going to be true. Five seconds behind and it is five seconds of edits instead of ninety pages, which is the case we actually care about.&lt;/p&gt;

&lt;p&gt;Carol then does the same thing with server C over her websocket, and everybody is current.&lt;/p&gt;

&lt;p&gt;This is our dropped message fix too. A server that missed a broadcast is in exactly the same position server C was in, holding a copy that is quietly behind. Same question, same answer. A lost message stops being permanent and becomes something we recover from.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Pub/Sub Becomes the Bottleneck
&lt;/h2&gt;

&lt;p&gt;At a big enough scale, the pub/sub is the thing that starts hurting.&lt;/p&gt;

&lt;p&gt;Every server subscribed to a document's channel gets every message on that channel, and delivering them is work Redis does on its single thread. The fan out multiplies quickly. Two hundred servers each subscribed to five thousand documents is a million subscriptions on one instance.&lt;/p&gt;

&lt;p&gt;It is also all shared, so a single popular document eats Redis time and slows down all of our users. This is the &lt;strong&gt;noisy neighbor&lt;/strong&gt; problem.&lt;/p&gt;

&lt;p&gt;Here is what helps, roughly in the order to try it:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stop publishing on every keystroke.&lt;/strong&gt; Batch operations over a short window, something like twenty to fifty milliseconds, and publish them as one message. Users cannot feel the difference and it can cut our message volume by an order of magnitude on an active document. This one is easy and it should be the first thing we do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Watch out for Redis Cluster.&lt;/strong&gt; This one surprises people. In a normal Redis Cluster, pub/sub messages are broadcast to every node in the cluster, because any client could be subscribed on any node. So adding nodes does not divide our pub/sub load, it multiplies it, and the thing we scaled out to fix gets worse. Redis 7.0 added sharded pub/sub, which sends a channel only to the shard that owns it. Use it, or the cluster migration will be a downgrade.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consider document affinity.&lt;/strong&gt; This is sticky sessions, except we stick on the document id instead of the user id. Every document hashes to one server, every connection for it gets routed there, and that server holds all of its connections in local memory without touching the pub/sub at all. The problem is when a pod restarts and we need to hand off the document and all of its connections.&lt;/p&gt;

&lt;h2&gt;
  
  
  What If Every Document Had Its Own Server?
&lt;/h2&gt;

&lt;p&gt;Let's look back at where all of this actually came from.&lt;/p&gt;

&lt;p&gt;The first version worked. One server, a map of documents, a forwarding loop. It was maybe thirty lines and it was correct. Then we added a second server, and from that moment on every single thing in this article has been damage control. The pub/sub, the channel per document, the affinity routing. None of it is about collaborative editing. All of it is about the fact that our users are spread across machines that don't know about each other.&lt;/p&gt;

&lt;p&gt;So what if they weren't? What if every document had its own server, and everybody editing that document connected to it?&lt;/p&gt;

&lt;p&gt;That solves everything. There is nothing to broadcast, because all the sockets are already in one place. There is nobody to coordinate with, because there is nobody else. We would just go back to the version that worked.&lt;/p&gt;

&lt;p&gt;The reason we don't do this is that it sounds insane. A server per document means a server for every meeting note and every grocery list anyone ever typed. That is millions of processes, each holding an operating system's worth of overhead, and almost all of them sitting idle at any given moment. Nobody is provisioning a container per document.&lt;/p&gt;

&lt;p&gt;But that objection is about the cost of a server, not about the idea. So what happens if the thing we spin up per document gets small enough and cheap enough?&lt;/p&gt;

&lt;h3&gt;
  
  
  V8 Isolates
&lt;/h3&gt;

&lt;p&gt;This is what Cloudflare Workers are built on.&lt;/p&gt;

&lt;p&gt;Most serverless platforms give each application a container or a micro VM. That is a real machine boundary, which means real memory overhead and a real startup cost, usually somewhere between a few hundred milliseconds and a few seconds. It is why cold starts are something we have to design around.&lt;/p&gt;

&lt;p&gt;Workers don't do that. They run our code in a &lt;strong&gt;V8 isolate&lt;/strong&gt;, which is the same mechanism Chrome uses to keep one tab's JavaScript from touching another tab's. An isolate is a sandbox inside a process rather than a process of its own, so a single process can host thousands of them at once. There is no container to pull, no OS to boot, and no runtime to initialize per instance.&lt;/p&gt;

&lt;p&gt;The numbers are what matter here. An isolate starts in a couple of milliseconds and carries a few megabytes of overhead instead of a few hundred. Cloudflare can even start ours while the TLS handshake is still finishing, so in practice the cold start disappears into time we were already spending.&lt;/p&gt;

&lt;p&gt;That completely changes what "one per document" costs. A million containers is a fantasy. A million isolates, mostly idle, is just Tuesday.&lt;/p&gt;

&lt;h3&gt;
  
  
  Durable Objects
&lt;/h3&gt;

&lt;p&gt;A Worker on its own is stateless, so it isn't enough for us yet. It has no identity and no memory between requests.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;Durable Object&lt;/strong&gt; is the piece that adds both. It is a Worker with a name and with storage attached to it, and Cloudflare guarantees that for a given name, exactly one instance of it is alive anywhere in the world at a time. Every request for that name is routed to that one instance, wherever it came from.&lt;/p&gt;

&lt;p&gt;So we derive the object's name from the document id. Now every client editing a document connects to the same object, because there is no other object to connect to. We got our server per document.&lt;/p&gt;

&lt;p&gt;Look at what that deletes. There is no pub/sub, because all the sockets are already in the same place. There is no load balancer to make sticky and no affinity routing to build, because routing by name is the platform's job. And storage is attached to the object and lives on the same machine, so a snapshot is a local write instead of a background job reaching across the network to a shared database, which means we can afford to do it far more often and lose far less.&lt;/p&gt;

&lt;p&gt;Server C never has to ask server A for the difference either, because there is no server C. Carol still has to be handed the ninety pages when she opens the document, but the current version is either in the object's memory or in its own storage, so there is nobody to ask and nothing that can quietly fall behind.&lt;/p&gt;

&lt;p&gt;Our whole diagram collapses back into the first version we wrote.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why This Scales Better
&lt;/h3&gt;

&lt;p&gt;The surprising part is that this is not a step backwards. Going from many servers to one server per document sounds like giving up on scale, and it is the opposite.&lt;/p&gt;

&lt;p&gt;Our Redis design has a shared bottleneck in the middle. Every document's traffic goes through the same pub/sub, so documents compete with each other for it, and the way we scale is by splitting that pub/sub into more pieces. With Durable Objects there is nothing in the middle to contend on. Documents are independent by construction, so a million documents is a million tiny objects instead of a million subscriptions on one Redis instance. Adding documents adds capacity instead of consuming it.&lt;/p&gt;

&lt;p&gt;That also kills the noisy neighbor problem, since a popular document is now its own object burning its own CPU with no shared pub/sub to slow down for everybody else.&lt;/p&gt;

&lt;p&gt;We also stop paying for documents nobody is using. With websocket hibernation, an object that has open connections but no traffic is evicted from memory while those connections stay alive, and it wakes back up when a message arrives. Since starting an isolate costs a couple of milliseconds, waking up is cheap enough to do constantly. Most documents are idle most of the time, and now that costs us nothing.&lt;/p&gt;

&lt;p&gt;And because objects are created near whoever first used them, a document a team in Berlin works on lives in Berlin, instead of in whichever region we happened to pick for our cluster.&lt;/p&gt;

&lt;h3&gt;
  
  
  What It Doesn't Solve
&lt;/h3&gt;

&lt;p&gt;It does not solve concurrent editing for us.&lt;/p&gt;

&lt;p&gt;It is tempting to think it does, since the object is single threaded and handles one message at a time, so every edit passes through one place in a definite order. But an order is not a resolution. Alice and Bob both wrote their edit at the same time.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Tradeoffs
&lt;/h3&gt;

&lt;p&gt;Deleting most of an architecture is not something we get for free. Here is what we are paying for it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We are marrying a platform.&lt;/strong&gt; Durable Objects are a Cloudflare product. The programming model, the storage API and the deployment story all come from them, so if we ever have to leave we are not migrating, we are rebuilding.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It is not Node.&lt;/strong&gt; Workers run in a V8 isolate with web standard APIs, not in a Node process. There is no filesystem, and a good chunk of npm assumes Node built ins that aren't there. There is a compatibility layer that covers a lot of it now, but "does our stack actually run on this" is a question we have to answer before we design around it, not after. For our case we are fine, since a CRDT library is pure JavaScript, but that is luck rather than a general rule.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One object is one thread.&lt;/strong&gt; This is the same hot document problem as before, except now we cannot route our way out of it. The object is the unit, so a document with a thousand people typing in it has one single threaded process handling all of them, and no amount of scaling helps. It is the noisy neighbor fix seen from the other side, since nothing leaks out of an object and nothing can be spread out of one either. Worth noting that Google Docs caps simultaneous editors at around a hundred and sends everyone else to view only, so this limit is not unique to us. But we should know where our ceiling is instead of discovering it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One instance is also one point of failure.&lt;/strong&gt; The guarantee that exactly one object is alive is what makes the whole thing work, and it means there is no replica standing by. While that object is restarting, migrating between machines, or picking up a deploy, that document is unavailable and its sockets drop. It is usually brief, and clients reconnecting cover it, but our availability story for a single document is now "one thing, and it comes back quickly" rather than "any server can serve it".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Location is decided once.&lt;/strong&gt; The object is created near whoever opened the document first, and that is where it stays. That is great for a team in one city and less great for a team split between Berlin and Sydney, where somebody is always paying the round trip. We can give placement hints, but there is still exactly one location, and the first user picks it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The cost model is different.&lt;/strong&gt; We stop paying for idle servers and start paying per object, per request, and for how long objects stay awake. Hibernation makes idle documents genuinely cheap, which is the main thing. But it is a different shape of bill than "we already have some VMs and a Redis", and it is worth modelling against our actual usage before assuming it is cheaper.&lt;/p&gt;

&lt;p&gt;None of these are dealbreakers for a collaborative editor. The point is that we traded a pile of infrastructure we control for a primitive we don't, and that is a trade with a direction. It is the right one surprisingly often, but it should be a decision.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running It Ourselves
&lt;/h3&gt;

&lt;p&gt;The vendor lock in argument took a real hit this month, when Ryan Dahl released &lt;strong&gt;&lt;a href="https://celld.dev/" rel="noopener noreferrer"&gt;celld&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;celld is an open source implementation of Workers and Durable Objects that we run on our own machines. It is Apache 2.0, it takes the same Wrangler bundles and config, and it implements the same JavaScript APIs, so the code we wrote for Cloudflare is the code that runs here.&lt;/p&gt;

&lt;p&gt;It is also extremely new, and the limitations are real. This is Ryan Dahl's third JavaScript runtime after Node and Deno, So it is very promising.&lt;/p&gt;

&lt;p&gt;So this is not what we build our editor on this quarter. But it does change what the lock in tradeoff means. Durable Objects are starting to look less like one vendor's product and more like a shape that other people can implement, and that is a much better position for us to be standing in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Let's put the whole thing together.&lt;/p&gt;

&lt;p&gt;A stateless websocket fleet behind a load balancer, each server holding its own local sockets and subscribing to a Redis channel per document. Edits are CRDT operations, which commute, so nobody has to be the authority on ordering and every server stays interchangeable. A connecting client tells the server what it already has and gets back the rest, which is also how a server recovers anything the pub/sub dropped. Documents live in memory and get snapshotted on an interval and when the last client leaves, and whatever was typed since then is what a crash costs us. When the pub/sub hurts, we batch, we shard it, and eventually we pin each document to one server.&lt;/p&gt;

&lt;p&gt;That last fix is the tell. All of it exists because the sockets for one document are scattered across machines that don't know about each other. Durable Objects remove the scattering instead of compensating for it, and the bill is a platform we don't own and one thread per document.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>scalability</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Why you should cancel your HTTP requests</title>
      <dc:creator>Omer Davidson</dc:creator>
      <pubDate>Tue, 15 Apr 2025 20:05:43 +0000</pubDate>
      <link>https://dev.to/dodson/why-you-should-cancel-your-http-requests-23pj</link>
      <guid>https://dev.to/dodson/why-you-should-cancel-your-http-requests-23pj</guid>
      <description>&lt;p&gt;&lt;em&gt;originaly posted at: &lt;a href="https://www.omerdavidson.dev/blog/why-you-should-cancel-your-http-requests/" rel="noopener noreferrer"&gt;my personal website&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Have you ever built an autocomplete feature that felt slow or returned unexpected results? This often happens because of the way browsers handle HTTP requests. Here's a simple autocomplete example you might have implemented:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Autocomplete&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setResults&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/api/search?q=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nf"&gt;setResults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why This is a Problem
&lt;/h2&gt;

&lt;p&gt;Browsers using HTTP 1.1 typically only allow 6 simultaneous requests to the same domain. With an autocomplete feature, each keystroke triggers a new request. If a user types quickly, you'll quickly hit this limit. This can cause:&lt;/p&gt;

&lt;p&gt;Slow performance: Your autocomplete becomes slow because new requests have to wait for previous ones to finish.&lt;/p&gt;

&lt;p&gt;Incorrect results (race conditions): Older requests that finish later might overwrite newer, more relevant results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simple Solution: Canceling Requests
&lt;/h2&gt;

&lt;p&gt;The good news is, browsers let you cancel ongoing requests. You can easily add this capability to your autocomplete component using an AbortController. Here's how you do it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Autocomplete&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setResults&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AbortController&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchResults&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/api/search?q=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nf"&gt;setResults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AbortError&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;fetchResults&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, each new query cancels the previous request, ensuring only the latest data updates your component. Your autocomplete feature will feel faster and won't have any race conditions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The HTTP/2 solution
&lt;/h2&gt;

&lt;p&gt;HTTP/2 can alleviate this by multiplexing multiple requests over a single TCP connection, greatly increasing concurrency. However, it's not the default everywhere yet, and many systems still rely&amp;nbsp;on&amp;nbsp;HTTP&amp;nbsp;1.1.&lt;br&gt;&lt;br&gt;
Even when using HTTP/2 you should still cancel your HTTP requests to avoid race conditions.&lt;/p&gt;
&lt;h2&gt;
  
  
  Handling Cancellations on Your Server
&lt;/h2&gt;

&lt;p&gt;If you want to go the extra mile, you can also handle canceled requests on your backend.&lt;/p&gt;

&lt;p&gt;Here's an example using Express:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/search&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dbOperation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;startDbSearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;close&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writableEnded&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;dbOperation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Request canceled by client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;dbOperation&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Operation canceled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Server running on port 3000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By stopping expensive operations like DB queries you can reduce server load.&lt;/p&gt;

&lt;p&gt;understanding and implementing request cancellation, you significantly enhance the responsiveness and efficiency of your web applications.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>http</category>
      <category>react</category>
      <category>performance</category>
    </item>
    <item>
      <title>5 easy ways to elevate your website</title>
      <dc:creator>Omer Davidson</dc:creator>
      <pubDate>Wed, 11 Dec 2024 20:49:29 +0000</pubDate>
      <link>https://dev.to/dodson/5-easy-fixes-to-your-website-31ln</link>
      <guid>https://dev.to/dodson/5-easy-fixes-to-your-website-31ln</guid>
      <description>&lt;p&gt;I see these 5 mistakes made all the time, even by experienced developers. And they are so easy to fix once you start to look out for them.&lt;/p&gt;

&lt;h1&gt;
  
  
  1. Prevent layout shifts on overflow
&lt;/h1&gt;

&lt;p&gt;If you have an element with &lt;code&gt;overflow: auto&lt;/code&gt; then it will have a scrollbar only when the element is overflowing. The problem is that once the element is overflowing and the scrollbar appears, the content has shrunk to accommodate the width of the scrollbar.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5wywp243kn43xm76vx8c.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5wywp243kn43xm76vx8c.gif" alt="Image description" width="480" height="854"&gt;&lt;/a&gt;&lt;br&gt;
To avoid the unnecessary layout shift, add:&lt;br&gt;
&lt;code&gt;scrollbar-gutter: stable&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It will reserve space for the scrollbar even if it is not visible.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vv98mnlgwbxovs3xx0g.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vv98mnlgwbxovs3xx0g.gif" alt="Image description" width="480" height="854"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the time of writing this, only 74% of users have this feature. But it is a nice progressive enhancement. Meaning those on newer browser can enjoy a better user experience while those that are on older browsers are not affected.&lt;/p&gt;
&lt;h1&gt;
  
  
  2. respect the device's preference for dark mode
&lt;/h1&gt;

&lt;p&gt;If you already implemented dark mode in your website, you can spare the user from selecting dark mode manually by checking it's device's preference for light or dark mode&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matchMedia&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;matchMedia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(prefers-color-scheme: dark)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// dark mode&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So if the user already selected dark mode in it's settings, then you can have the default value be dark mode in your website as well.&lt;/p&gt;

&lt;p&gt;Some websites even choose to not have a dark mode toggle at all and rely solely on the device's preference.&lt;/p&gt;

&lt;h1&gt;
  
  
  3. Links should be real links
&lt;/h1&gt;

&lt;p&gt;When you have a button that should redirect to a different part of your website, the obvious way to do it is to have an event listener for click and to redirect the user using JavaScript. &lt;br&gt;
This is wrong, whenever you can use a browser primitive (eg: link, form) then you should almost always use that instead.&lt;/p&gt;

&lt;p&gt;using &amp;lt;a&amp;gt; tag instead has a lot of benefits.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Being able to Ctrl click (or long press on mobile) to open the link in a different tab&lt;/li&gt;
&lt;li&gt;Hovering over the link shows you where you will be redirected to&lt;/li&gt;
&lt;li&gt;Other programs like screen readers and search engine crawlers will work a lot better&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are using react-router or Next.js than you should use that framework's Link component to prevent full page reloads.&lt;/p&gt;
&lt;h1&gt;
  
  
  4. Link previews
&lt;/h1&gt;

&lt;p&gt;When a user shares a link to your website, all chat and social media apps have a preview feature to see some of the content of that website before the user clicks it. By simply adding a couple of meta tags to your &amp;lt;head&amp;gt; object you will allow other apps to show previews to your website.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;The Rock (1996)&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:title"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"The Rock"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:type"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"video.movie"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:url"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"https://www.imdb.com/title/tt0117500/"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:image"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"https://ia.media-imdb.com/images/rock.jpg"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
...
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmngefbtmf6dnjkl5ypy0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmngefbtmf6dnjkl5ypy0.jpg" alt="Image description" width="800" height="334"&gt;&lt;/a&gt;&lt;br&gt;
And in &lt;a href="https://react.dev/reference/react-dom/components/meta#annotating-the-document-with-metadata" rel="noopener noreferrer"&gt;react 19&lt;/a&gt; it is easier than ever to edit your &amp;lt;head&amp;gt; object. You previously had to use third party libraries like &lt;code&gt;react-helmet&lt;/code&gt; but now it is built in to react.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;BlogPost&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"description"&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;excerpt&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"og:title"&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"og:description"&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;excerpt&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"og:image"&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"og:url"&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"og:type"&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"article"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is called the &lt;a href="https://ogp.me/" rel="noopener noreferrer"&gt;Open Graph Protocol&lt;/a&gt;.&lt;br&gt;
A very useful tool is &lt;a href="https://socialsharepreview.com/" rel="noopener noreferrer"&gt;social share preview&lt;/a&gt; which lets you test how your preview will look in different websites and give you advice on how to make it better.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that this works best with server side rendering as most social media apps won't run JavaScript to get your link preview.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;
  
  
  5. Use lables with your inputs
&lt;/h1&gt;

&lt;p&gt;I often see checkboxes and when I try to click the checkbox's label nothing happens. Aside from poor accessibility, this means the user has to click the tiny checkbox to select it.&lt;br&gt;&lt;br&gt;
To solve this use the &amp;lt;label&amp;gt; tag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Click me to select the checkbox
&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works for all input types. Clicking the label of a text input for example will focus into the textbox.&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
      <category>css</category>
      <category>ux</category>
    </item>
    <item>
      <title>A brief history of ES modules</title>
      <dc:creator>Omer Davidson</dc:creator>
      <pubDate>Sat, 16 Nov 2024 18:47:20 +0000</pubDate>
      <link>https://dev.to/dodson/a-brief-history-of-es-modules-2fld</link>
      <guid>https://dev.to/dodson/a-brief-history-of-es-modules-2fld</guid>
      <description>&lt;p&gt;You've probably used ES modules in modern JavaScript development, but do you know the history behind their evolution? Understanding the journey from early JavaScript practices to today’s module system will help you appreciate how far we've come and why ES modules are a game changer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Early Days: JavaScript's Beginnings
&lt;/h2&gt;

&lt;p&gt;The year is 1995, four years after the first web page was created. Most websites were simple — static pages with text and minimal interactivity. However, developers were quickly looking for ways to make web pages more dynamic.&lt;/p&gt;

&lt;p&gt;In this environment, Netscape (the dominant web browser at the time) hired Brendan Eich to create a scripting language that would run directly in the browser. This led to the birth of JavaScript, a language designed to be simple and accessible, especially for non-programmers like web designers. And so he did, completing the first version in &lt;strong&gt;10 days&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Originally, JavaScript was meant to add small enhancements to web pages, like form validation, without needing to round-trip data to the server. However, as websites became more interactive, JavaScript quickly grew beyond its original purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Global Scope and Early Workarounds
&lt;/h2&gt;

&lt;p&gt;In the early days, all JavaScript code lived in the global scope. As more developers added code to the same page, the risk of name collisions grew. If multiple scripts used the same variable or function name, the code could break in unexpected ways.&lt;/p&gt;

&lt;p&gt;To manage this, developers resorted to naming conventions to prevent collisions. If a piece of code was meant to run only once, developers often wrapped it in an IIFE (Immediately Invoked Function Expression). This kept functions and variables scoped within the function, preventing them from polluting the global namespace.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was good enough at the time as most websites were server side rendered with very little client side logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  CommonJS: Server-side modules
&lt;/h2&gt;

&lt;p&gt;In 2008 Ryan Dahl created Node.js, a JavaScript runtime for building server side applications. This opened up a whole new world of possibilities, but the lack of a module system meant that developers struggled to manage large codebases.&lt;/p&gt;

&lt;p&gt;In 2009, CommonJS was introduced to solve this problem for the server-side. The CommonJS module system allowed developers to define modules, expose functionality, and import other modules. Here's an example of how it worked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;math&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./math&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;subtract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;subtract&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;subtract&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With CommonJS, each file is treated as its own module, and modules are imported using the &lt;code&gt;require&lt;/code&gt; function and exported using &lt;code&gt;module.exports&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Some key features of CommonJS include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The file extension is optional when requiring a module (e.g., require('./math') automatically looks for math.js).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Module loading is synchronous, meaning the program waits for the module to load before continuing execution.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Later in the article we will see why Ryan Dahl admitted to regretting these 2 design decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  AMD: Optimizing the Browser
&lt;/h2&gt;

&lt;p&gt;Around the same time, another module system called AMD (Asynchronous Module Definition) was developed. While CommonJS was primarily focused on server-side JavaScript, AMD was designed to handle client-side JavaScript in the browser.&lt;/p&gt;

&lt;p&gt;The key feature of AMD was its ability to load modules asynchronously. This allowed the browser to load only the JavaScript needed for a page at any given time, improving performance by reducing initial page load time. It also solved issues related to dependency resolution, ensuring that a module would only run once its dependencies had finished loading.&lt;/p&gt;

&lt;p&gt;AMD's benefits included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Smaller JavaScript files loaded on-demand.&lt;/li&gt;
&lt;li&gt;Fewer page load errors due to more predictable module loading.&lt;/li&gt;
&lt;li&gt;Performance optimization through asynchronous loading.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the rise of npm in 2010 (a package manager for server-side JavaScript), the need for sharing code across the browser and the server became apparent. Enter Browserify, a tool that allowed developers to use CommonJS modules in the browser by transforming the code to be compatible with the browser’s environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  UMD: The Best of Both Worlds... Sort Of
&lt;/h2&gt;

&lt;p&gt;With the 2 competing standards, CommonJS and AMD. There was a need for a single module system that could work everywhere without the need for a build step. And in 2011, the universal module definition(UMD) was introduced.&lt;/p&gt;

&lt;p&gt;UMD combined the best of both worlds, allowing developers to write a module that could run in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js (using CommonJS).&lt;/li&gt;
&lt;li&gt;Browsers (using AMD).&lt;/li&gt;
&lt;li&gt;The global scope (if neither CommonJS nor AMD was present).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;UMD became very popular amongst library authors with notable libraries like Lodash, Underscore.js, Backbone.js, and Moment.js adpoting it. However, UMD had some significant downsides. While it solved the compatibility problem, it came with the complexity of managing both systems, and it inherited the problems of both AMD and CommonJS.&lt;/p&gt;

&lt;h2&gt;
  
  
  ES modules: The ultimate modules standard
&lt;/h2&gt;

&lt;p&gt;In 2015, ES Modules (ESM) were introduced as part of the ECMAScript standard, finally offering a native module system for JavaScript. By 2017, all major browsers supported ES modules, and in 2020, Node.js added support as well.&lt;/p&gt;

&lt;p&gt;Let's see why ES modules are the best:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Convenient Syntax
&lt;/h3&gt;

&lt;p&gt;The following UMD code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;define&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;define&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// AMD&lt;/span&gt;
    &lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;([],&lt;/span&gt; &lt;span class="nx"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// CommonJS&lt;/span&gt;
    &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Global&lt;/span&gt;
    &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;can now be reduced to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To be fair, no one actually wrote UMD like that. They used tools like umdify to generate that code. But with ES modules being built in we can skip the build step and have a smaller bundle size.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Better Optimization
&lt;/h3&gt;

&lt;p&gt;ES modules are static, meaning that tools can analyze the code structure at compile time to determine which code is being used and which is not. This allows for tree shaking, where unused code is removed from the final bundle.&lt;/p&gt;

&lt;p&gt;Because CommonJS and AMD modules are dynamic (evaluated at runtime), tree shaking is much less efficient with these systems, often resulting in larger bundles.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. More Explicit Code
&lt;/h3&gt;

&lt;p&gt;When importing a module with CommonJS, specifiying the file extension is optional.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// load math.js file&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;math&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./math&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what does math actually refer to? Is it a JavaScript file? A JSON file? An index.js file inside a math directory?&lt;/p&gt;

&lt;p&gt;When using static analysis tools like ES Lint,typescript or prettier every require becomes a guessing game.&lt;br&gt;
is it math.js?&lt;br&gt;
is it math.jsx?&lt;br&gt;
is it math.cjs?&lt;br&gt;
is it math.mjs?&lt;br&gt;
is it math.ts?&lt;br&gt;
is it math.tsx?&lt;br&gt;
is it math.mts?&lt;br&gt;
is it math.cts?&lt;br&gt;
is it math/index.js?&lt;br&gt;
is it math/index.jsx?&lt;/p&gt;

&lt;p&gt;You get the idea.&lt;/p&gt;

&lt;p&gt;Reading a file is expensive. It is a lot less performant than reading from memory. Importing &lt;code&gt;math/index.js&lt;/code&gt; results in 9 IO operations instead of 1! And this guessing game is slowing down our tooling and hurts developer experience.&lt;/p&gt;

&lt;p&gt;In ES modules we avoid this mess by having file extensions be mandatory.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Asynchronous Loading
&lt;/h3&gt;

&lt;p&gt;Unlike CommonJS, which loads modules synchronously (blocking the entire process until the module is loaded), ES modules are asynchronous. This allows JavaScript to continue executing while the module is being loaded in the background, improving performance — particularly in environments like Node.js.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migration Challenges: Why It Took So Long
&lt;/h2&gt;

&lt;p&gt;Despite the clear benefits, adopting ES modules was no simple task. Here’s why the transition took so long:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimgs.xkcd.com%2Fcomics%2Fstandards_2x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimgs.xkcd.com%2Fcomics%2Fstandards_2x.png" title="A very relevant xkcd about standards" alt="A comic about standards" width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Costly Migration
&lt;/h3&gt;

&lt;p&gt;Switching from CommonJS to ES modules was not a trivial change, especially for large projects. The syntax differences, combined with the need for tooling support, made migration a significant effort.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Lack of Node.js Support
&lt;/h3&gt;

&lt;p&gt;It took node.js 5 years to fully support ES modules. During this time, developers had to maintain compatibility with both CommonJS (on the server) and ES modules (on the browser). This dual support created a lot of friction in the ecosystem.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Compatability issues
&lt;/h3&gt;

&lt;p&gt;Even after Node.js added support for ES modules, CommonJS modules couldn’t load ES modules. While ES modules could load CommonJS modules, the two systems weren’t fully interoperable, creating additional headaches for package authors who had to support both systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Future: ES Modules Are Here to Stay
&lt;/h2&gt;

&lt;p&gt;The future of JavaScript modules is bright, and here are a few key developments that will make ES modules the dominant system moving forward:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Node.js 23
&lt;/h3&gt;

&lt;p&gt;In Node.js 23 we finally have the ability to load ES modules from CommonJS.&lt;br&gt;
There is a small caveat: ES module that use top-level await  cannot be imported into CommonJS, since await can only be used in asynchronous functions, and CommonJS is synchronous.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. JSR (JavaScript Registry)
&lt;/h3&gt;

&lt;p&gt;A new javascript package registry that competes with npm. It has a lot of advantages over npm which I won't go over here. But the interesting thing is you are only allowed to upload ES modules packages. No need for supporting the old standards.&lt;/p&gt;

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

&lt;p&gt;The journey from global scope hacks to modern ES modules has transformed how we structure JavaScript. After years of experimenting with CommonJS, AMD, and UMD, ES modules have emerged as the clear standard, offering simpler syntax, better optimization, and improved performance.&lt;/p&gt;

&lt;p&gt;While migrating to ES modules has been challenging, especially with Node.js support and ecosystem compatibility, the benefits are undeniable. With Node.js 23 improving interoperability and new tools like JSR promoting a unified module system, ES modules are set to become the default for JavaScript.&lt;/p&gt;

&lt;p&gt;As we continue to embrace ES modules, we can look forward to cleaner, faster, and more maintainable code, marking a new era of modularity in JavaScript development.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>node</category>
      <category>javascript</category>
      <category>npm</category>
    </item>
    <item>
      <title>A useState performance tip you may not have known</title>
      <dc:creator>Omer Davidson</dc:creator>
      <pubDate>Tue, 29 Oct 2024 18:11:30 +0000</pubDate>
      <link>https://dev.to/dodson/a-usestate-performance-tip-you-may-not-have-known-3nfc</link>
      <guid>https://dev.to/dodson/a-usestate-performance-tip-you-may-not-have-known-3nfc</guid>
      <description>&lt;p&gt;Let's say we have a react component with a useState inside it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;expensiveCalculation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;AboutPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;expensiveCalculation&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We initiate the state with the result of the &lt;code&gt;expensiveCalculation&lt;/code&gt; function.&lt;br&gt;
Every time the component re-renders, the function &lt;code&gt;expensiveCalculation&lt;/code&gt; will run even though we only need it's result as the initial value of useState. The function's result will not be used.&lt;/p&gt;

&lt;p&gt;To avoid the expensive calculation during re-renders, pass the function itself without calling it. react is smart enough to invoke the function itself on mount and not every render.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;expensiveCalculation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;AboutPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expensiveCalculation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>react</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>webperf</category>
    </item>
  </channel>
</rss>
