<?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: Ali Morshedzadeh</title>
    <description>The latest articles on DEV Community by Ali Morshedzadeh (@xenral).</description>
    <link>https://dev.to/xenral</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2914290%2Fae98d84e-e7bc-4536-ad28-8837733c3022.jpeg</url>
      <title>DEV Community: Ali Morshedzadeh</title>
      <link>https://dev.to/xenral</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/xenral"/>
    <language>en</language>
    <item>
      <title>Never Miss an Error: Get Node.js Logs and Alerts on Your Phone via Telegram</title>
      <dc:creator>Ali Morshedzadeh</dc:creator>
      <pubDate>Tue, 01 Apr 2025 15:26:09 +0000</pubDate>
      <link>https://dev.to/xenral/never-miss-an-error-get-nodejs-logs-and-alerts-on-your-phone-via-telegram-1ic4</link>
      <guid>https://dev.to/xenral/never-miss-an-error-get-nodejs-logs-and-alerts-on-your-phone-via-telegram-1ic4</guid>
      <description>&lt;p&gt;In modern Node.js and Next.js applications, robust logging is &lt;strong&gt;essential&lt;/strong&gt; for more than just debugging – it's key to maintaining healthy, high-performing production systems. Proper logs allow you to quickly identify issues (error tracking), monitor performance metrics, and understand application behavior in real time. For example, if a critical error occurs on your server, you want to know immediately; relying on only basic logs or delayed email reports isn't enough. Real-time alerts (like a message to your phone) can notify you of critical failures instantly, so you can react before minor issues escalate. In short, advanced logging and alerting give development teams the visibility and response time needed to keep applications running smoothly even as they grow in complexity.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Drawbacks of Using &lt;code&gt;console.log&lt;/code&gt; in Production
&lt;/h2&gt;

&lt;p&gt;Many developers start with simple &lt;code&gt;console.log&lt;/code&gt; statements for debugging, but &lt;strong&gt;traditional logging&lt;/strong&gt; via the console quickly shows its limitations in production environments. The Node.js console provides no concept of log severity or filtering – everything is just printed to stdout or stderr with minimal formatting. This means you can't easily distinguish an error from an info message, and there's no built-in way to turn off verbose logs or focus on critical ones. The output is unstructured (often just plain text), lacking standardized timestamps or JSON formatting, which makes it hard to parse or search when troubleshooting issues across many log lines.&lt;/p&gt;

&lt;p&gt;Furthermore, &lt;code&gt;console.log&lt;/code&gt; has no mechanism for &lt;strong&gt;remote or real-time alerts&lt;/strong&gt; – it's purely local output. If something goes wrong in production, you're relying on manually checking log files or terminal output after the fact. There's no immediate notification to your team or integration with monitoring tools. Another challenge is that sprinkling &lt;code&gt;console.log&lt;/code&gt; everywhere can clutter your code and even impact performance (especially if left in production code paths), since writing to console is synchronous and can be slow when logging extensively. Overall, while &lt;code&gt;console.log&lt;/code&gt; is fine for quick local debugging, it’s &lt;em&gt;“wholly unsuitable for serious production logging”&lt;/em&gt; without features like log levels, structured output, and customizable destinations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing LogPro: A Modern Node.js Logging Package for TypeScript
&lt;/h2&gt;

&lt;p&gt;To address these logging challenges, &lt;strong&gt;LogPro&lt;/strong&gt; comes into play as a modern solution. LogPro is a minimalistic, structured logging library built for Node.js applications written in TypeScript. It is designed from the ground up to provide a &lt;strong&gt;production-grade logging&lt;/strong&gt; experience out-of-the-box, going far beyond &lt;code&gt;console.log&lt;/code&gt; in capability. With LogPro, developers get a clean API for sending structured log events and the flexibility to route those logs to various outputs – including real-time channels like Telegram.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How LogPro Solves Common Logging Problems:&lt;/strong&gt; LogPro brings features that directly tackle the shortcomings of traditional logging. It implements multiple log levels (so you can differentiate debug information from errors and fatal issues), formats logs as structured JSON for easy analysis, and allows attaching contextual metadata to every log (so you know &lt;em&gt;which&lt;/em&gt; user or request an error pertains to, for instance). It also automatically adjusts its format and detail based on the environment – for example, you can have colorful, readable logs during development and compact JSON logs in production. Crucially, LogPro supports &lt;strong&gt;custom transports&lt;/strong&gt; – meaning you can send logs to external services. One of its standout capabilities is the ability to send important log messages directly to &lt;strong&gt;Telegram&lt;/strong&gt;, enabling instant alerts for critical events. In short, LogPro aims to be a one-stop logging package for Node.js/Next.js developers who need more power and polish in their logging system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Features of &lt;a href="https://github.com/xenral/LogPro" rel="noopener noreferrer"&gt;LogPro&lt;/a&gt; for Production-Grade Logging
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/xenral/LogPro" rel="noopener noreferrer"&gt;LogPro&lt;/a&gt; offers a suite of features tailored for professional logging needs in Node.js and Next.js apps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Structured JSON logging&lt;/strong&gt; – Logs are output in JSON format for easy parsing and analysis by log management systems. This means each log entry can include fields like timestamp, level, message, and context in a machine-readable way. No more scraping unstructured text to extract details.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiple log levels&lt;/strong&gt; – Support for levels like &lt;code&gt;debug&lt;/code&gt;, &lt;code&gt;info&lt;/code&gt;, &lt;code&gt;warn&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt;, and &lt;code&gt;fatal&lt;/code&gt; to categorize logs by severity. You can filter or suppress lower-priority messages in production, while still logging verbosely during development. This ensures critical errors surface when it matters, without drowning in noise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contextual data support&lt;/strong&gt; – Ability to attach metadata (context) to logs consistently. For example, you might include a user ID, request ID, or other relevant info with each log. LogPro makes it easy to pass a context object that will be merged into the log output, so you have richer information for debugging and auditing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment-aware configuration&lt;/strong&gt; – The logger can adjust automatically based on &lt;code&gt;NODE_ENV&lt;/code&gt;. For instance, in development you might get human-friendly output with colors and full debug details, while in production LogPro defaults to JSON format and a higher log level (e.g. only &lt;code&gt;info&lt;/code&gt; and above). This adaptive behavior means you get the right balance of verbosity vs. clarity depending on where your app is running.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Human-readable formatting (for dev)&lt;/strong&gt; – In non-production environments, LogPro can pretty-print logs with colors and indentation. This improves readability during local development or testing, making it easier to spot issues without manually parsing JSON.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero dependencies core&lt;/strong&gt; – The core logging functionality has no external runtime dependencies. This keeps the library lightweight and performance-friendly. (Transports for optional features are add-ons, so if you don't use Telegram or others, you aren't paying a cost for them.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extensible transports (e.g., Telegram)&lt;/strong&gt; – LogPro supports adding custom transports to send logs to various destinations. Out of the box, it provides a Telegram transport that can push selected log messages to a Telegram chat or channel. This is extremely useful for setting up real-time alerts: for example, you can configure LogPro to send all errors or critical issues to your team's Telegram group instantly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All these features make LogPro a powerful &lt;strong&gt;Node.js logging package&lt;/strong&gt; for serious applications. It's especially appealing to TypeScript developers, as it is written in TypeScript and provides type definitions, ensuring type-safe configuration and usage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up LogPro in a Node.js/Next.js Project (TypeScript)
&lt;/h2&gt;

&lt;p&gt;Getting started with LogPro is straightforward. Let's walk through how to add LogPro to a Node.js or Next.js application and use it to improve your logging.&lt;/p&gt;

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

&lt;p&gt;First, install the LogPro package (available on npm under &lt;code&gt;@xenral/logpro&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @xenral/logpro
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(If you prefer Yarn: &lt;code&gt;yarn add @xenral/logpro&lt;/code&gt;.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Because LogPro is built with TypeScript, you'll get type definitions automatically with the package.&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic Usage in TypeScript
&lt;/h3&gt;

&lt;p&gt;After installing, you can import the logger and start logging with different levels right away. In a Next.js app, you might do this in an API route or any server-side code. In a plain Node.js app, you might set this up in your main server file. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&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;logger&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;@xenral/logpro&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Basic logging usage&lt;/span&gt;
&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Application started&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// informational log&lt;/span&gt;
&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Connection timeout, retrying...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// warning condition&lt;/span&gt;
&lt;span class="nx"&gt;logger&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to connect to database&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// error condition&lt;/span&gt;

&lt;span class="c1"&gt;// Logging with context data&lt;/span&gt;
&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User logged in&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Logging an error with stack trace&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// some operation that throws&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Something went wrong&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;logger&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Operation failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-import&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We import the default &lt;code&gt;logger&lt;/code&gt; from LogPro. By default, this logger is environment-aware. In development, these logs would appear in the console in a colored, pretty format. In production (when &lt;code&gt;NODE_ENV&lt;/code&gt; is &lt;code&gt;"production"&lt;/code&gt;), they would appear as JSON lines which are easier to ship to log aggregators.&lt;/li&gt;
&lt;li&gt;We log messages at various levels: &lt;code&gt;.info()&lt;/code&gt; for a general event, &lt;code&gt;.warn()&lt;/code&gt; for a potential issue, and &lt;code&gt;.error()&lt;/code&gt; for a failure. Each method will include a timestamp and other metadata automatically.&lt;/li&gt;
&lt;li&gt;We include additional context information in some logs (like user ID and role on a login event). LogPro will merge this context into the log output (e.g., adding &lt;code&gt;userId&lt;/code&gt; and &lt;code&gt;role&lt;/code&gt; fields in the JSON), which is extremely useful for debugging issues related to specific users or operations.&lt;/li&gt;
&lt;li&gt;When catching an exception, we pass the &lt;code&gt;error&lt;/code&gt; object as a third argument to &lt;code&gt;logger.error()&lt;/code&gt;. LogPro will capture the stack trace and error message and include them in the log entry. This means you get the full error details in your logs without needing a separate &lt;code&gt;console.error&lt;/code&gt; or manual stack print. Logging unexpected errors with stack traces is crucial for post-mortem analysis in production, and LogPro makes it simple.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This basic setup already greatly improves the quality of your &lt;strong&gt;Next.js production logs&lt;/strong&gt; or Node.js service logs. You'll have structured, leveled logs that you can filter and analyze. But LogPro can do more – particularly when it comes to &lt;strong&gt;real-time alerting&lt;/strong&gt; for critical issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring Telegram Alerts for Real-Time Logging
&lt;/h3&gt;

&lt;p&gt;One of LogPro's killer features is the ability to send log messages to Telegram. This allows you to get instant notifications on your phone or Telegram desktop app whenever something important happens (e.g., an error or a specific event in your app). Setting this up involves a few steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Install Telegram Bot API dependency:&lt;/strong&gt; LogPro's Telegram transport is built on the popular Node Telegram Bot API. Install it in your project with: &lt;code&gt;npm install node-telegram-bot-api&lt;/code&gt; (this is an optional dependency).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a Telegram bot:&lt;/strong&gt; Use the official &lt;a href="https://t.me/BotFather" rel="noopener noreferrer"&gt;@BotFather&lt;/a&gt; bot on Telegram to create a new bot for your application. BotFather will walk you through naming your bot and will provide you with a &lt;strong&gt;bot token&lt;/strong&gt; (a long string) once created. Save this token securely – treat it like a password.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Get a chat ID for alerts:&lt;/strong&gt; Decide where you want the alerts to go – it could be a private chat with yourself or a group/channel for your dev team. You will need the &lt;strong&gt;chat ID&lt;/strong&gt; number for that destination. If it's a chat with yourself, you can obtain your own chat ID by using a service like &lt;a href="https://t.me/username_to_id_bot" rel="noopener noreferrer"&gt;@username_to_id_bot&lt;/a&gt; on Telegram (just start it and follow instructions to get your ID). For a group, you might need to add the bot to the group and then get the group's ID similarly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add the Telegram transport in code:&lt;/strong&gt; Now you can use LogPro's &lt;code&gt;TelegramTransport&lt;/code&gt; to send logs to Telegram. You'll configure it with the token and chat ID from the above steps.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's an example of adding a Telegram transport to the logger in code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&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;getLogger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;LogLevel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TelegramTransport&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;@xenral/logpro&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// (Make sure node-telegram-bot-api is installed as mentioned above)&lt;/span&gt;

&lt;span class="c1"&gt;// Create or get a logger instance (could be your main app logger)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Create a Telegram transport for LogPro&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;telegramTransport&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;TelegramTransport&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_TELEGRAM_BOT_TOKEN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;// from BotFather&lt;/span&gt;
  &lt;span class="na"&gt;chatId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_CHAT_ID&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;             &lt;span class="c1"&gt;// from @username_to_id_bot or similar&lt;/span&gt;
  &lt;span class="na"&gt;minLevel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;LogLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ERROR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="c1"&gt;// minimum level to send (e.g., only errors and above)&lt;/span&gt;
  &lt;span class="c1"&gt;// Optional: filter function to fine-tune which logs are sent&lt;/span&gt;
  &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&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;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;urgent&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Attach the Telegram transport to the logger&lt;/span&gt;
&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addTransport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;telegramTransport&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Example log messages&lt;/span&gt;
&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Application started&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
&lt;span class="c1"&gt;// ^ This info log goes to console (or file), but not to Telegram (because of minLevel ERROR).&lt;/span&gt;

&lt;span class="nx"&gt;logger&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Database connection failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="na"&gt;urgent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="na"&gt;dbHost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prod-db-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; 
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// ^ This error log will appear in console and also trigger a Telegram alert, &lt;/span&gt;
&lt;span class="c1"&gt;// since its level is ERROR and we marked it as urgent in context (matching our filter).&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We import &lt;code&gt;TelegramTransport&lt;/code&gt; from LogPro. &lt;/li&gt;
&lt;li&gt;We instantiate it with the bot &lt;code&gt;token&lt;/code&gt; and target &lt;code&gt;chatId&lt;/code&gt;. We also set &lt;code&gt;minLevel: LogLevel.ERROR&lt;/code&gt; to ensure that only logs of level ERROR or FATAL (the highest severities) are sent to Telegram. This prevents spamming Telegram with debug or info logs.&lt;/li&gt;
&lt;li&gt;An optional &lt;code&gt;filter&lt;/code&gt; function is shown: in this case, we're only sending logs to Telegram if the log entry's context has &lt;code&gt;urgent: true&lt;/code&gt;. This is just an example of how you can tag certain logs for alerts. You could decide to send all errors, or perhaps all logs on a certain logger name or context property – it's customizable.&lt;/li&gt;
&lt;li&gt;We then add this transport to our logger. From that point on, any log message that meets the criteria (level and filter) will be forwarded to Telegram &lt;strong&gt;in addition&lt;/strong&gt; to whatever other outputs the logger has (console, files, etc.).&lt;/li&gt;
&lt;li&gt;In the example, an &lt;code&gt;info&lt;/code&gt; log of "Application started" would &lt;strong&gt;not&lt;/strong&gt; go to Telegram (it's below ERROR level). But an &lt;code&gt;error&lt;/code&gt; log about a database failure will be sent to Telegram because it's an error-level and we flagged it as urgent.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After deploying this, you'll start receiving Telegram messages from your bot whenever such an error occurs. The message typically contains the log level, the logger name (in this case "app"), the text message, and any context (e.g., &lt;code&gt;dbHost&lt;/code&gt;) you included. This immediate visibility can be a game-changer for production support – instead of waiting to discover an issue via user reports or digging through log files, you get proactively alerted in real time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Make sure to keep your bot token secure. Do not commit it to your repository. Use environment variables or a secrets management system to load the token and chat ID. For example, you might set &lt;code&gt;process.env.TG_BOT_TOKEN&lt;/code&gt; and &lt;code&gt;process.env.TG_CHAT_ID&lt;/code&gt; in your deployment environment, and pass those into the &lt;code&gt;TelegramTransport&lt;/code&gt; config instead of hardcoding the strings. This keeps sensitive credentials out of your codebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices for Production Logging with LogPro
&lt;/h2&gt;

&lt;p&gt;Using a powerful logging tool like LogPro effectively requires following some best practices to get the most out of it while keeping your application secure and performant. Here are some recommendations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use appropriate log levels and avoid noise:&lt;/strong&gt; In production, it's usually wise to log at &lt;code&gt;info&lt;/code&gt; level and above, reserving &lt;code&gt;debug&lt;/code&gt; logs for development or troubleshooting specific issues. LogPro lets you set a global minimum level (e.g., using &lt;code&gt;logger.setLevel(LogLevel.INFO)&lt;/code&gt; or via environment) so that verbose logs don't overwhelm your production log files or alerts. This ensures you capture important events without the noise. You can elevate the level temporarily (to &lt;code&gt;debug&lt;/code&gt;) if investigating an incident, but remember to revert back. Also, take advantage of LogPro's levels – use &lt;code&gt;warn&lt;/code&gt; for recoverable issues or suspicious situations, &lt;code&gt;error&lt;/code&gt; for actual errors, and &lt;code&gt;fatal&lt;/code&gt; if the app might crash or terminate. Defining clear severity levels helps downstream log analysis and alerting systems work better.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Always capture errors with stack traces:&lt;/strong&gt; When an exception is caught or an error occurs, make sure to log it with the error object (as shown in the try/catch example above). The stack trace and error message are invaluable for debugging. LogPro's API is designed to include error information, so pass the error object to &lt;code&gt;logger.error()&lt;/code&gt; or &lt;code&gt;logger.fatal()&lt;/code&gt;. Also consider logging a brief message explaining the context of the error (e.g., what the app was trying to do) along with the error – this combination of context + stack trace is ideal. By diligently logging errors, you'll have an &lt;em&gt;auditable history&lt;/em&gt; of exceptions and can perform root cause analysis later.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Avoid logging sensitive information:&lt;/strong&gt; &lt;strong&gt;Security&lt;/strong&gt; is as important as utility when it comes to logs. Never expose confidential or personal data in your logs. This includes passwords, API keys, authentication tokens, credit card numbers, or personal user information. Even IP addresses or email addresses might be considered sensitive (PII) in some contexts. A famous incident in 2018 involved a company accidentally logging plaintext user passwords internally, highlighting how dangerous careless logging can be. Treat logs as data that could potentially leak; if your logs are forwarded to external systems (like a cloud log service or Telegram), the risk is even higher. With LogPro, you have fine control over what you include in context. Be mindful to exclude or sanitize sensitive fields. For instance, if you have a user object, log a user ID instead of the full user details. If you must log sensitive data for debugging, consider using LogPro's ability to filter or transform logs (or do it manually) to redact those values. Always err on the side of caution: &lt;em&gt;"Your application logs should never capture data like passwords, credit card details, or authorization tokens."&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Protect your alert channels:&lt;/strong&gt; If you're using the Telegram integration or any external alerting, ensure that the channel is private or only accessible to your team. Telegram bots cannot send messages to a user who hasn't started a conversation with them or a group that hasn't added them, so there's some inherent security, but be aware that whatever you send to Telegram is going through an external service. Do not send extremely sensitive data over these alerts. The goal of alerts is to inform you that something went wrong, with enough detail to take action or look deeper in your secure logs. Think of Telegram alerts as a heads-up; the full context might still reside in your application logs or error tracking system which are more protected.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Monitor performance impact:&lt;/strong&gt; LogPro is designed to be lightweight (no dependencies and efficient JSON serialization), but any logging has overhead. In very high-throughput parts of your application, be mindful not to log excessively (for example, in a tight loop or for every single web request in a very high QPS service, you might want to sample logs). Use log levels wisely to avoid performance degradation. The benefit of structured logging is you can later aggregate and analyze logs, so you might not need to log every detail of every request, just the significant events. If you use async logging or buffer logs to send out (LogPro could be extended for that), ensure those mechanisms are reliable under load.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By following these practices, you'll maintain a high signal-to-noise ratio in your logs, keep your application secure, and ensure your logging infrastructure (and alerting via Telegram) remains sustainable as traffic grows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: Elevate Your Node.js Monitoring with LogPro
&lt;/h2&gt;

&lt;p&gt;As developers, we can't improve what we don't monitor. Advanced logging is a cornerstone of effective application monitoring and error recovery. If you're building serious applications with Node.js or Next.js, relying solely on &lt;code&gt;console.log&lt;/code&gt; is a risk you don't want to take – you might miss critical insights or lose precious time during an outage. Embracing a &lt;strong&gt;production-grade logging tool&lt;/strong&gt; like LogPro can transform how you track your app’s health in production. You'll gain structured, meaningful logs, real-time visibility into issues via Telegram alerts, and the confidence that no error will slip by unnoticed.&lt;/p&gt;

&lt;p&gt;LogPro is easy to integrate, highly configurable, and &lt;strong&gt;purpose-built for TypeScript Node.js projects&lt;/strong&gt;. It addresses the pain points of traditional logging while adding modern features that save time and headaches for developers. Whether you need to debug atricky issue, audit user actions, or get instant notifications of failures, LogPro has you covered.&lt;/p&gt;

&lt;p&gt;Don’t wait for the next production fire to realize your logging is inadequate. Take proactive steps now: &lt;strong&gt;try LogPro in your Node.js/Next.js project&lt;/strong&gt; and experience the difference in your production monitoring. With better logs and timely alerts, you'll be ready to tackle problems head-on and ensure your application runs smoothly 24/7. Happy logging!&lt;/p&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;LogPro GitHub Repository – &lt;em&gt;A minimalistic, structured logging utility for TypeScript projects&lt;/em&gt; (&lt;a href="https://github.com/xenral/LogPro#:~:text=LogPro" rel="noopener noreferrer"&gt;GitHub - xenral/LogPro: A minimal, structured logging utility for TypeScript projects&lt;/a&gt;)
&lt;/li&gt;
&lt;li&gt;Better Stack Community – &lt;em&gt;11 Best Practices for Logging in Node.js&lt;/em&gt; (Ayooluwa Isaiah, Oct 2023) (&lt;a href="https://betterstack.com/community/guides/logging/nodejs-logging-best-practices/#:~:text=While%20the%20above%20methods%20provide,unsuitable%20for%20serious%20production%20logging" rel="noopener noreferrer"&gt;11 Best Practices for Logging in Node.js | Better Stack Community&lt;/a&gt;)
&lt;/li&gt;
&lt;li&gt;Medium (Arunangshu Das, Dec 2024) – &lt;em&gt;What Is the Best Way to Manage Node.js Application Logs in Production?&lt;/em&gt; (&lt;a href="https://arunangshudas.medium.com/what-is-the-best-way-to-manage-node-js-application-logs-in-production-72f687bd6eda#:~:text=Efficiently%20managing%20logs%20in%20a,issues%2C%20track%20performance%20metrics%2C%20and" rel="noopener noreferrer"&gt;What Is the Best Way to Manage Node.js Application Logs in Production? | by Arunangshu Das | Medium&lt;/a&gt;)
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>programming</category>
      <category>debug</category>
    </item>
    <item>
      <title>Canceling HTTP Requests in React (TypeScript) – Why It Matters &amp; How to Do It</title>
      <dc:creator>Ali Morshedzadeh</dc:creator>
      <pubDate>Wed, 26 Mar 2025 16:01:34 +0000</pubDate>
      <link>https://dev.to/xenral/canceling-http-requests-in-react-typescript-why-it-matters-how-to-do-it-3hah</link>
      <guid>https://dev.to/xenral/canceling-http-requests-in-react-typescript-why-it-matters-how-to-do-it-3hah</guid>
      <description>&lt;p&gt;In modern frontend development, managing HTTP calls is just as important as making them. If you've ever fired off an API request in a JavaScript app (especially a React + TypeScript project) and then realized you don't need the result anymore, you know the dilemma. Should you &lt;strong&gt;cancel the request&lt;/strong&gt; or just ignore the response? It turns out that &lt;strong&gt;canceling fetch requests&lt;/strong&gt; (or any async operation) is crucial for keeping your app efficient and bug-free. In this article, we'll have a friendly conversation about &lt;strong&gt;why canceling HTTP requests matters&lt;/strong&gt; – touching on memory leaks, race conditions, and unwanted UI updates – and explore how to implement it. We'll look at the common challenges developers face with request cancellation and then introduce a modern, lightweight solution: the &lt;a href="https://github.com/xenral/async-cancelator" rel="noopener noreferrer"&gt;async-cancelator&lt;/a&gt; NPM package. Finally, we'll walk through an example of using it in a React app with TypeScript to &lt;strong&gt;cancel an API call&lt;/strong&gt; gracefully.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Canceling HTTP Requests Matters
&lt;/h2&gt;

&lt;p&gt;When a web application launches an asynchronous request (like fetching data), a lot can happen while waiting for the response. If the user navigates away or triggers another request, that original response might arrive at an inconvenient time. Canceling unnecessary requests is not just an optimization – it's often a necessity to prevent memory leaks, race conditions, and UI glitches. Here are the key reasons request cancellation is so important:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Avoiding Memory Leaks:&lt;/strong&gt; Unneeded API calls that continue running can hold onto resources. If a component unmounts but its fetch is still in progress, the result may try to update state on an unmounted component. By canceling the request when it's no longer needed, we free those resources.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Preventing Race Conditions:&lt;/strong&gt; In a dynamic app, the user might trigger multiple requests in succession (for example, typing in a search box sending new queries). If an older request returns after a newer one, it can overwrite or conflict with the latest data. Canceling the outdated call ensures that only the latest response updates the state, avoiding inconsistent UI states.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Avoiding Unwanted or Erroneous UI Updates:&lt;/strong&gt; Stale responses can not only show wrong data but also cause errors. React warns when an async task tries to update UI after a component has unmounted. By canceling the request on time, we stop these unwanted updates that could confuse users or throw errors.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short, &lt;strong&gt;canceling HTTP requests in JavaScript&lt;/strong&gt; (and particularly in React apps) is about maintaining control over your app's state and resources. It leads to better performance (no wasted bandwidth on irrelevant responses) and a smoother user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Challenges in Canceling Async Requests
&lt;/h2&gt;

&lt;p&gt;Even though canceling requests is so important, developers often struggle with it. The issue lies in how JavaScript promises and async functions work. By default, a promise &lt;em&gt;does not&lt;/em&gt; have a built-in cancel mechanism – once you kick off an async task, it will run to completion and there's no direct way to stop it halfway. This can lead to a few challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No Native Promise Cancellation:&lt;/strong&gt; Historically, JavaScript didn't allow you to directly cancel a promise. Developers resorted to workarounds like setting a flag (e.g., &lt;code&gt;let isCancelled = true&lt;/code&gt;) to at least ignore the result when it arrives. This pattern can prevent a state update if a component is unmounted, but it doesn't actually stop the request from running in the background.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Manual Cancellation Boilerplate:&lt;/strong&gt; The introduction of the &lt;strong&gt;Fetch API&lt;/strong&gt; and the &lt;code&gt;AbortController&lt;/code&gt; provided a way to cancel HTTP fetch requests. Using the AbortController API, you can create a controller, pass its &lt;code&gt;signal&lt;/code&gt; to &lt;code&gt;fetch&lt;/code&gt;, and later call &lt;code&gt;controller.abort()&lt;/code&gt; to cancel the request. While effective, it requires extra boilerplate every time. You need to create the controller, attach signals, handle errors, and clean up properly in your React effects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Inconsistent Patterns Across APIs:&lt;/strong&gt; Not all async operations support a universal cancel method. Fetch has AbortController, Axios historically used cancel tokens, and other libraries require their own cancellation logic. Managing these different patterns can be cumbersome, leading to inconsistent code and additional boilerplate.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Risk of Ignoring Real Errors:&lt;/strong&gt; When implementing cancellation, one challenge is distinguishing an intentional cancel from a genuine error. For instance, when you abort a fetch, it throws an error that you typically want to ignore or handle differently from other errors. Forgetting to handle that properly can result in confusing error logs or user messages.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All these challenges mean that while &lt;strong&gt;canceling fetch requests in React&lt;/strong&gt; (or any JS app) is possible, it can be tedious and error-prone to do it manually for every async operation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Traditional Solution: AbortController
&lt;/h2&gt;

&lt;p&gt;Before jumping into newer solutions, it's worth acknowledging the standard way to cancel HTTP requests in front-end apps: the &lt;strong&gt;AbortController API&lt;/strong&gt;. This API is built into modern browsers and is commonly used in React apps to cancel fetch calls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How AbortController Works:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
You create an &lt;code&gt;AbortController&lt;/code&gt; instance and get its &lt;code&gt;signal&lt;/code&gt;. Pass this &lt;code&gt;signal&lt;/code&gt; into the fetch request. Later, when you call &lt;code&gt;controller.abort()&lt;/code&gt;, the signal triggers the fetch to abort, causing the fetch promise to reject with an "AbortError". In React, you typically set this up in a &lt;code&gt;useEffect&lt;/code&gt; hook:&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="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="kd"&gt;const&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="o"&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;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.example.com/data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="cm"&gt;/* use the 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;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="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;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;Fetch aborted&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Fetch error:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Cleanup: abort the request if still in-flight&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="p"&gt;[]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern effectively prevents memory leaks and race conditions. However, while AbortController is powerful, using it for every request can become repetitive. You have to create, pass, and clean up controllers each time, which adds boilerplate, especially in larger codebases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing async-cancelator – A Modern, Lightweight Solution
&lt;/h2&gt;

&lt;p&gt;One modern solution for simplifying async cancellation is the &lt;a href="https://github.com/xenral/async-cancelator" rel="noopener noreferrer"&gt;async-cancelator&lt;/a&gt; NPM package. This library was created to address exactly the problems we discussed. Instead of handling cancellation manually each time, async-cancelator provides a convenient API to make any promise cancellable. It works with fetch requests, third-party APIs, or any async function you write, all with a consistent approach.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is async-cancelator?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
It's a zero-dependency JavaScript library (usable in Node.js and browser environments) that gives you tools to cancel promises and even impose timeouts on them. Essentially, you wrap your async operation using the library, and it gives you back a promise that you can cancel on demand. It has full TypeScript support, making it a great fit for React+TypeScript projects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Benefits of Using async-cancelator:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simplified Promise Cancellation:&lt;/strong&gt; Wrap any async operation in a cancellable promise. You get a &lt;code&gt;cancel()&lt;/code&gt; function to call when you need to terminate the task, eliminating the need for manual flags or AbortControllers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Automatic Promise Timeouts:&lt;/strong&gt; Specify a timeout for any promise. If the operation doesn't finish in time, it automatically rejects with a clear TimeoutError. This saves you from writing custom timeout logic and ensures your app doesn't hang on slow requests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cross-Platform and Framework-Agnostic:&lt;/strong&gt; It works in Node.js, vanilla browser JS, or within React apps. Whether you're canceling a network call or a long computation, the same approach applies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;TypeScript Support:&lt;/strong&gt; Full type definitions mean you get type safety and IntelliSense as you integrate cancellation into your functions, reducing bugs and making refactors easier.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Zero Dependencies &amp;amp; Lightweight:&lt;/strong&gt; The library is focused solely on async cancellation and timeout functionality, so it adds minimal overhead to your project.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Essentially, async-cancelator offers a clean API to create &lt;strong&gt;cancellable promises&lt;/strong&gt; and manage them. Instead of manually wiring up an AbortController or juggling cancellation flags, you can use this tool to keep your code readable and consistent. It handles the heavy lifting of canceling async operations so you can focus on your app's logic.&lt;/p&gt;
&lt;h2&gt;
  
  
  How Does async-cancelator Work?
&lt;/h2&gt;

&lt;p&gt;async-cancelator provides a few utility functions that make cancellation straightforward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;createCancellable(asyncFunction)&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Wraps your async function and returns an object with &lt;code&gt;{ promise, cancel }&lt;/code&gt;. Calling &lt;code&gt;cancel()&lt;/code&gt; prevents further then-handlers or state updates from that task. Inside your function, you receive a &lt;code&gt;signal&lt;/code&gt; object to check (e.g., &lt;code&gt;if (signal.cancelled) return;&lt;/code&gt;) at appropriate points.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;createCancellableWithReject(asyncFunction)&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Similar to the previous function, but if you call &lt;code&gt;cancel()&lt;/code&gt;, the returned promise will be &lt;strong&gt;rejected&lt;/strong&gt; with a special &lt;code&gt;CancellationError&lt;/code&gt;. This allows you to handle cancellations in a &lt;code&gt;try-catch&lt;/code&gt; block just like any other error.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;withTimeout(promise, ms, message?)&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Wraps any promise and returns a new promise that will reject if the original doesn't settle within the specified time. If it times out, it rejects with a &lt;code&gt;TimeoutError&lt;/code&gt;, ensuring your app doesn't wait indefinitely on slow operations.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These utilities give you a unified approach to managing async tasks—whether they involve fetching data, performing computations, or any other promise-based work.&lt;/p&gt;
&lt;h2&gt;
  
  
  Example: Canceling an API Call in a React &lt;code&gt;useEffect&lt;/code&gt; Hook
&lt;/h2&gt;

&lt;p&gt;To illustrate how to integrate async-cancelator in a React component, consider a component that fetches data from an API and displays it. We want to ensure that if the component unmounts or the API endpoint changes, we cancel any ongoing request.&lt;/p&gt;

&lt;p&gt;First, install async-cancelator via npm or yarn. Then use it in your component like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&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="nx"&gt;useState&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createCancellableWithReject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CancellationError&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;async-cancelator&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;DataFetcher&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Error&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Wrap the fetch call in a cancellable promise&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;promise&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cancel&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createCancellableWithReject&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;signal&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Handle the promise outcome&lt;/span&gt;
    &lt;span class="nx"&gt;promise&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;result&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="nf"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;CancellationError&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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Fetch cancelled:&lt;/span&gt;&lt;span class="dl"&gt;"&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="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Cleanup: cancel the request if the component unmounts or the URL changes&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Component unmounted or URL changed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;url&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;div&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;loading&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;Loading...&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="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;Error: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&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="si"&gt;}&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;pre&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;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&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="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&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;pre&lt;/span&gt;&lt;span class="p"&gt;&amp;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;div&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;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;DataFetcher&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What's Happening Here?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;We wrap our fetch call using &lt;code&gt;createCancellableWithReject&lt;/code&gt;, which gives us both a &lt;code&gt;promise&lt;/code&gt; and a &lt;code&gt;cancel&lt;/code&gt; function.&lt;/li&gt;
&lt;li&gt;The promise is then handled with &lt;code&gt;.then&lt;/code&gt; and &lt;code&gt;.catch&lt;/code&gt;. On success, the component state is updated. On error, we differentiate between a genuine error and a cancellation.&lt;/li&gt;
&lt;li&gt;The cleanup function in the &lt;code&gt;useEffect&lt;/code&gt; hook calls &lt;code&gt;cancel()&lt;/code&gt; to ensure that if the component unmounts or the URL prop changes, the in-flight request is canceled. This prevents any unwanted state updates after the component has been removed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This pattern can be adapted as needed. For example, if you wanted to impose a timeout on the request, you could use the &lt;code&gt;withTimeout&lt;/code&gt; helper to wrap the fetch promise. The library makes it easy to mix cancellation and timeouts as required by your application.&lt;/p&gt;

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

&lt;p&gt;Canceling HTTP requests in frontend JavaScript applications is a best practice that can save you from memory leaks, race condition bugs, and poor user experiences. In React, cleaning up async tasks in &lt;code&gt;useEffect&lt;/code&gt; is essential to avoid the dreaded "state update on an unmounted component" warning. Traditionally, the AbortController API has been the go-to solution for canceling fetch requests, but it can be verbose to implement consistently.&lt;/p&gt;

&lt;p&gt;The async-cancelator library offers a modern approach to request management by abstracting cancellation logic into a simple, reusable API. With it, you get cancellable promises and easy timeouts, all in a neat package that plays well with React hooks. This means less boilerplate and more confidence that your app only processes the async tasks it really needs. Embracing promise cancellation with tools like async-cancelator leads to cleaner, more maintainable code—and a smoother user experience.&lt;/p&gt;

&lt;p&gt;Happy coding, and may your requests always be intentional and under control!&lt;/p&gt;

</description>
      <category>performance</category>
      <category>react</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Boost React Performance and User Experience with Keyboard Shortcuts</title>
      <dc:creator>Ali Morshedzadeh</dc:creator>
      <pubDate>Sat, 22 Mar 2025 19:42:05 +0000</pubDate>
      <link>https://dev.to/xenral/boost-react-performance-and-user-experience-with-keyboard-shortcuts-5242</link>
      <guid>https://dev.to/xenral/boost-react-performance-and-user-experience-with-keyboard-shortcuts-5242</guid>
      <description>&lt;p&gt;When building modern web applications, performance and user experience (UX) often rank among the top priorities. One effective yet often overlooked way to improve both is by integrating keyboard shortcuts. Not only do shortcuts speed up user interaction, they also reduce reliance on repetitive mouse actions, resulting in a smoother and more efficient experience. In this article, we’ll explore how to leverage the lightweight &lt;strong&gt;&lt;a href="https://www.npmjs.com/package/react-keyhub" rel="noopener noreferrer"&gt;react-keyhub&lt;/a&gt;&lt;/strong&gt; package to implement fast, centralized keyboard shortcuts in your React app. We’ll also highlight its &lt;strong&gt;live preview&lt;/strong&gt; so you can see it in action before integrating it into your own project.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Keyboard Shortcuts Enhance Performance and UX
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Faster Workflows:&lt;/strong&gt; By avoiding constant mouse travel, keyboard shortcuts allow users to perform repetitive actions quickly, improving overall task efficiency.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enhanced Accessibility:&lt;/strong&gt; Power users and individuals with limited motor abilities benefit from an interface that can be fully navigated using the keyboard.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reduced Cognitive Load:&lt;/strong&gt; Consistent and predictable shortcuts help users remember and perform actions seamlessly, minimizing friction in the user flow.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Performance Gains:&lt;/strong&gt; A single, optimized event listener (as offered by react-keyhub) ensures minimal re-renders and more efficient event handling compared to attaching multiple onKeyDown or onKeyUp listeners across components.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Previewing react-keyhub in Action
&lt;/h2&gt;

&lt;p&gt;If you’d like to see &lt;strong&gt;react-keyhub&lt;/strong&gt; before you install it, check out the &lt;strong&gt;Live Shortcut Sheet Demo&lt;/strong&gt; at:&lt;br&gt;&lt;br&gt;
&lt;strong&gt;&lt;a href="https://xenral.github.io/keyhub-example" rel="noopener noreferrer"&gt;https://xenral.github.io/keyhub-example&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You’ll find a working shortcut sheet, discover how various keyboard combos are registered, and explore how the package handles event logic behind the scenes.&lt;/p&gt;


&lt;h2&gt;
  
  
  Introducing react-keyhub
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;react-keyhub&lt;/strong&gt; is a TypeScript-friendly keyboard shortcut manager specifically designed for React applications. Its notable features include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Central Configuration:&lt;/strong&gt; All shortcuts are defined in one place, making them easy to maintain and update.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimized Performance:&lt;/strong&gt; A single event listener handles all keyboard events, reducing overhead and improving responsiveness.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context Awareness:&lt;/strong&gt; You can define shortcuts that only activate in specific contexts (e.g., editor, terminal, or global).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built-In Shortcut Sheet:&lt;/strong&gt; Quickly display all registered shortcuts in a user-friendly modal or sidebar for improved discoverability.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalable and Modular API:&lt;/strong&gt; Subscribe to any shortcut from any component without repeating logic or code.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;To get started, install &lt;strong&gt;react-keyhub&lt;/strong&gt; via your preferred package manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;react-keyhub
&lt;span class="c"&gt;# or&lt;/span&gt;
yarn add react-keyhub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Basic Setup and Usage
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Configure Your Shortcuts&lt;/strong&gt;
Create a shortcut configuration object (or extend the default shortcuts) to centralize all your key mappings:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;   &lt;span class="c1"&gt;// shortcuts.ts&lt;/span&gt;
   &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defaultShortcuts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ShortcutSettings&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-keyhub&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myShortcuts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ShortcutSettings&lt;/span&gt; &lt;span class="o"&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;defaultShortcuts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Include predefined shortcuts (e.g., Ctrl+S for Save)&lt;/span&gt;
     &lt;span class="na"&gt;customAction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="na"&gt;keyCombo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ctrl+k&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Custom Action&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Perform a custom action&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;global&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;enabled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Custom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;regular&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;ol&gt;
&lt;li&gt;
&lt;strong&gt;Wrap Your App with the Provider&lt;/strong&gt;
The &lt;code&gt;KeyHubProvider&lt;/code&gt; makes your shortcuts and event handling available throughout the React component tree:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;   &lt;span class="c1"&gt;// App.tsx&lt;/span&gt;
   &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;KeyHubProvider&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-keyhub&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;myShortcuts&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;./shortcuts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;MyMainComponent&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;./MyMainComponent&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;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;KeyHubProvider&lt;/span&gt; &lt;span class="na"&gt;shortcuts&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;myShortcuts&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="nc"&gt;MyMainComponent&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="nc"&gt;KeyHubProvider&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;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Subscribe to a Shortcut&lt;/strong&gt;
Within any component, use &lt;code&gt;useShortcut&lt;/code&gt; to subscribe to a specific shortcut by its ID:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;   &lt;span class="c1"&gt;// MyMainComponent.tsx&lt;/span&gt;
   &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&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="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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useShortcut&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ShortcutSheet&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-keyhub&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;MyMainComponent&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isSheetOpen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsSheetOpen&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

     &lt;span class="c1"&gt;// Subscribe to an existing shortcut (e.g., showShortcuts = Ctrl+/)&lt;/span&gt;
     &lt;span class="nf"&gt;useShortcut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;showShortcuts&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="nf"&gt;setIsSheetOpen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

     &lt;span class="c1"&gt;// Subscribe to our custom action (Ctrl+K)&lt;/span&gt;
     &lt;span class="nf"&gt;useShortcut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;customAction&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="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;Custom action triggered!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="c1"&gt;// Perform custom logic here&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;div&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;Welcome to Our App&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;Press Ctrl+/ to open the Shortcut Sheet.&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="nc"&gt;ShortcutSheet&lt;/span&gt; 
           &lt;span class="na"&gt;isOpen&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isSheetOpen&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; 
           &lt;span class="na"&gt;onClose&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setIsSheetOpen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="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;div&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;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;MyMainComponent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With just these steps, you have a centralized shortcuts system that’s easy to maintain and scalable for future growth.&lt;/p&gt;




&lt;h2&gt;
  
  
  Performance Benefits of a Centralized Shortcut System
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reduced Event Handlers:&lt;/strong&gt; Rather than adding multiple key listeners to separate components, &lt;strong&gt;react-keyhub&lt;/strong&gt; uses a single optimized event listener at the document level. This approach lowers the chance of event conflicts and cuts down on overhead, improving your app’s responsiveness.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Minimized Re-Renders:&lt;/strong&gt; Because subscription logic is handled through a specialized hook that references a central event bus, you avoid unnecessary re-renders triggered by re-attaching or removing event listeners in various components.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scoped Updates:&lt;/strong&gt; If you need to enable, disable, or update shortcuts, you only do it once. This saves time and resources compared to toggling multiple event handlers scattered across your codebase.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Best Practices for Improving UX with Shortcuts
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Keep Shortcuts Simple:&lt;/strong&gt; Use combinations that are widely recognized (e.g., Ctrl+S for Save). Avoid overly complex combos that users are less likely to remember.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Provide a Shortcut Reference:&lt;/strong&gt; The built-in &lt;code&gt;ShortcutSheet&lt;/code&gt; component in &lt;strong&gt;react-keyhub&lt;/strong&gt; is perfect for displaying all shortcuts in one place, helping users discover features quickly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Contextual Shortcuts:&lt;/strong&gt; For advanced scenarios, define shortcuts that only work when a certain part of the UI is active (e.g., editor context vs. global context). This prevents users from triggering irrelevant actions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Accessibility Checks:&lt;/strong&gt; Ensure that your shortcuts do not interfere with accessibility features, such as screen readers or native browser shortcuts (e.g., Ctrl+T for new tab in most browsers).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Example: Dynamic Registration for Power Users
&lt;/h2&gt;

&lt;p&gt;When you need additional shortcuts for power users, you can register them dynamically and remove them later if they become irrelevant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useShortcutRegister&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-keyhub&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;PowerUserShortcut&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="nf"&gt;useShortcutRegister&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;powerUserMode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;keyCombo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ctrl+shift+p&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Activate Power-User Mode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Enables advanced features&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;global&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;enabled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Advanced&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;regular&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;action&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="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Power-User Mode Activated!&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;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// This component only handles the shortcut and has no UI&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This dynamic approach underscores how flexible &lt;strong&gt;react-keyhub&lt;/strong&gt; can be for evolving user needs.&lt;/p&gt;




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

&lt;p&gt;Implementing keyboard shortcuts in your React application is a straightforward strategy to &lt;strong&gt;enhance both performance and user experience&lt;/strong&gt;. By centralizing keyboard events with &lt;strong&gt;&lt;a href="https://www.npmjs.com/package/react-keyhub" rel="noopener noreferrer"&gt;react-keyhub&lt;/a&gt;&lt;/strong&gt;, you benefit from reduced overhead, scalable shortcut management, and an improved workflow for end users.&lt;/p&gt;

&lt;p&gt;And don’t forget: you can &lt;strong&gt;try it out first&lt;/strong&gt; with the&lt;br&gt;&lt;br&gt;
&lt;strong&gt;&lt;a href="https://xenral.github.io/keyhub-example" rel="noopener noreferrer"&gt;Live Shortcut Sheet Demo&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
to understand how react-keyhub works in a real-world scenario.&lt;/p&gt;

&lt;p&gt;Whether you’re catering to power users who crave keyboard efficiency or simply looking to streamline navigation, &lt;strong&gt;react-keyhub&lt;/strong&gt; provides all the tools you need. By following best practices for shortcut creation, context awareness, and dynamic registration, your React app can deliver a faster and more intuitive experience for everyone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ready to supercharge your React app with keyboard shortcuts?&lt;/strong&gt; Install &lt;strong&gt;react-keyhub&lt;/strong&gt; today and start improving your application’s performance and UX.&lt;/p&gt;

</description>
      <category>performance</category>
      <category>ux</category>
      <category>react</category>
      <category>shortcut</category>
    </item>
    <item>
      <title>React Keyboard Shortcuts: Boost App Performance Using React-Keyhub</title>
      <dc:creator>Ali Morshedzadeh</dc:creator>
      <pubDate>Sun, 16 Mar 2025 14:48:35 +0000</pubDate>
      <link>https://dev.to/xenral/react-keyboard-shortcuts-boost-app-performance-using-react-keyhub-25co</link>
      <guid>https://dev.to/xenral/react-keyboard-shortcuts-boost-app-performance-using-react-keyhub-25co</guid>
      <description>&lt;h2&gt;
  
  
  Introduction: Why Keyboard Shortcuts Improve UX in React
&lt;/h2&gt;

&lt;p&gt;Keyboard shortcuts are a proven way to enhance user experience by enabling faster interactions. They &lt;strong&gt;boost productivity and save time for power users&lt;/strong&gt;. In practice, a few quick key presses can accomplish tasks much faster than navigating through clicks and menus, allowing users to operate your application more efficiently. Modern web applications (from email clients to project management tools) often include rich sets of shortcuts to streamline frequent actions. In the context of React apps, implementing &lt;strong&gt;keyboard shortcuts&lt;/strong&gt; can significantly improve the UX for power users and developers alike, providing a smoother and more efficient workflow.&lt;/p&gt;

&lt;p&gt;However, adding keyboard shortcuts to a React application isn’t without challenges. Managing numerous key events across components can become complex as an app grows. Developers need to carefully handle event listeners, prevent conflicts, and maintain performance. Below, we’ll explore these challenges and introduce &lt;a href="https://github.com/xenral/react-keyhub" rel="noopener noreferrer"&gt;&lt;strong&gt;React-Keyhub&lt;/strong&gt;&lt;/a&gt;, a new library that simplifies and optimizes keyboard shortcut management in React applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges in Managing Keyboard Shortcuts in React
&lt;/h2&gt;

&lt;p&gt;Implementing keyboard shortcuts “from scratch” or with basic libraries can lead to several challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Complex Event Handling:&lt;/strong&gt; Using native event listeners (e.g. &lt;code&gt;keydown&lt;/code&gt;/&lt;code&gt;keyup&lt;/code&gt;) in React components requires adding and removing listeners in lifecycle hooks (or using &lt;code&gt;useEffect&lt;/code&gt;). In large apps with many components, this can scatter shortcut logic across the codebase. If multiple components register the &lt;em&gt;same&lt;/em&gt; shortcut, their behaviors may conflict or override each other, leading to unpredictable results. For example, two open components listening for the same key combo might both trigger, or one might prevent the other from firing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Global vs Local Scope:&lt;/strong&gt; Determining when a shortcut should be active is tricky. Some shortcuts are global (should work anywhere in the app), while others are context-specific (only active in a certain section or when a modal is focused). Handling this manually often means tracking focus or application state to enable/disable listeners. One developer noted that using a library like Mousetrap (a low-level key handler) required manually registering and deregistering shortcuts on component focus to simulate context-specific behavior. This manual bookkeeping is error-prone and hard to scale.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Preventing Unwanted Behavior:&lt;/strong&gt; Many key combinations have default browser actions (e.g. Ctrl+S opening the browser “Save Page” dialog, Ctrl+P for Print, etc.). When implementing custom shortcuts, you typically need to call &lt;code&gt;event.preventDefault()&lt;/code&gt; to stop the browser from executing its default action. Forgetting to do so can lead to confusing user experiences. Similarly, you might want to ignore key events that occur while the user is typing in an input field (so that, for instance, typing “Ctrl+F” in a search box doesn’t trigger your app’s global Find shortcut). Handling these cases with manual listeners or simpler libraries can be cumbersome – one Stack Overflow user pointed out that a React hotkeys library didn’t provide a built-in way to prevent shortcuts from firing when typing in a text input, illustrating how this can be a tricky issue to solve.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Maintenance and Consistency:&lt;/strong&gt; Without a central definition, shortcut mappings can end up duplicated in multiple places. For instance, a component might define a keyboard shortcut and also display the shortcut in a tooltip or menu. If these definitions are not centralized, it’s easy for them to get out of sync (e.g. you change the key combo in one place and forget to update the tooltip). This duplication can lead to inconsistencies. Additionally, some older React shortcut libraries have fallen out of maintenance or have known bugs (as reported with certain keys on macOS in an older library), which is a concern for long-term projects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Performance Concerns:&lt;/strong&gt; Attaching many event listeners (one per component or feature) can impact performance. Each listener added to &lt;code&gt;window&lt;/code&gt; or &lt;code&gt;document&lt;/code&gt; will execute on every key press, potentially doing redundant checks. In a naïve implementation, dozens of components each with their own &lt;code&gt;keydown&lt;/code&gt; handler can slow down response time or cause unnecessary re-renders. Ensuring that shortcuts are handled efficiently (with minimal overhead per key stroke) requires careful architecture.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Given these challenges, a robust solution for keyboard shortcut management in React is highly valuable. &lt;strong&gt;React-Keyhub&lt;/strong&gt; aims to address these issues by providing a central, declarative system for defining and handling shortcuts in React applications. Let’s introduce React-Keyhub and see how it tackles the pain points outlined above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing &lt;a href="https://github.com/xenral/react-keyhub" rel="noopener noreferrer"&gt;React-Keyhub&lt;/a&gt;: A Modern Approach to React Shortcuts
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;React-Keyhub&lt;/strong&gt; is a lightweight, scalable keyboard shortcut manager for React apps that brings a much-needed structured approach to handling shortcuts. It was designed to solve the common challenges of shortcut implementation by offering a comprehensive feature set out of the box. Key features of React-Keyhub include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Centralized Configuration:&lt;/strong&gt; All keyboard shortcuts can be defined in one place, rather than scattered throughout components . This means you have a single source of truth for shortcut definitions (keys, descriptions, scope, etc.), making it easy to review or modify shortcuts globally.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;TypeScript Support and Type Safety:&lt;/strong&gt; React-Keyhub is built with TypeScript, and it provides strong type definitions for your shortcuts and hooks. This ensures that if you reference a shortcut by its ID in your code, you’ll get compile-time checks and autocompletion. You can’t accidentally use a non-existent shortcut ID without TypeScript catching it – a big win for maintainability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Optimized Performance (Single Event Listener):&lt;/strong&gt; Unlike ad-hoc solutions that attach many listeners, React-Keyhub uses &lt;strong&gt;one central event listener&lt;/strong&gt; (an “event bus”) for all shortcuts. All key presses are funneled through this single handler with an efficient lookup mechanism to determine if a defined shortcut was pressed. This design minimizes overhead and avoids the duplicated work of multiple listeners. It’s essentially a publish/subscribe model: one global listener publishes key events, and React-Keyhub notifies the relevant subscriber callbacks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Modular, Hook-based API:&lt;/strong&gt; The library provides React Hooks (and equivalent utilities) to subscribe to shortcuts from any component. You don’t have to wrap components in special providers beyond the one top-level provider. Any component can call a hook like &lt;code&gt;useShortcut('save', callback)&lt;/code&gt; to respond to a shortcut. This keeps your component logic concise and focused only on what to do when the shortcut triggers, while React-Keyhub handles the wiring behind the scenes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Built-in Shortcut Cheat Sheet UI:&lt;/strong&gt; A standout feature is the &lt;strong&gt;ShortcutSheet&lt;/strong&gt; component, which can render a nice looking list of all registered shortcuts in your app. Many applications implement a “keyboard shortcuts help” modal or overlay (often shown when the user presses a certain key like &lt;code&gt;?&lt;/code&gt; or Ctrl+/). React-Keyhub provides this out of the box, so you don’t need to manually create and maintain a shortcuts reference UI. This improves the user experience by making shortcuts discoverable and reduces the developer effort to document them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No External Dependencies:&lt;/strong&gt; &lt;a href="https://www.npmjs.com/package/react-keyhub" rel="noopener noreferrer"&gt;React-Keyhub&lt;/a&gt; is self-contained and doesn’t rely on jQuery or other large libraries (it only depends on React itself). This keeps bundle size small and avoids compatibility issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dynamic Updates at Runtime:&lt;/strong&gt; Shortcuts can be enabled, disabled, or even reconfigured on the fly. This means if your app needs to change shortcuts based on user preferences or mode, React-Keyhub can handle that without requiring a full reload. For example, you could let users remap keys or turn off certain shortcuts and apply those changes immediately.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Contextual Shortcuts:&lt;/strong&gt; React-Keyhub has built-in concept of &lt;em&gt;scope&lt;/em&gt; and &lt;em&gt;context&lt;/em&gt; for shortcuts. You can define shortcuts that only work under certain conditions – for instance, when a specific modal or page is active, or when focus is in a particular UI region. This is much simpler than manually adding/removing listeners on focus changes. If a shortcut has a &lt;code&gt;context&lt;/code&gt; defined, React-Keyhub will only trigger it when the active context matches, otherwise it’s ignored. This addresses the earlier focus management problem directly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Support for Key Sequences:&lt;/strong&gt; In addition to single key combinations (like Ctrl+K), React-Keyhub supports multi-key sequences. This means you can define shortcuts that require pressing keys in order (e.g. press &lt;code&gt;G&lt;/code&gt; then &lt;code&gt;C&lt;/code&gt; to trigger some action). This capability is useful for apps that have many shortcuts and want to offer mnemonic sequences (like Gmail or GitHub’s “go to” shortcuts). React-Keyhub handles the timing and state for sequences so you don’t have to implement that logic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Theming and Responsive UI for ShortcutSheet:&lt;/strong&gt; The provided ShortcutSheet component supports light and dark themes (or auto theme) and can be displayed as a modal or a sidebar. It’s also responsive, so it will adapt to different screen sizes or layouts. This means the shortcut help UI will look consistent with your app style and can be shown in different ways depending on your design needs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Developer Experience Enhancements:&lt;/strong&gt; Little touches like &lt;strong&gt;autocomplete suggestions&lt;/strong&gt; for shortcut IDs in your editor (thanks to the type definitions) and informative console warnings if you try to use an undefined shortcut ID, make the developer experience smoother. These help catch errors early and guide you as you integrate shortcuts into your app.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall, React-Keyhub’s feature set is designed to cover the needs of large, complex React applications that want a reliable, maintainable way to implement keyboard shortcuts. It effectively &lt;strong&gt;addresses the earlier challenges&lt;/strong&gt;: central configuration and context handling mitigate unpredictable behavior and duplication, built-in prevention of default events and input-field ignoring addresses the interference issues, and the single listener architecture boosts performance.&lt;/p&gt;

&lt;p&gt;In the next section, we’ll walk through a practical tutorial on setting up React-Keyhub in a React application, illustrating how to define shortcuts and use the library’s API in a step-by-step guide.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started with React-Keyhub (Implementation Guide)
&lt;/h2&gt;

&lt;p&gt;Integrating React-Keyhub into your project is straightforward. In this mini &lt;strong&gt;React-Keyhub tutorial&lt;/strong&gt;, we’ll cover the essential steps to get it up and running, along with best practices for usage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Install the package.&lt;/strong&gt; If you haven’t already, add React-Keyhub to your project via &lt;a href="https://www.npmjs.com/package/react-keyhub" rel="noopener noreferrer"&gt;npm&lt;/a&gt; or Yarn:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;react-keyhub
&lt;span class="c"&gt;# or&lt;/span&gt;
yarn add react-keyhub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will download the library and add it to your dependencies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Define your shortcuts configuration.&lt;/strong&gt; Decide on the keyboard shortcuts you want in your application and define them in a central object. React-Keyhub comes with a set of &lt;strong&gt;default shortcuts&lt;/strong&gt; for common actions (like Save, Copy, Paste, etc.), which you can import and extend. For example, you can create a &lt;code&gt;myShortcuts&lt;/code&gt; object that merges the defaults with your custom shortcuts:&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;defaultShortcuts&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-keyhub&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;myShortcuts&lt;/span&gt; &lt;span class="o"&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;defaultShortcuts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;// include common defaults&lt;/span&gt;
  &lt;span class="na"&gt;newIssue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;keyCombo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ctrl+shift+n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;New Issue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Create a new issue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;global&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;enabled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Issues&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;regular&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;openSettings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;keyCombo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ctrl+,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// Ctrl + comma&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Open Settings&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Open the settings panel&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;global&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;enabled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Navigation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;regular&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;In this example, we included a couple of custom shortcuts (&lt;code&gt;newIssue&lt;/code&gt; and &lt;code&gt;openSettings&lt;/code&gt;) in addition to the defaults. Each shortcut entry has properties like the key combination (&lt;code&gt;keyCombo&lt;/code&gt;), a human-friendly name and description, its scope (global or local), and so on. You can define as many as you need here – keeping them in one object makes it easy to manage. (We’ll discuss all the available shortcut properties in a moment.) &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Wrap your app with the &lt;code&gt;KeyHubProvider&lt;/code&gt;.&lt;/strong&gt; React-Keyhub uses a context provider to make the shortcut definitions available throughout your React component tree. In your application’s root (for example, in your entry file or top-level App component), wrap the app with &lt;code&gt;&amp;lt;KeyHubProvider&amp;gt;&lt;/code&gt; and pass in the &lt;code&gt;shortcuts&lt;/code&gt; object you created:&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;KeyHubProvider&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-keyhub&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;myShortcuts&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;./myShortcuts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// the object we defined in Step 2&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&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;./App&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;Root&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;KeyHubProvider&lt;/span&gt; &lt;span class="na"&gt;shortcuts&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;myShortcuts&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="nc"&gt;App&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="nc"&gt;KeyHubProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This makes all your shortcuts active and ensures they are listened for globally. By default, the provider attaches a key event listener to the &lt;code&gt;document&lt;/code&gt; and sets up all shortcuts defined in &lt;code&gt;myShortcuts&lt;/code&gt;. You can also pass an &lt;code&gt;options&lt;/code&gt; prop to &lt;code&gt;KeyHubProvider&lt;/code&gt; to tweak settings (like &lt;code&gt;preventDefault&lt;/code&gt;, &lt;code&gt;stopPropagation&lt;/code&gt;, and other behaviors – more on these in the Performance section). But for most cases, the defaults are sensible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Subscribe to shortcuts in your components.&lt;/strong&gt; With the provider in place, individual components can use hooks to respond to specific shortcuts. The primary hook is &lt;code&gt;useShortcut(shortcutId, callback)&lt;/code&gt;. When you call this hook, it will register the given &lt;code&gt;callback&lt;/code&gt; to run whenever the shortcut with that ID is triggered. For example, in a component:&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;useShortcut&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-keyhub&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;EditorPane&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Subscribe to the "save" shortcut (e.g., Ctrl+S) to run saveDocument()&lt;/span&gt;
  &lt;span class="nf"&gt;useShortcut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;save&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;event&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;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;saveDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// custom logic to save content&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Subscribe to a custom shortcut "openSettings"&lt;/span&gt;
  &lt;span class="nf"&gt;useShortcut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;openSettings&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="nf"&gt;openSettingsModal&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// ... component JSX ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we used &lt;code&gt;useShortcut('save', ...)&lt;/code&gt; to intercept the Save command. The &lt;code&gt;event&lt;/code&gt; passed in can be used to &lt;code&gt;preventDefault()&lt;/code&gt; if needed (though note, React-Keyhub’s provider has an option to do this automatically for all shortcuts). We also hook into our custom &lt;code&gt;openSettings&lt;/code&gt; shortcut to open a settings modal. You can call &lt;code&gt;useShortcut&lt;/code&gt; multiple times in the same component for different shortcut IDs. Under the hood, these are all tying into the central event bus, rather than adding new DOM listeners for each usage – so you get a clean component API without the performance hit of separate listeners.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5: (Optional) Display a Shortcut Cheat Sheet.&lt;/strong&gt; To make your shortcuts discoverable, you can include the provided &lt;code&gt;&amp;lt;ShortcutSheet&amp;gt;&lt;/code&gt; component somewhere in your app (often in a top-level component or a modal overlay). This component will render a list of all registered shortcuts, grouped and nicely formatted. A common pattern is to toggle the visibility of the ShortcutSheet when the user presses a certain key (like Ctrl+/ or ?). In fact, React-Keyhub &lt;strong&gt;includes a default shortcut&lt;/strong&gt; for this purpose: by default pressing Ctrl+/ is mapped to an action ID &lt;code&gt;"showShortcuts"&lt;/code&gt;, intended to open or toggle the shortcuts menu. You can leverage that:&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="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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useShortcut&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ShortcutSheet&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-keyhub&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;App&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;sheetOpen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSheetOpen&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Toggle the shortcut sheet when "showShortcuts" shortcut (Ctrl+/) is pressed&lt;/span&gt;
  &lt;span class="nf"&gt;useShortcut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;showShortcuts&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="nf"&gt;setSheetOpen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;open&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;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* ... rest of your app UI ... */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ShortcutSheet&lt;/span&gt; 
        &lt;span class="na"&gt;isOpen&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;sheetOpen&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; 
        &lt;span class="na"&gt;onClose&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setSheetOpen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; 
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&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;In this snippet, pressing Ctrl+/ anywhere in the app will trigger the &lt;code&gt;useShortcut('showShortcuts', ...)&lt;/code&gt; hook and toggle the &lt;code&gt;ShortcutSheet&lt;/code&gt;. The &lt;code&gt;&amp;lt;ShortcutSheet&amp;gt;&lt;/code&gt; component will show all the shortcuts (including names and key combos, exactly as defined in your &lt;code&gt;myShortcuts&lt;/code&gt; object). We can see in the React-Keyhub documentation that using the ShortcutSheet is as simple as controlling its &lt;code&gt;isOpen&lt;/code&gt; prop and handling an &lt;code&gt;onClose&lt;/code&gt; callback. By default it will show all shortcuts, but you can also filter it or theme it if needed.&lt;/p&gt;

&lt;p&gt;That’s it for the basic setup! With these steps, you have a working keyboard shortcut system in your React application. All your shortcuts are declared in one place, any component can respond to them, and you have a built-in way to show the user what shortcuts are available.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Practices:&lt;/strong&gt; When implementing keyboard shortcuts with React-Keyhub, consider these tips:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Group and Name Shortcuts Clearly:&lt;/strong&gt; Use the &lt;code&gt;group&lt;/code&gt; property to categorize shortcuts (for example "File" vs "Edit" vs "Navigation") and provide descriptive &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;description&lt;/code&gt; for each shortcut. This will make the ShortcutSheet more user-friendly and your configuration easier to understand.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Choose Scopes Appropriately:&lt;/strong&gt; If a shortcut should always be available, use &lt;code&gt;scope: 'global'&lt;/code&gt;. If it only makes sense in a certain UI region, consider using &lt;code&gt;scope: 'local'&lt;/code&gt; and managing it via context or only mounting the listener in that part of the app. React-Keyhub’s &lt;code&gt;context&lt;/code&gt; feature can also be used for more fine-grained control (e.g., only active when a certain mode or component is active). For instance, you might set &lt;code&gt;context: 'editor'&lt;/code&gt; on editor-specific shortcuts and call &lt;code&gt;useShortcutContext('editor')&lt;/code&gt; in your Editor component to activate those.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Prevent Conflicts and Defaults:&lt;/strong&gt; Rely on React-Keyhub’s default behavior of preventing default browser actions and stopping propagation for shortcuts. By default &lt;code&gt;preventDefault&lt;/code&gt; and &lt;code&gt;stopPropagation&lt;/code&gt; are enabled in the provider options, which means your Ctrl+S won’t open the browser save dialog, and the event won’t trickle down to other handlers once handled. These defaults save you from having to manually call &lt;code&gt;e.preventDefault()&lt;/code&gt; in every handler (unless you have a special case). They also ensure that once a key combo is handled by a shortcut, it won’t inadvertently trigger another handler elsewhere.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test on Multiple Platforms:&lt;/strong&gt; If your app is used on both Windows and macOS, test the shortcuts on each. Modifier keys differ (e.g., macOS users have the Command key). React-Keyhub supports the standard &lt;code&gt;ctrl+&lt;/code&gt; prefixes and will work with the Control key; if you want to support Command on Mac, you might define shortcuts using the &lt;code&gt;meta&lt;/code&gt; key in the combo (e.g., &lt;code&gt;meta+s&lt;/code&gt; for Command+S) or simply instruct Mac users to use Command if Control is not present. (React-Keyhub doesn’t automatically unify Ctrl/Cmd, so you might define both if needed for a truly cross-platform shortcut.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Keep Shortcut Actions Lightweight:&lt;/strong&gt; The handlers you register with &lt;code&gt;useShortcut&lt;/code&gt; should ideally perform quick actions or defer heavier work (e.g., trigger a dispatch or state update, rather than doing a very expensive computation directly in the handler). This way, the UI remains responsive to key presses. Since the event handling is centralized, you want to keep each key event’s processing as efficient as possible.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By following these practices, you’ll ensure a smooth integration of React-Keyhub that is maintainable and user-friendly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance Considerations and Optimizations
&lt;/h2&gt;

&lt;p&gt;One of the primary advantages of React-Keyhub is its focus on performance. Let’s highlight how it optimizes keyboard event handling and what that means for your React app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Single Event Listener Architecture:&lt;/strong&gt; React-Keyhub attaches &lt;strong&gt;one&lt;/strong&gt; keyboard event listener to a target (by default, the &lt;code&gt;document&lt;/code&gt; object) for the entire application. All key presses are caught by this listener, and React-Keyhub internally routes the event to the appropriate shortcut callback(s) if a match is found. This is much more efficient than adding separate listeners in each component. In a traditional approach, if 10 components needed shortcuts, you might end up with 10 &lt;code&gt;keydown&lt;/code&gt; listeners. With React-Keyhub, there’s just one listener no matter how many shortcuts you have. Fewer listeners mean less work for the browser on each key event and reduced memory usage. The library’s documentation explicitly notes this &lt;em&gt;“single event listener with efficient lookup”&lt;/em&gt; design as an optimized performance feature.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Efficient Key Lookup:&lt;/strong&gt; When a key event occurs, React-Keyhub quickly determines if that key (or key combination) corresponds to a registered shortcut. Under the hood, it likely maintains a map of key combos to shortcut IDs for constant-time lookups, as well as tracking state for sequence shortcuts. This avoids having to loop through all shortcuts for each key press. The result is that even if you have dozens of shortcuts, a key press will be processed in a very short time to decide if there’s a match.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Preventing Unnecessary Work:&lt;/strong&gt; By default, the library prevents the default browser behavior for recognized shortcuts and stops the event’s propagation. This means once a shortcut is handled, other parts of your app or other listeners won’t also try to handle the same event. Stopping propagation not only prevents conflicts, but it saves the browser from having to notify other (non-existent) handlers for that event. Also, React-Keyhub’s default &lt;code&gt;ignoreInputFields: true&lt;/code&gt; setting means that if the user is typing into a text field or textarea, the global shortcut handler will &lt;em&gt;ignore&lt;/em&gt; those key events. This is a smart optimization: there’s no need to run shortcut logic for keys that the user is intentionally typing as input. This also ensures that typing in a form doesn’t accidentally trigger shortcuts – a big usability win, essentially solving the problem noted earlier with other libraries.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Debounce Option:&lt;/strong&gt; The provider offers a &lt;code&gt;debounceTime&lt;/code&gt; option  which is by default 0, but you could set to a few milliseconds if you want to coalesce fast repeated key presses. For example, if a user holds down a key that triggers a shortcut repeatedly, a debounce can prevent firing the action too frequently. This is optional and can be tuned if your use case involves very rapid key presses.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No Forced Re-renders:&lt;/strong&gt; The React-Keyhub context is designed such that simply pressing keys does not cause React component re-renders in itself. The &lt;code&gt;useShortcut&lt;/code&gt; hook adds an event subscription, but it doesn’t use React state to trigger renders on each key press. Your components will only re-render if your shortcut handler explicitly updates state. This is important for performance – you don’t want every key press to re-trigger a React reconciliation unless something in the UI actually changed as a result. React-Keyhub manages the event listening outside of React’s render cycle (in the event bus), so it won’t slow down your app’s rendering.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lightweight Footprint:&lt;/strong&gt; Given it has no external dependencies and is built for this specific purpose, React-Keyhub keeps its bundle small. A smaller, purpose-built library often outperforms trying to use a general-purpose library or writing repetitive code throughout the app. Also, because it’s centralized, the code for handling shortcuts is loaded once instead of potentially duplicated logic in multiple components.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In summary, React-Keyhub contributes to &lt;strong&gt;React performance optimization&lt;/strong&gt; by minimizing the number of event listeners and avoiding unnecessary processing on each keystroke. It was built with large applications in mind, where performance and scalability matter. By using it, you can be confident that adding more shortcuts won’t linearly degrade your app’s performance, thanks to the efficient event handling strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison with Other Shortcut Libraries
&lt;/h2&gt;

&lt;p&gt;When evaluating options, consider this quick comparison:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;React-Keyhub&lt;/th&gt;
&lt;th&gt;react-hotkeys-hook&lt;/th&gt;
&lt;th&gt;Mousetrap&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TypeScript Support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Built-In Shortcut Visualization&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Context-Aware Shortcuts&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sequence Shortcuts&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dynamic Updates&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;React-Keyhub is a relatively new entrant in the React ecosystem, so it’s worth comparing it to other approaches you might have seen or used for keyboard shortcuts in React:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Manual Event Listeners vs React-Keyhub:&lt;/strong&gt; Some developers roll their own solution using React’s &lt;code&gt;useEffect&lt;/code&gt; to attach &lt;code&gt;keydown&lt;/code&gt; listeners. While this works for simple cases, it becomes hard to manage as shortcuts multiply. You’d need to handle global state or context manually, ensure cleanup on unmount, and coordinate multiple listeners. By contrast, React-Keyhub provides a structured system with far less boilerplate – you declare shortcuts and use hooks, rather than dealing with low-level events yourself. The built-in features like default prevention and context awareness save a lot of effort that a manual solution would require.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mousetrap (or low-level libraries):&lt;/strong&gt; Mousetrap is a popular vanilla JS library for key events. It’s powerful but not React-specific. Using it in React means you still have to manage integration (e.g., calling Mousetrap in lifecycle hooks). One major limitation noted with Mousetrap is that it has a global key binding state, so only one callback can be bound to a given key combination at a time. If you want context-specific behavior, you must unbind and rebind keys as the user’s focus or context changes, which can get complex (and risk unbinding something that another part of the app needed). React-Keyhub inherently supports multiple shortcuts with the same keys in different contexts (using &lt;code&gt;scope&lt;/code&gt; or &lt;code&gt;context&lt;/code&gt;), and it prioritizes them appropriately (with a &lt;code&gt;priority&lt;/code&gt; setting if needed). This means no manual unbinding is required — local shortcuts can override global ones when active, etc., in a controlled manner.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;React-Hotkeys (and React-Hotkeys-Hook):&lt;/strong&gt; &lt;strong&gt;react-hotkeys&lt;/strong&gt; is an older library that introduced the idea of defining shortcuts via a wrapping component (&lt;code&gt;&amp;lt;HotKeys&amp;gt;&lt;/code&gt;). It works, but as reported by some developers, it hasn’t been actively maintained and had quirks (especially with certain keys on macOS). More modern solutions like &lt;strong&gt;react-hotkeys-hook&lt;/strong&gt; offer a hooks-based API, but under the hood they may still attach multiple event listeners if you use the hook in many components. One developer using a hooks-based library noted they needed to create a dedicated top-level component to catch all key events to avoid attaching many listeners throughout the app. React-Keyhub saves you from this pattern by design — its provider is effectively that single top-level listener, and you don’t need to engineer it yourself. Additionally, many of these libraries don’t provide extras like a built-in shortcuts help dialog or TypeScript type safety. React-Keyhub’s built-in ShortcutSheet and TS integration set it apart, giving it an edge in both user experience and developer experience.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;React-Keyboard-Event-Handler / React-Keydown / Others:&lt;/strong&gt; There are several other small libraries that allow you to specify key handlers in React. Many of them focus on &lt;em&gt;component-level&lt;/em&gt; shortcuts or require higher-order components/decorators. They can be useful for specific isolated cases, but they often lack a global perspective. For example, handling orchestrated sequences of keys, or enabling/disabling shortcuts dynamically, might not be supported out of the box. In contrast, React-Keyhub was built to be a holistic solution, combining the global management with per-component subscription. It also provides utilities to dynamically register or update shortcuts at runtime, which is rarely found in simpler libraries.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In summary, React-Keyhub’s advantages lie in its &lt;strong&gt;unified approach and rich feature set&lt;/strong&gt;. It takes lessons learned from earlier tools and addresses their shortcomings (maintenance, context handling, performance, etc.). If your application only needs a couple of very simple shortcuts, a smaller hook or manual listener could do the job. But for any non-trivial application where shortcuts are an important part of the UI, React-Keyhub offers a robust, scalable solution with features that would be tedious to implement manually. It’s designed to grow with your app, whereas piecemeal solutions might start to falter as you add more shortcuts or more complex requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Use Cases for Keyboard Shortcuts in React Apps
&lt;/h2&gt;

&lt;p&gt;To put things into perspective, let’s consider some &lt;strong&gt;common use cases&lt;/strong&gt; and examples of keyboard shortcuts in real-world React applications. Many of these scenarios are easily implemented with React-Keyhub (and some are even included in its default shortcut set):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;File and Document Operations:&lt;/strong&gt; Web applications that mimic desktop apps often include shortcuts like &lt;strong&gt;Save (Ctrl+S)&lt;/strong&gt;, &lt;strong&gt;Print (Ctrl+P)&lt;/strong&gt;, or &lt;strong&gt;New Item (Ctrl+N)&lt;/strong&gt; for convenience. For instance, a project management tool might use &lt;em&gt;Ctrl+N&lt;/em&gt; to create a new task. React-Keyhub’s default shortcuts include Save and Print with the standard key combos, which you can use or override as needed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Editing Commands:&lt;/strong&gt; Rich text editors or form-heavy apps benefit from shortcuts for &lt;strong&gt;Undo/Redo (Ctrl+Z / Ctrl+Y)&lt;/strong&gt;, &lt;strong&gt;Copy/Paste (Ctrl+C / Ctrl+V)&lt;/strong&gt;, &lt;strong&gt;Find (Ctrl+F)&lt;/strong&gt;, etc. These accelerate text manipulation and navigation. If you’re building, say, a notes app or code editor in React, implementing Undo/Redo via shortcuts is almost essential for a good user experience.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Navigation and View Switching:&lt;/strong&gt; Many apps allow quick navigation using keys. For example, an email client might use &lt;strong&gt;J/K&lt;/strong&gt; to move to the next or previous message, or a dashboard might use &lt;strong&gt;Ctrl+1/2/3&lt;/strong&gt; to switch tabs. Some applications even implement &lt;strong&gt;sequence shortcuts&lt;/strong&gt; for navigation; GitHub, for example, has shortcuts like pressing “G” then “C” to go to the Code tab. With React-Keyhub you can define a sequence like &lt;code&gt;'g c'&lt;/code&gt; to trigger a function. This could open a specific panel or focus a certain element in your app.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Help and Accessibility:&lt;/strong&gt; Providing a shortcut for “Help” or “Show Shortcuts” is a common pattern. For instance, &lt;strong&gt;F1&lt;/strong&gt; is often used to open help documentation. Another common one is &lt;strong&gt;? or Ctrl+/&lt;/strong&gt; to open an overlay listing all available shortcuts (as discussed, React-Keyhub even has &lt;code&gt;showShortcuts&lt;/code&gt; built in for this purpose). Including such a shortcut is great for discoverability – users can hit one key and see a cheat sheet of what they can do without touching the mouse.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Context-Specific Controls:&lt;/strong&gt; In complex interfaces, certain shortcuts only make sense in a given context. For example, a code editor might offer Vim-style navigation keys (H/J/K/L for left/down/up/right) but only when the editor is active and in “vim mode”. Using React-Keyhub, you could assign those keys to actions with a context like &lt;code&gt;context: 'vim'&lt;/code&gt;, so they &lt;em&gt;only&lt;/em&gt; work when that context is set. Another scenario: in a modal dialog, you might want the Enter key to submit and Escape key to close, but only when that modal is open. Rather than globally binding Enter/Escape (which could interfere elsewhere), you can register those shortcuts as local to the modal’s context or scope.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Power-User Features:&lt;/strong&gt; Shortcuts aren’t just for basic commands. They can unlock advanced workflows. Think of an application like Photoshop – nearly every action has a shortcut. In a React app, you might allow batch actions via shortcuts (e.g., multi-select items and press &lt;strong&gt;Delete&lt;/strong&gt; key to remove them), or developer tools might have hidden shortcuts for debugging views. With a system like React-Keyhub, adding these is straightforward and doesn’t clutter your UI (until the user calls up the ShortcutSheet to see them).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These examples barely scratch the surface, but they illustrate how ubiquitous and useful keyboard shortcuts can be. When designing your application’s shortcuts, it’s good to follow established conventions (like using common keys for common actions – users appreciate when “Ctrl+F” means Find, etc.). React-Keyhub’s default set can act as a guide in this regard, as it includes many standard shortcuts with familiar key bindings. You can customize or disable any defaults that don’t apply to your app.&lt;/p&gt;

&lt;p&gt;By implementing keyboard shortcuts thoughtfully, you cater to both novice users (who might gradually learn the shortcuts) and expert users (who will greatly appreciate the efficiency). Your React app can provide a desktop-like experience where power users rarely need to take their hands off the keyboard, thanks to a well-implemented shortcut system.&lt;/p&gt;

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

&lt;p&gt;Keyboard shortcuts in React applications are a powerful tool for enhancing user experience and boosting productivity. They serve as “accelerators” that let experienced users get things done rapidly. Yet, managing a web of keyboard events in a complex React app can be challenging without the right approach. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;React-Keyhub&lt;/strong&gt; offers a professional, robust solution to this problem. By centralizing shortcut definitions, providing a simple hooks API, and handling all the heavy lifting (event management, context, sequences, etc.), it lets developers focus on &lt;em&gt;what&lt;/em&gt; should happen on a shortcut, rather than &lt;em&gt;how&lt;/em&gt; to wire it up. We’ve seen how it addresses common issues like event conflicts, performance overhead, and maintenance hassles, all while adding valuable features like a built-in shortcuts cheat sheet and TypeScript support.&lt;/p&gt;

&lt;p&gt;For an intermediate React developer, React-Keyhub strikes a great balance: it’s deep enough to handle advanced use cases (dynamic updates, context-specific controls, intricate key sequences), but it’s also accessible to those with less experience – you can start with basic shortcuts and gradually adopt more features. The step-by-step integration is straightforward, and the defaults mean you get sensible behavior without a lot of configuration.&lt;/p&gt;

&lt;p&gt;In adopting React-Keyhub, you make your application more efficient and user-friendly, especially for power users who rely on keyboard commands. It’s not about adding “cool tricks” – it’s about improving the overall usability and responsiveness of your app. And because React-Keyhub is focused on performance and clean architecture, you can add as many shortcuts as your app needs without worrying about a tangle of event listeners or sluggish key responses.&lt;/p&gt;

&lt;p&gt;In summary, leveraging keyboard shortcuts in your React app (with the help of a tool like &lt;a href="https://www.npmjs.com/package/react-keyhub" rel="noopener noreferrer"&gt;React-Keyhub&lt;/a&gt;) is a smart move for both user experience and developer maintainability. It allows you to deliver features that feel polished and responsive. Whether you’re building a rich text editor, a dashboard, or any interactive web application, consider giving your users the gift of speed with well-implemented &lt;strong&gt;React shortcuts&lt;/strong&gt;. With React-Keyhub, doing so is easier and more effective than ever. Happy coding, and may your users never have to reach for the mouse when they don’t want to! &lt;/p&gt;

</description>
      <category>performance</category>
      <category>webdev</category>
      <category>react</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Still Using Redux Everywhere? How an Event Bus Could Transform Your React Workflow</title>
      <dc:creator>Ali Morshedzadeh</dc:creator>
      <pubDate>Fri, 07 Mar 2025 14:12:48 +0000</pubDate>
      <link>https://dev.to/xenral/still-using-redux-everywhere-how-an-event-bus-could-transform-your-react-workflow-18ej</link>
      <guid>https://dev.to/xenral/still-using-redux-everywhere-how-an-event-bus-could-transform-your-react-workflow-18ej</guid>
      <description>&lt;p&gt;Modern React developers often reach for state management libraries (Redux, Zustand, Recoil, etc.) by default to handle shared state. However, using these state managers everywhere can introduce &lt;strong&gt;unnecessary complexity&lt;/strong&gt; and boilerplate into your apps. In many cases, an &lt;strong&gt;event-driven architecture&lt;/strong&gt; can be a simpler, more flexible solution. In this article, we’ll explore the pitfalls of overusing state managers, discuss the event-driven alternative, introduce &lt;strong&gt;&lt;code&gt;react-eventizer&lt;/code&gt;&lt;/strong&gt; as a powerful event bus for React, and walk through real-world examples with code. We’ll also cover best practices and when to choose events vs. state managers. Let’s dive in!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pitfalls of Overusing State Managers
&lt;/h2&gt;

&lt;p&gt;State management libraries like &lt;strong&gt;Redux&lt;/strong&gt;, &lt;strong&gt;Zustand&lt;/strong&gt;, and &lt;strong&gt;Recoil&lt;/strong&gt; are powerful tools for complex apps. They provide a centralized store and fancy features (time-travel debugging, middleware, etc.), but they come at a cost. Introducing Redux or similar libraries in every situation can be &lt;strong&gt;overkill for small to medium apps&lt;/strong&gt;, adding unnecessary overhead. Redux in particular often requires a lot of boilerplate (actions, reducers, providers) which can make code harder to understand and maintain. Even “lighter” solutions like Zustand or Recoil, while simpler, still add an extra layer of abstraction that you might not need for straightforward cases.&lt;/p&gt;

&lt;p&gt;Another common issue is &lt;strong&gt;prop drilling&lt;/strong&gt; and tangled callback chains in deep component hierarchies. In a typical React app, data flows &lt;em&gt;down&lt;/em&gt; through props and events bubble &lt;em&gt;up&lt;/em&gt; through callback props. In a simple component tree this is fine, but as your app grows, passing props through multiple layers (just to get data to a distant component) and wiring callbacks back up becomes cumbersome. You end up with intermediate components that exist only to shuttle data around, making the code harder to follow and maintain. This tight coupling of components increases cognitive load and brittleness in your codebase.&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%2Futdsvuqssylnjh4shcmu.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%2Futdsvuqssylnjh4shcmu.jpg" alt="Prop Drilling in React" width="800" height="434"&gt;&lt;/a&gt; &lt;em&gt;Prop drilling forces passing props (green arrows) down many layers, and bubbling callbacks (red arrows) up through intermediaries, which can tangle your component architecture.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;To avoid prop drilling hell, many developers instinctively turn to a global state solution (Context API, Zustand, Redux, etc.) so components can share data without explicit prop passing ([Event-Driven Architecture for Clean React Component Communication - DEV Community]. While this can solve one problem, overusing global state introduces new complexity – you might be maintaining a giant store or context for things that could be handled more simply. So is there a middle ground? &lt;strong&gt;Event-driven architecture&lt;/strong&gt; offers a compelling alternative for many scenarios.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Event-Driven Alternative: Loosely Coupled Components
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Event-driven architecture&lt;/strong&gt; in React allows components to communicate through a publish/subscribe (&lt;em&gt;pub-sub&lt;/em&gt;) pattern rather than via shared state or direct parent-child links. In essence, components can &lt;strong&gt;emit events&lt;/strong&gt; (publish) and &lt;strong&gt;listen for events&lt;/strong&gt; (subscribe) using a central &lt;strong&gt;event bus&lt;/strong&gt;, without needing to know about each other’s existence. This decouples your components, making the app architecture more modular and easier to maintain.&lt;/p&gt;

&lt;p&gt;In an event-driven approach, when something happens in one component, it can broadcast an event into a central channel. Any other component interested in that event can react to it, regardless of where it sits in the component tree. This means no more drilling props down or lifting state up just to get two distant components talking – the event bus handles that communication in a &lt;strong&gt;transparent, global&lt;/strong&gt; way.&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%2Faxk1b8detity5spgfkyg.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%2Faxk1b8detity5spgfkyg.jpg" alt="Event-driven communication" width="800" height="533"&gt;&lt;/a&gt; &lt;em&gt;Event-driven communication: sub-components dispatch events to a central events handler (blue arrows), which notifies any listening components. This eliminates the need to thread callbacks up through every intermediate parent.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;Because components remain unaware of who is listening or emitting, this pattern &lt;strong&gt;promotes loose coupling&lt;/strong&gt;. Your components become more self-contained: they just announce what happened (e.g. &lt;em&gt;“item X was deleted”&lt;/em&gt;) or respond to announcements (&lt;em&gt;“someone deleted an item, I should update my list”&lt;/em&gt;) without tight integrations. The result is often &lt;strong&gt;simpler code flow&lt;/strong&gt; – especially for cross-cutting concerns like global notifications, logging, or syncing data – and potentially &lt;strong&gt;fewer lines of code&lt;/strong&gt; than an equivalent Redux setup. In fact, an event bus can bring clarity to a large codebase by centralizing how events are handled.&lt;/p&gt;

&lt;h2&gt;
  
  
  Meet &lt;code&gt;react-eventizer&lt;/code&gt;: An Event Bus for React
&lt;/h2&gt;

&lt;p&gt;So how can we implement an event-driven architecture in React? &lt;strong&gt;&lt;code&gt;react-eventizer&lt;/code&gt;&lt;/strong&gt; is a lightweight library that provides exactly this: a &lt;strong&gt;React-friendly event bus&lt;/strong&gt; system. According to its documentation, &lt;code&gt;react-eventizer&lt;/code&gt; is a “lightweight, zero-dependency React event bus with full TypeScript support” that &lt;strong&gt;enables decoupled component communication using a pub/sub model&lt;/strong&gt;. In simpler terms, it lets you set up a global event hub in your React app, with minimal code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key features of &lt;a href="https://github.com/xenral/react-eventizer" rel="noopener noreferrer"&gt;react-eventizer&lt;/a&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔄 &lt;strong&gt;Simple Pub/Sub API:&lt;/strong&gt; Provides easy methods or hooks to emit events and subscribe to them.&lt;/li&gt;
&lt;li&gt;🔒 &lt;strong&gt;Type-Safe:&lt;/strong&gt; Built with TypeScript generics, so you can define strict types for each event’s payload (catching mistakes at compile time).&lt;/li&gt;
&lt;li&gt;⚛️ &lt;strong&gt;Seamless React Integration:&lt;/strong&gt; Uses React Context under the hood and offers custom Hooks (&lt;code&gt;useSubscribe&lt;/code&gt;, &lt;code&gt;useEmitter&lt;/code&gt;, etc.) to interface with the event bus anywhere in your component tree.&lt;/li&gt;
&lt;li&gt;🪶 &lt;strong&gt;Lightweight:&lt;/strong&gt; Zero external dependencies and a minimal footprint. It won’t bloat your bundle or slow your app.&lt;/li&gt;
&lt;li&gt;🧩 &lt;strong&gt;Decoupled Communication:&lt;/strong&gt; Allows components to talk to each other without direct relationships, eliminating the need for prop drilling or overly complex state lifting.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short, &lt;code&gt;react-eventizer&lt;/code&gt; gives you the benefits of an event bus pattern in a convenient React package.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started with &lt;code&gt;react-eventizer&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Using &lt;a href="https://www.npmjs.com/package/react-eventizer" rel="noopener noreferrer"&gt;react-eventizer&lt;/a&gt; in your project is straightforward. You can install it via npm or yarn:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;react-eventizer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then follow these general steps to set it up:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Define your events:&lt;/strong&gt; For TypeScript users, define an &lt;strong&gt;EventMap&lt;/strong&gt; interface listing all event names and their payload types. For example, you might have events like &lt;code&gt;'user:login'&lt;/code&gt;, &lt;code&gt;'notification:new'&lt;/code&gt;, &lt;code&gt;'theme:change'&lt;/code&gt;, etc., each mapped to the shape of data it carries (or to &lt;code&gt;void&lt;/code&gt; if no payload). This step is optional but highly recommended for type safety.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create and provide the event bus:&lt;/strong&gt; Initialize a new event bus instance (e.g. &lt;code&gt;const eventBus = new Eventizer&amp;lt;EventMap&amp;gt;()&lt;/code&gt;) and make it available to your app by wrapping your app in an &lt;code&gt;&amp;lt;EventizerProvider&amp;gt;&lt;/code&gt; with the bus passed as a prop. Typically, you do this once at the root (e.g. in &lt;code&gt;App.tsx&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscribe to events:&lt;/strong&gt; In any component that needs to react to a global event, use the &lt;code&gt;useSubscribe(eventName, callback)&lt;/code&gt; hook provided by &lt;code&gt;react-eventizer&lt;/code&gt;. This hook will register your callback to run whenever that event is emitted. (It also handles unsubscribing automatically on component unmount to prevent memory leaks).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Emit events:&lt;/strong&gt; Whenever a component needs to broadcast something, use the &lt;code&gt;useEmitter(eventName)&lt;/code&gt; hook to get an emitter function, and call that function with the appropriate payload. All subscribers to that event will then be notified.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s look at a quick example of &lt;code&gt;react-eventizer&lt;/code&gt; in action. Imagine we want to broadcast a user login event so various parts of the app can respond (maybe to display a welcome message, update a profile, fetch user-specific data, etc.). We’ll emit a &lt;code&gt;"user:login"&lt;/code&gt; event from a login form and subscribe to it in a user profile component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* LoginForm.tsx – emitter example */&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEmitter&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-eventizer&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;LoginForm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUsername&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emitLogin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useEmitter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user:login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// prepare an emitter for the 'user:login' event&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleLogin&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;// Emit the user:login event with a payload when login happens&lt;/span&gt;
    &lt;span class="nf"&gt;emitLogin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;123&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;form&lt;/span&gt; &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;e&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;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="nf"&gt;handleLogin&lt;/span&gt;&lt;span class="p"&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;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setUsername&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Username"&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;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Login&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&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;form&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;span class="cm"&gt;/* UserProfile.tsx – subscriber example */&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useSubscribe&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-eventizer&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;UserProfile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUsername&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Subscribe to 'user:login' events&lt;/span&gt;
  &lt;span class="nf"&gt;useSubscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user:login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setUsername&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&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;username&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`Welcome, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;username&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Please log in&lt;/span&gt;&lt;span class="dl"&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;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the &lt;strong&gt;LoginForm&lt;/strong&gt; doesn’t need to directly inform &lt;strong&gt;UserProfile&lt;/strong&gt; through context or shared state. It simply emits a &lt;code&gt;"user:login"&lt;/code&gt; event when the form is submitted. The &lt;strong&gt;UserProfile&lt;/strong&gt; component, anywhere in the app, subscribes to &lt;code&gt;"user:login"&lt;/code&gt; and updates its local state when that event occurs. The two components remain completely decoupled – they only share an event contract. If no component cared about &lt;code&gt;"user:login"&lt;/code&gt;, the event would just go into the void with no issues. If multiple components subscribe to it, all of them will receive the event. This pub-sub mechanism can dramatically simplify scenarios where &lt;strong&gt;many components need to respond to the same event&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Use Cases Made Easier with Events
&lt;/h2&gt;

&lt;p&gt;What kinds of scenarios benefit most from an event-driven approach? Let’s explore a few common use cases where &lt;code&gt;react-eventizer&lt;/code&gt; can make React development easier, along with how you might implement them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cross-Component Communication (No More Prop Drilling)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; You have components that need to talk to each other but are not directly related in the JSX hierarchy. For instance, a deeply nested component needs to send info to a top-level layout or sibling component far away. Traditionally, you might lift state up to a common ancestor or use context – or you suffer prop drilling through many layers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How &lt;code&gt;react-eventizer&lt;/code&gt; helps:&lt;/strong&gt; Use a global event instead! Any component can emit an event that any other component can listen for, without threading props. This achieves instant cross-component communication with &lt;strong&gt;zero prop drilling&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, suppose you have a list of items and a separate notifications area. When an item is deleted in the list, you want to show a notification banner. Without an event bus, you might have to pass a callback from the notifications system down into the item component, or use a global state flag. With &lt;code&gt;react-eventizer&lt;/code&gt;, it’s trivial:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In a deeply nested component (e.g. DeleteButton inside an Item component)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emitDelete&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useEmitter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;item:delete&lt;/span&gt;&lt;span class="dl"&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;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;emitDelete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;itemId&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;gt;&lt;/span&gt;Delete Item&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;// ... Meanwhile, in a totally different part of the app ...&lt;/span&gt;
&lt;span class="nf"&gt;useSubscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;item:delete&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;deletedItemId&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="nf"&gt;notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Item &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;deletedItemId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; was deleted successfully.`&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;Here the Delete button simply emits an &lt;code&gt;"item:delete"&lt;/code&gt; event carrying an item ID. A Notification component (or any component interested in deletions) subscribes to &lt;code&gt;"item:delete"&lt;/code&gt; and, when it hears one, triggers a user notification. Neither component needs to know about the other – they only share the event name and data schema. This &lt;strong&gt;loose coupling&lt;/strong&gt; means you can add or remove listeners freely without breaking functionality, and you don’t have to route that event through parent components. It makes your code more modular and your component tree cleaner.&lt;/p&gt;

&lt;h3&gt;
  
  
  Global Notifications and Alerts
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; You want to trigger notification messages or alerts from anywhere in the app (deep in the bowels of your code), but have them all display in a common UI component (like a toast container or alert stack). Without events, you might lift all notification state to a top-level store or use context to expose functions for adding notifications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How &lt;code&gt;react-eventizer&lt;/code&gt; helps:&lt;/strong&gt; Treat notifications as global events. Any part of the app can emit a &lt;code&gt;"notification:new"&lt;/code&gt; event with the message and type of notification. A single &lt;code&gt;NotificationCenter&lt;/code&gt; component can subscribe to that event and handle displaying all incoming notifications.&lt;/p&gt;

&lt;p&gt;For example, using &lt;code&gt;react-eventizer&lt;/code&gt; you could do something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Emit a notification event after some action (e.g., after saving data)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emitNotification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useEmitter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;notification:new&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="nf"&gt;emitNotification&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Data saved successfully!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// NotificationCenter component listens for any new notifications&lt;/span&gt;
&lt;span class="nf"&gt;useSubscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;notification:new&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;notif&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;// push the new notification into local state (to render it)&lt;/span&gt;
  &lt;span class="nf"&gt;setNotifications&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prevList&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;prevList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;notif&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;Every time an event is emitted (perhaps from anywhere: after API calls, user actions, errors caught, etc.), the &lt;code&gt;NotificationCenter&lt;/code&gt; will pick it up and update its list of notifications. This way, &lt;strong&gt;components don’t have to directly call the notification system&lt;/strong&gt; or import it; they just fire an event and forget. The notification handling is centralized, which is easier to manage and avoids duplicating notification logic across components.&lt;/p&gt;

&lt;h3&gt;
  
  
  Global UI State Changes (Themes, Modals, etc.)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Some UI state changes need to affect multiple independent components. For instance, changing the app’s theme should update many components, or closing a modal might need to inform various parts of the UI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How &lt;code&gt;react-eventizer&lt;/code&gt; helps:&lt;/strong&gt; For one-off triggers like theme changes or modal toggles, an event can be more convenient than storing this state globally. You can emit a &lt;code&gt;"theme:change"&lt;/code&gt; event or &lt;code&gt;"modal:close"&lt;/code&gt; event that any component can respond to.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Theme switching:&lt;/em&gt; Instead of putting the current theme in a context and making every component consume that context, you could simply have a central theme store (or even just local storage) and emit &lt;code&gt;"theme:change"&lt;/code&gt; events. Components that care (like those that style themselves differently or a &lt;code&gt;&amp;lt;ThemeProvider/&amp;gt;&lt;/code&gt;) subscribe to apply the new theme. This can reduce continuous re-renders that a context might cause, since an event will only trigger subscribers once when it’s emitted.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Modal control:&lt;/em&gt; If a deeply nested component triggers a modal to open, and some other component needs to know when that modal closes, you can emit &lt;code&gt;"modal:close"&lt;/code&gt; upon closure. Any component can listen for &lt;code&gt;"modal:close"&lt;/code&gt; to, say, refresh data or update the URL, without tightly coupling to the modal logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The event-driven approach essentially &lt;strong&gt;broadcasts UI intents&lt;/strong&gt; (like “theme updated” or “modal closed”) in a way that any interested piece of UI can act on, without maintaining that intent as persistent state if it’s not needed beyond the moment of the change.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling Asynchronous or External Events
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Your app receives asynchronous data or messages (from WebSockets, server-sent events, etc.) that multiple components may need to respond to. Managing this via state can get tricky, especially if not all components mount at the same time or if the data isn’t something you want to store globally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How &lt;code&gt;react-eventizer&lt;/code&gt; helps:&lt;/strong&gt; You can funnel external messages into events. For instance, imagine you have a WebSocket connection that receives real-time updates. With &lt;code&gt;react-eventizer&lt;/code&gt;, your WebSocket service can emit events whenever a message arrives:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Pseudocode for a WebSocket message handler&lt;/span&gt;
&lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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="nx"&gt;eventBus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;socket:message&lt;/span&gt;&lt;span class="dl"&gt;'&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="c1"&gt;// In any component that needs live updates&lt;/span&gt;
&lt;span class="nf"&gt;useSubscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;socket:message&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;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// handle the incoming data (update component state, etc.)&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="s2"&gt;Received live update:&lt;/span&gt;&lt;span class="dl"&gt;"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By doing this, you &lt;strong&gt;decouple the WebSocket logic from your React components&lt;/strong&gt; entirely. Components just declare an interest in &lt;code&gt;'socket:message'&lt;/code&gt; events and react when they occur. If the WebSocket implementation changes or moves, the components don’t care – as long as events with that name are emitted. Similarly, you can handle other external or async events (like a countdown finishing, a media query change, etc.) in this pub-sub way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices for Event-Driven Architecture in React
&lt;/h2&gt;

&lt;p&gt;Using &lt;code&gt;react-eventizer&lt;/code&gt; (or any event bus) effectively requires a bit of forethought. Here are some best practices to keep your event-driven React architecture clean and maintain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Define event types centrally:&lt;/strong&gt; Declare all your event names and payload structures in one place (e.g. an &lt;code&gt;events.ts&lt;/code&gt; file with the EventMap interface). This makes it easier to manage and document what events exist in the system, and ensures publishers and subscribers use the correct event names.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use namespaces for event names:&lt;/strong&gt; Adopting a naming convention like &lt;code&gt;"category:action"&lt;/code&gt; (for example, &lt;code&gt;user:login&lt;/code&gt;, &lt;code&gt;cart:add&lt;/code&gt;, &lt;code&gt;socket:message&lt;/code&gt;) helps avoid name collisions and keeps events organized. It’s easier to identify the purpose of an event at a glance, and you won’t accidentally have two different subsystems using generic names like &lt;code&gt;"update"&lt;/code&gt; that conflict.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean up if needed:&lt;/strong&gt; If you use the &lt;code&gt;useSubscribe&lt;/code&gt; hook, it will automatically unsubscribe when the component unmounts. But if you ever subscribe manually via the event bus’s &lt;code&gt;on()&lt;/code&gt; method, be sure to unsubscribe (&lt;code&gt;off()&lt;/code&gt;) when appropriate to prevent memory leaks or unwanted behavior. Keeping subscription lifecycles tied to component lifecycles is usually safest.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don’t abuse events:&lt;/strong&gt; While events are great for decoupling, avoid emitting events willy-nilly or in very tight loops. Use events for higher-level communication, not as a replacement for every function call or state update. Too many global events can make it hard to trace what's happening. Also, avoid using events to store long-term state – remember that an event is a moment in time, not a state container.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By following these practices, you can ensure that your event-driven approach remains &lt;strong&gt;predictable and scalable&lt;/strong&gt; as your application grows.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Use Events vs. When to Use a State Manager
&lt;/h2&gt;

&lt;p&gt;It’s important to strike a balance. Event-driven architecture can simplify many aspects of a React app, but it’s not a silver bullet for all state management needs. Here’s a balanced perspective on when to choose each approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use an Event Bus (like &lt;code&gt;react-eventizer&lt;/code&gt;) for:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Decoupled interactions and notifications.&lt;/em&gt; If your goal is to have parts of the app react to something happening elsewhere (user actions, global UI triggers, background processes) without maintaining a lot of shared state, events are ideal. They shine for ephemeral, momentary communications – things that happen, other parts respond, and that’s it. This includes the use cases we discussed: cross-component messages, broadcast notifications, one-off UI updates, and integrating external event streams. Events keep these interactions simple and avoid the overhead of global state for transient data.&lt;br&gt;&lt;br&gt;
&lt;em&gt;Reducing coupling.&lt;/em&gt; Use events when you want to avoid forcing a direct link or dependency between components or modules. This can make your code more modular and testable (you can test components by faking events, for example).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use a State Manager (Redux, Zustand, Recoil, Context API) for:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Persistent and global state.&lt;/em&gt; If you have &lt;strong&gt;application-wide state&lt;/strong&gt; that many components need to read and update over time (such as user authentication info, a large form data structure, or caching of fetched data), a state store or context might be more appropriate. These tools are designed to hold onto data and make it accessible anywhere, whereas an event bus by itself doesn’t store data – it just transmits it.&lt;br&gt;&lt;br&gt;
&lt;em&gt;Complex state logic and debugging.&lt;/em&gt; When your state has complex transitions, strict requirements, or benefits from time-travel debugging and traceability (e.g. using Redux DevTools), a structured state manager is beneficial. Redux, for instance, can record every action and state change, which is great for debugging complex apps. Events are more fire-and-forget, which can be harder to debug if overused for critical state changes (though you can certainly log them).&lt;br&gt;&lt;br&gt;
&lt;em&gt;Forms and synchronized state.&lt;/em&gt; Some scenarios (like multi-step forms or any feature where many parts of the UI must reflect the exact same state consistently) might still be easier with a centralized state or context that components read from directly, rather than emitting events to each other to stay in sync.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In practice, you don’t have to choose one or the other exclusively. You can &lt;strong&gt;mix and match&lt;/strong&gt;: use an event bus for what it’s good at and a state store for what it’s good at. For example, you might use React Context or Zustand to hold the current user and theme (since those are needed as state by many components at all times), but use &lt;code&gt;react-eventizer&lt;/code&gt; to handle things like “user logged in” or “theme changed” as events to trigger animations, fetches, or notifications across the app. In fact, the combination can be powerful: the event can carry the new state, and subscribers can update their own state or even a context based on it.&lt;/p&gt;

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

&lt;p&gt;“&lt;strong&gt;Stop using state managers everywhere&lt;/strong&gt;” doesn’t mean state libraries are bad – it means you should &lt;strong&gt;use them judiciously&lt;/strong&gt;. Global state managers like Redux, Recoil, and Zustand are fantastic for certain use cases, but not every problem is a nail requiring that hammer. Many times, an &lt;strong&gt;event-driven approach&lt;/strong&gt; provides a simpler and more elegant solution for component communication without the ceremony of global stores. By using &lt;code&gt;react-eventizer&lt;/code&gt; or a similar event bus, you can make your React apps more modular, reduce prop drilling, and simplify how components interact.&lt;/p&gt;

&lt;p&gt;The key is to &lt;strong&gt;choose the right tool for the job&lt;/strong&gt;: if you find yourself adding a lot of complexity just to get components talking, consider using events to decouple and simplify. On the other hand, if you truly need a single source of truth for state and the ability to inspect or revert it, a state manager might be the way to go. Often, a hybrid approach yields the best of both worlds.&lt;/p&gt;

&lt;p&gt;By understanding both paradigms, you can architect React applications that are &lt;strong&gt;simpler, more efficient, and easier to maintain&lt;/strong&gt;. So next time you’re about to pull in a heavy state management library for a simple interaction, ask yourself: &lt;em&gt;“Can an event solve this?”&lt;/em&gt; You might be surprised how often the answer is yes, and how much cleaner your code can be as a result.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>performance</category>
      <category>react</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How to Cancel Async Operations and Handle Timeouts in JavaScript, React, and Node.js</title>
      <dc:creator>Ali Morshedzadeh</dc:creator>
      <pubDate>Wed, 05 Mar 2025 14:24:02 +0000</pubDate>
      <link>https://dev.to/xenral/how-to-cancel-async-operations-and-handle-timeouts-in-javascript-react-and-nodejs-3j73</link>
      <guid>https://dev.to/xenral/how-to-cancel-async-operations-and-handle-timeouts-in-javascript-react-and-nodejs-3j73</guid>
      <description>&lt;p&gt;Managing asynchronous operations in JavaScript can sometimes feel like trying to herd cats—chaotic and unpredictable. Async tasks have a way of slipping through the cracks, whether they’re running longer than expected, getting stuck, or just hanging around when you don’t need them. If you’ve worked with async code, you know the struggle of handling timeouts, cancellations, and keeping everything running smoothly.&lt;/p&gt;

&lt;p&gt;Here are some common problems that can arise:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Canceling long-running or unnecessary tasks&lt;/strong&gt;: Ever start an async operation only to realize halfway through that you don’t need it anymore? Without a way to cancel that promise, it just keeps going, wasting time and resources.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Timeouts&lt;/strong&gt;: Async operations that take too long can freeze up your app, leaving users frustrated. You need a way to automatically reject those promises that exceed your desired duration, so your app stays responsive.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cross-platform consistency&lt;/strong&gt;: Whether you're working in Node.js, a browser environment, or React, managing async tasks across different platforms can become a real headache.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;TypeScript support&lt;/strong&gt;: If you’re working with TypeScript, managing async operations while ensuring proper type safety can be tricky without the right tools.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, here’s the good news—&lt;strong&gt;we’ve found a way to tackle all these issues easily&lt;/strong&gt;. Tools like &lt;strong&gt;async-cancelator&lt;/strong&gt; are designed to take care of these exact problems. By using it in our projects, we’ve been able to cancel promises effortlessly, handle timeouts, and keep everything running smoothly across different platforms.&lt;/p&gt;

&lt;p&gt;What makes this tool so great? It helps us manage async operations with clean, simple code. Instead of dealing with messy workarounds, we can focus on the logic that matters. Plus, it’s a zero-dependency solution that integrates seamlessly with our existing projects. It handles cancellation, timeouts, and even TypeScript support without making things complicated.&lt;/p&gt;

&lt;p&gt;Here’s how you can jump in:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Install it&lt;/strong&gt;: Just run &lt;code&gt;npm install async-cancelator&lt;/code&gt; (or &lt;code&gt;yarn add async-cancelator&lt;/code&gt; if that’s your thing) to get started.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start using it&lt;/strong&gt;: Implement cancellable promises and timeouts where you need them in your project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enjoy&lt;/strong&gt;: Test your async code, knowing it won’t hang or overstay its welcome.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By adding &lt;strong&gt;async-cancelator&lt;/strong&gt; to your toolkit, you’ll write smoother, more maintainable async code that behaves exactly as you expect. No more wrestling with timeouts or trying to cancel tasks manually. Ready to give it a go? Start by giving it a spin in your next project, and let it take care of the heavy lifting. Let's code!&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;First things first, let’s get this party started by installing the tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;async-cancelator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or, if you’re feeling yarn-y:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add async-cancelator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find the full source code for this library on &lt;a href="https://github.com/xenral/async-cancelator" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Use &lt;code&gt;async-cancelator&lt;/code&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Creating a Cancellable Promise
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createCancellable&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;async-cancelator&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cancel&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createCancellable&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;signal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cancelled&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="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;someAsyncOperation&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;signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cancelled&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="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Operation completed&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="c1"&gt;// Later, if you change your mind:&lt;/span&gt;
&lt;span class="nf"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Operation no longer needed&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;h3&gt;
  
  
  Cancellable Promise with Automatic Rejection
&lt;/h3&gt;

&lt;p&gt;If you prefer your promises to throw a fit when canceled, &lt;code&gt;createCancellableWithReject&lt;/code&gt; is here for that drama.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createCancellableWithReject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CancellationError&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;async-cancelator&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cancel&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createCancellableWithReject&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;signal&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;await&lt;/span&gt; &lt;span class="nf"&gt;someAsyncOperation&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Operation completed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;try&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Operation no longer needed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;promise&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;error&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;error&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;CancellationError&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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Operation was cancelled: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&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="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Handle other errors&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setting a Timeout
&lt;/h3&gt;

&lt;p&gt;Don’t want to wait forever? Set a timeout to keep things snappy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;withTimeout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TimeoutError&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;async-cancelator&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;timeoutPromise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;withTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.example.com/data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="mi"&gt;5000&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 timed out&lt;/span&gt;&lt;span class="dl"&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;timeoutPromise&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;error&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;error&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;TimeoutError&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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Operation timed out: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&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="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Handle other errors&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Combining Cancellation and Timeout
&lt;/h3&gt;

&lt;p&gt;Why not have the best of both worlds? Combine cancellation and timeout for ultimate control.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createCancellable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;withTimeout&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;async-cancelator&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cancel&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createCancellable&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;signal&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;// Your async operation&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;timeoutPromise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;withTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;promise&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Operation timed out&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No longer needed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using with React Hooks
&lt;/h3&gt;

&lt;p&gt;React developers, rejoice! &lt;strong&gt;&lt;a href="https://github.com/xenral/async-cancelator" rel="noopener noreferrer"&gt;async-cancelator&lt;/a&gt;&lt;/strong&gt; plays nicely with hooks, ensuring your components stay in sync with your async logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&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;useRef&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createCancellableWithReject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CancellationError&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;async-cancelator&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;useFetchData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cancelRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cancel&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createCancellableWithReject&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;signal&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&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="nx"&gt;cancelRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cancel&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;CancellationError&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

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

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cancelRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;cancelRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Component unmounted&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;return&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="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/xenral/async-cancelator" rel="noopener noreferrer"&gt;async-cancelator&lt;/a&gt;&lt;/strong&gt; is like the Swiss Army knife for managing asynchronous tasks in JavaScript. Its ability to handle cancellations and timeouts with ease makes it a must-have in your development toolkit. So go ahead, give it a try, and take control of your async operations like a pro.&lt;/p&gt;




&lt;h3&gt;
  
  
  Let’s Keep the Conversation Going!
&lt;/h3&gt;

&lt;p&gt;🚀 Have you tried handling async cancellations or timeouts differently?&lt;br&gt;&lt;br&gt;
💡 Got any cool use cases or edge cases to share?&lt;br&gt;&lt;br&gt;
👇 Drop a comment below and let’s discuss!  &lt;/p&gt;

&lt;p&gt;Looking forward to hearing your thoughts! 🔥&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>tutorial</category>
      <category>node</category>
    </item>
  </channel>
</rss>
