<?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: Josh Claunch</title>
    <description>The latest articles on DEV Community by Josh Claunch (@josh_claunch).</description>
    <link>https://dev.to/josh_claunch</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%2F591287%2F4cdc251f-8322-4538-b004-419fb1e93eab.png</url>
      <title>DEV Community: Josh Claunch</title>
      <link>https://dev.to/josh_claunch</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/josh_claunch"/>
    <language>en</language>
    <item>
      <title>How Atoms Fixed Flux</title>
      <dc:creator>Josh Claunch</dc:creator>
      <pubDate>Thu, 15 Jun 2023 21:22:54 +0000</pubDate>
      <link>https://dev.to/josh_claunch/how-atoms-fixed-flux-ggg</link>
      <guid>https://dev.to/josh_claunch/how-atoms-fixed-flux-ggg</guid>
      <description>&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@markbasarabvisuals?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Mark Basarab&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/1OtUkD_8svc?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(Originally posted on &lt;a href="https://hackernoon.com/how-atoms-fixed-flux"&gt;HackerNoon&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/facebookexperimental/Recoil"&gt;Recoil&lt;/a&gt; introduced the atomic model to the React world. Its new powers came at the cost of a steep learning curve and sparse learning resources.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/pmndrs/jotai"&gt;Jotai&lt;/a&gt; and &lt;a href="https://github.com/Omnistac/zedux"&gt;Zedux&lt;/a&gt; later simplified various aspects of this new model, offering many new features and pushing the limits of this amazing new paradigm.&lt;/p&gt;

&lt;p&gt;Other articles will focus on the differences between these tools. This article will focus on one big feature that all 3 have in common:&lt;/p&gt;

&lt;p&gt;They fixed Flux.&lt;/p&gt;

&lt;h2&gt;
  
  
  Flux
&lt;/h2&gt;

&lt;p&gt;If you don't know Flux, here's a quick gist:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--m9SMIeti--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2y15k8wpsinonjh1mgv8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m9SMIeti--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2y15k8wpsinonjh1mgv8.png" alt="Actions -&amp;gt; Dispatcher -&amp;gt; Stores -&amp;gt; Views" width="800" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Besides Redux, all Flux-based libraries basically followed this pattern: An app has multiple stores. There is only one Dispatcher whose job is to feed actions to all the stores in a proper order. This "proper order" means dynamically sorting out dependencies between the stores.&lt;/p&gt;

&lt;p&gt;For example, take an e-commerce app setup:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RzsVnn7m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u3j5f9alijpxdwfm911o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RzsVnn7m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u3j5f9alijpxdwfm911o.png" alt="UserStore &amp;lt;-&amp;gt; CartStore &amp;lt;-&amp;gt; PromosStore" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the user moves, say, a banana to their cart, the PromosStore needs to wait for CartStore's state to update before sending off a request to see if there's an available banana coupon.&lt;/p&gt;

&lt;p&gt;Or perhaps bananas can't ship to the user's area. The CartStore needs to check the UserStore before updating. Or perhaps coupons can only be used once a week. The PromosStore needs to check the UserStore before sending the coupon request.&lt;/p&gt;

&lt;p&gt;Flux doesn't like these dependencies. From the &lt;a href="https://legacy.reactjs.org/blog/2014/07/30/flux-actions-and-the-dispatcher.html#why-we-need-a-dispatcher"&gt;legacy React docs&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The objects within a Flux application are highly decoupled, and adhere very strongly to the &lt;a href="https://en.wikipedia.org/wiki/Law_of_Demeter"&gt;Law of Demeter&lt;/a&gt;, the principle that each object within a system should know as little as possible about the other objects in the system.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The theory behind this is solid. 100%. Soo ... why did this multi-store flavor of Flux die?&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependency Trees
&lt;/h2&gt;

&lt;p&gt;Well turns out, dependencies between isolated state containers are inevitable. In fact, to keep code modular and DRY, you &lt;em&gt;should&lt;/em&gt; be using other stores frequently.&lt;/p&gt;

&lt;p&gt;In Flux, these dependencies are created on-the-fly:&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="c1"&gt;// This example uses Facebook's own `flux` library&lt;/span&gt;
&lt;span class="nx"&gt;PromosStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dispatchToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dispatcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;register&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="k"&gt;if&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;actionType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;add-to-cart&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;// wait for CartStore to update first:&lt;/span&gt;
    &lt;span class="nx"&gt;dispatcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;CartStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dispatchToken&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="c1"&gt;// now send the request&lt;/span&gt;
    &lt;span class="nx"&gt;sendPromosRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UserStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CartStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;promos&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;dispatcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;actionType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;promos-fetched&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;promos&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actionType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;promos-fetched&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;PromosStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setPromos&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;promos&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;CartStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dispatchToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dispatcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;register&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="k"&gt;if&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;actionType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;add-to-cart&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;// wait for UserStore to update first:&lt;/span&gt;
    &lt;span class="nx"&gt;dispatcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;UserStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dispatchToken&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;UserStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;canBuy&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;item&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;CartStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addItem&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;item&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 example shows how dependencies aren't directly declared between stores - rather, they're pieced together on a per-action basis. These informal dependencies require digging through implementation code to find.&lt;/p&gt;

&lt;p&gt;This is a very simple example! But you can already see how helter-skelter Flux feels. Side effects, selection operations, and state updates are all cobbled together. This colocation can actually be kind of nice. But mix in some informal dependencies, triple the recipe, and serve it on some boilerplate and you'll see Flux break down quickly.&lt;/p&gt;

&lt;p&gt;Other Flux implementations like &lt;a href="https://github.com/acdlite/flummox"&gt;Flummox&lt;/a&gt; and &lt;a href="https://github.com/reflux/refluxjs"&gt;Reflux&lt;/a&gt; improved the boilerplate and debugging experience. While very usable, dependency management was the one nagging problem that plagued all Flux implementations. Using another store felt ugly. Deeply-nested dependency trees were hard to follow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LJAsmSmL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgflip.com/7l071n.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LJAsmSmL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgflip.com/7l071n.jpg" alt="Flux in Theory vs Flux in Practice" width="620" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This ecommerce app could someday have stores for OrderHistory, ShippingCalculator, DeliveryEstimate, BananasHoarded, etc. A large app could easily have hundreds of stores. How do you keep dependencies up-to-date in every store? How do you track side effects? What about purity? What about debugging? Are bananas really a berry?&lt;/p&gt;

&lt;p&gt;As for the programming principles introduced by Flux, unidirectional data flow was a winner, but, for now, the Law of Demeter was not.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Singleton Model
&lt;/h2&gt;

&lt;p&gt;We all know how &lt;a href="https://github.com/reduxjs/redux"&gt;Redux&lt;/a&gt; came roaring in to save the day. It ditched the concept of multiple stores in favor of a singleton model. Now everything can access everything else without any "dependencies" at all.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qldWsPRq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/17jlq0w8whz2cjt2g96c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qldWsPRq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/17jlq0w8whz2cjt2g96c.png" alt="Actions -&amp;gt; Middleware -&amp;gt; Store -&amp;gt; Views" width="800" height="293"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Reducers are pure, so all logic dealing with multiple state slices &lt;em&gt;must&lt;/em&gt; go outside the store. The community made standards for managing side effects and derived state. Redux stores are beautifully debuggable. The only major Flux Flaw that Redux originally failed to fix was its boilerplate.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://redux-toolkit.js.org/"&gt;RTK&lt;/a&gt; later simplified Redux's infamous boilerplate. Then &lt;a href="https://github.com/pmndrs/zustand"&gt;Zustand&lt;/a&gt; removed some fluff at the cost of some debugging power. All of these tools have become extremely popular in the React world.&lt;/p&gt;

&lt;p&gt;With modular state, dependency trees grow so naturally complex that the best solution we could think of was, "Just don't do it I guess."&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iHJhHOiU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgflip.com/7l0mdg.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iHJhHOiU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgflip.com/7l0mdg.jpg" alt="Got problems? Just don't" width="702" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And it worked! This new singleton approach still works well enough for most apps. The Flux principles were so solid that simply removing the dependency nightmare fixed it.&lt;/p&gt;

&lt;p&gt;Or did it?&lt;/p&gt;

&lt;h2&gt;
  
  
  Back to Basics
&lt;/h2&gt;

&lt;p&gt;The success of the singleton approach begs the question, what was Flux getting at in the first place? Why did we ever want multiple stores?&lt;/p&gt;

&lt;p&gt;Allow me to shed some light on this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reason #1: Autonomy
&lt;/h3&gt;

&lt;p&gt;With multiple stores, pieces of state are broken out into their own autonomous, modular containers. These stores can be tested in isolation. They can also be shared easily between apps and packages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reason #2: Code Splitting
&lt;/h3&gt;

&lt;p&gt;These autonomous stores can be split into separate code chunks. In a browser, they can be lazy-loaded and plugged in on the fly.&lt;/p&gt;

&lt;p&gt;Redux's reducers are fairly easy to code-split too. Thanks to &lt;a href="https://redux.js.org/api/store#replacereducernextreducer"&gt;&lt;code&gt;replaceReducer&lt;/code&gt;&lt;/a&gt;, the only extra step is to create the new combined reducer. However, more steps may be required when side effects and middleware are involved.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reason #3: Standardized Primitives
&lt;/h3&gt;

&lt;p&gt;With the singleton model, it's difficult to know how to integrate an external module's internal state with your own. The Redux community introduced the Ducks pattern as an attempt to solve this. And it works, at the cost of a little boilerplate.&lt;/p&gt;

&lt;p&gt;With multiple stores, an external module can simply expose a store. For example, a form library can export a FormStore. The advantage of this is that the standard is "official", meaning people are less likely to create their own methodologies. This leads to a more robust, unified community and package ecosystem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reason #4: Scalability
&lt;/h3&gt;

&lt;p&gt;The singleton model is surprisingly performant. Redux has proven that. However, its selection model especially has a hard upper limit. I wrote some thoughts on this in &lt;a href="https://github.com/reduxjs/reselect/discussions/491#discussioncomment-5762615"&gt;this Reselect discussion&lt;/a&gt;. A big, expensive selector tree can really start to drag, even when taking maximum control over caching.&lt;/p&gt;

&lt;p&gt;On the other hand, with multiple stores, most state updates are isolated to a small portion of the state tree. They don't touch anything else in the system. This is scalable far beyond the singleton approach - in fact, with multiple stores, it's very difficult to hit CPU limitations before hitting memory limitations on the user's machine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reason #5: Destruction
&lt;/h3&gt;

&lt;p&gt;Destroying state is not too difficult in Redux. Just like in the code-splitting example, it requires only a few extra steps to remove a part of the reducer hierarchy. But it's still simpler with multiple stores - in theory, you can simply detach the store from the dispatcher and allow it to be garbage collected.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reason #6: Colocation
&lt;/h3&gt;

&lt;p&gt;This is the big one that Redux, Zustand, and the singleton model in general do not handle well. Side effects are separated from the state they interact with. Selection logic is separated from everything. While multi-store Flux was perhaps &lt;em&gt;too&lt;/em&gt; colocated, Redux went to the opposite extreme.&lt;/p&gt;

&lt;p&gt;With multiple autonomous stores, these things naturally go together. Really, Flux only lacked a few standards to prevent everything from becoming a helter-skelter hodge-podge of gobbledygook (sorry).&lt;/p&gt;

&lt;h3&gt;
  
  
  Reasons Summary
&lt;/h3&gt;

&lt;p&gt;Now, if you know the OG Flux library, you know that it actually wasn't great at all of these. The dispatcher still takes a global approach - dispatching every action to every store. The whole thing with informal/implicit dependencies also made code splitting and destruction less than perfect.&lt;/p&gt;

&lt;p&gt;Still, Flux had a lot of cool features going for it. Plus the multiple-store approach has potential for even more features like Inversion of Control and fractal (aka local) state management.&lt;/p&gt;

&lt;p&gt;Flux might have evolved into a truly powerful state manager if somebody hadn't named their goddess Demeter. I'm serious! ... Ok, I'm not. But now that you mention it, maybe Demeter's law deserves a closer look:&lt;/p&gt;

&lt;h2&gt;
  
  
  The Law of Demeter
&lt;/h2&gt;

&lt;p&gt;What exactly is this so-called "law"? From &lt;a href="https://en.wikipedia.org/wiki/Law_of_Demeter"&gt;Wikipedia&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Each unit should have only limited knowledge about other units: only units "closely" related to the current unit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Each unit should only talk to its friends; don't talk to strangers.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;This law was designed with Object-Oriented Programming in mind, but it can be applied in many areas, including React state management.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZvmxsjHl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1btlvthg8j1yhwqh8z93.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZvmxsjHl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1btlvthg8j1yhwqh8z93.png" alt="PromosStore shouldn't use CartStore's internal state or dependencies" width="800" height="196"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The basic idea is to prevent a store from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tightly-coupling itself to another store's implementation details.&lt;/li&gt;
&lt;li&gt;Using stores &lt;strong&gt;that it doesn't need to know about&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Using any other store without &lt;em&gt;explicitly&lt;/em&gt; declaring a dependency on that store.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In banana terms, a banana shouldn't peel another banana and shouldn't talk to a banana in another tree. However, it &lt;em&gt;can&lt;/em&gt; talk to the other tree if the two trees rig up a banana phone line first.&lt;/p&gt;

&lt;p&gt;This encourages separation of concerns and helps your code stay modular, DRY, and SOLID. Solid theory! So what was Flux missing?&lt;/p&gt;

&lt;p&gt;Well, inter-store dependencies are a natural part of a good, modular system. If a store needs to add another dependency, it should do that &lt;em&gt;and&lt;/em&gt; do it &lt;strong&gt;as explicitly as possible&lt;/strong&gt;. Here's some of that Flux code again:&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="nx"&gt;PromosStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dispatchToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dispatcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;register&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="k"&gt;if&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;actionType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;add-to-cart&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;// wait for CartStore to update first:&lt;/span&gt;
    &lt;span class="nx"&gt;dispatcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;CartStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dispatchToken&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="c1"&gt;// now send the request&lt;/span&gt;
    &lt;span class="nx"&gt;sendPromosRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UserStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CartStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;promos&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;dispatcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;actionType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;promos-fetched&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;promos&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actionType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;promos-fetched&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;PromosStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setPromos&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;promos&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;PromosStore has multiple dependencies declared in different ways - it waits for and reads from &lt;code&gt;CartStore&lt;/code&gt; &lt;em&gt;and&lt;/em&gt; it reads from &lt;code&gt;UserStore&lt;/code&gt;. The only way to discover these dependencies is to look for stores in PromosStore's implementation.&lt;/p&gt;

&lt;p&gt;Dev tools can't help make these dependencies more discoverable either. In other words, the dependencies are too implicit.&lt;/p&gt;

&lt;p&gt;While this is a very simple and contrived example, it illustrates how Flux misinterpreted the Law of Demeter. While I'm sure it was mostly born of a desire to keep Flux implementations small (real dependency management is a complex task!), this is where Flux fell short.&lt;/p&gt;

&lt;p&gt;Unlike the heroes of this story:&lt;/p&gt;

&lt;h2&gt;
  
  
  The Heroes
&lt;/h2&gt;

&lt;p&gt;In 2020, &lt;a href="https://github.com/facebookexperimental/Recoil"&gt;Recoil&lt;/a&gt; came stumbling onto the scene. While a little clumsy at first, it taught us a new pattern that revived the multiple-store approach of Flux.&lt;/p&gt;

&lt;p&gt;Unidirectional data flow moved from the store itself to the dependency graph. Stores were now called atoms. Atoms were properly autonomous and code-splittable. They had new powers like suspense support and hydration. And most importantly, atoms formally declare their dependencies.&lt;/p&gt;

&lt;p&gt;The atomic model was born.&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="c1"&gt;// a Recoil atom&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;greetingAtom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;atom&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;greeting&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello, World!&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;Recoil struggled with a bloated codebase, memory leaks, bad performance, slow development, and unstable features - most notably side effects. It would slowly iron out some of these, but in the meantime, other libraries took Recoil's ideas and ran with them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/pmndrs/jotai"&gt;Jotai&lt;/a&gt; burst onto the scene and quickly gained a following.&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="c1"&gt;// a Jotai atom&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;greetingAtom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;atom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello, World!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Besides being a tiny fraction of Recoil's size, Jotai offered better performance, sleeker APIs, and no memory leaks due to its WeakMap-based approach.&lt;/p&gt;

&lt;p&gt;However, it came at the cost of some power - the WeakMap approach makes cache control difficult and sharing state between multiple windows or other realms almost impossible. And the lack of string keys, while sleek, makes debugging a nightmare. Most apps should add those back in, drastically tarnishing Jotai's sleekness.&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="c1"&gt;// a (better?) Jotai atom&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;greetingAtom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;atom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello, World!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;greetingAtom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;debugLabel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;greeting&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few honorable mentions are &lt;a href="https://github.com/artalar/reatom"&gt;Reatom&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/nanostores"&gt;Nanostores&lt;/a&gt;. These libraries have explored more of the theory behind the atomic model and try to push its size and speed to the limit.&lt;/p&gt;

&lt;p&gt;The atomic model is fast and scales very well. But up until very recently, there were a few concerns that no atomic library had addressed very well:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The learning curve. Atoms are &lt;em&gt;different&lt;/em&gt;. How do we make these concepts approachable for React devs?&lt;/li&gt;
&lt;li&gt;Dev X and debugging. How do we make atoms discoverable? How do you track updates or enforce good practices?&lt;/li&gt;
&lt;li&gt;Incremental migration for existing codebases. How do you access external stores? How do you keep existing logic intact? How do you avoid a full rewrite?&lt;/li&gt;
&lt;li&gt;Plugins. How do we make the atomic model extensible? &lt;em&gt;Can&lt;/em&gt; it handle every possible situation?&lt;/li&gt;
&lt;li&gt;Dependency Injection. Atoms naturally define dependencies, but can they be swapped out during testing or in different environments?&lt;/li&gt;
&lt;li&gt;The Law of Demeter. How do we hide implementation details and prevent scattered updates?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where I come in. See, I'm the principle creator of another atomic library:&lt;/p&gt;

&lt;h2&gt;
  
  
  Zedux
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Omnistac/zedux"&gt;Zedux&lt;/a&gt; finally entered the scene a few weeks ago. Developed by a Fintech company in New York - the company I work for - Zedux was not only designed to be fast and scalable, but also to provide a sleek development and debugging experience.&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="c1"&gt;// a Zedux atom&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;greetingAtom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;atom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;greeting&lt;/span&gt;&lt;span class="dl"&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;Hello, World!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I won't go into depth about Zedux's features here - as I said, this article won't focus on the differences between these atomic libraries.&lt;/p&gt;

&lt;p&gt;Suffice it to say that Zedux addresses all the above concerns. For example, it's the first atomic library to offer real Inversion of Control and the first to bring us full-circle back to the Law of Demeter by offering &lt;a href="https://omnistac.github.io/zedux/docs/walkthrough/atom-apis#exports"&gt;atom exports&lt;/a&gt; for hiding implementation details.&lt;/p&gt;

&lt;p&gt;The last ideologies of Flux have finally been revived - not only revived, but improved! - thanks to the atomic model.&lt;/p&gt;

&lt;p&gt;So what exactly &lt;em&gt;is&lt;/em&gt; the atomic model?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Atomic Model
&lt;/h2&gt;

&lt;p&gt;These atomic libraries have many differences - they even have different definitions of what "atomic" means. The general consensus is that atoms are small, isolated, autonomous state containers reactively updated via a Directed Acyclic Graph.&lt;/p&gt;

&lt;p&gt;I know, I know, it sounds complex, but just wait till I explain it with bananas.&lt;/p&gt;

&lt;p&gt;I'm kidding! It's actually really simple:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SZLD3k5Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ttm6mc64fnv4oened3j7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SZLD3k5Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ttm6mc64fnv4oened3j7.png" alt="Update -&amp;gt; UserAtom -&amp;gt; CartAtom -&amp;gt; PromosAtom" width="800" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Updates ricochet through the graph. That's it!&lt;/p&gt;

&lt;p&gt;The point is, regardless of the implementation or the semantics, all of these atomic libraries have revived the concept of multiple stores and made them not only usable, but a real joy to work with.&lt;/p&gt;

&lt;p&gt;The 6 reasons I gave for wanting multiple stores are exactly the reasons why the atomic model is so powerful:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Autonomy - Atoms can be tested and used completely in isolation.&lt;/li&gt;
&lt;li&gt;Code Splitting - Import an atom and use it! No extra considerations required.&lt;/li&gt;
&lt;li&gt;Standardized Primitives - Anything can expose an atom for automatic integration.&lt;/li&gt;
&lt;li&gt;Scalability - Updates affect only a small part of the state tree.&lt;/li&gt;
&lt;li&gt;Destruction - Simply stop using an atom and all its state is garbage collected.&lt;/li&gt;
&lt;li&gt;Colocation - Atoms naturally define their own state, side effects, and update logic.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The simple APIs and scalability alone make atomic libraries an excellent choice for every React app. More power &lt;em&gt;and&lt;/em&gt; less boilerplate than Redux? Is this a dream?&lt;/p&gt;

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

&lt;p&gt;What a journey! The world of React state management never ceases to amaze, and I'm so glad I hitched a ride.&lt;/p&gt;

&lt;p&gt;We're just getting started. There is a lot of room for innovation with atoms. After spending years creating and using Zedux, I've seen how powerful the atomic model can be. In fact, its power is its achilles heel:&lt;/p&gt;

&lt;p&gt;When devs explore atoms, they often dig so deep into the possibilities that they come back saying, "Look at this crazy complex power," rather than, "Look at how simply and elegantly atoms solve this problem." I'm here to change this.&lt;/p&gt;

&lt;p&gt;The atomic model and the theory behind it haven't been taught in a way that's approachable for most React devs. In a way, the React world's experience of atoms so far has been the opposite of Flux:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ivVGV-yj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgflip.com/7kzxa2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ivVGV-yj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgflip.com/7kzxa2.jpg" alt="Atoms in Theory vs Atoms in Practice" width="620" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This article is the second in a series of learning resources I'm producing to help React devs understand how atomic libraries work and why you might want to use one. Check out the first article - Scalability: the Lost Level of React State Management.&lt;/p&gt;

&lt;p&gt;It took 10 years, but the solid CS theory introduced by Flux is finally impacting React apps in a big way thanks to the atomic model. And it will continue to do so for years to come.&lt;/p&gt;

</description>
      <category>react</category>
      <category>typescript</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Scalability: the Lost Level of React State Management</title>
      <dc:creator>Josh Claunch</dc:creator>
      <pubDate>Thu, 01 Jun 2023 14:14:24 +0000</pubDate>
      <link>https://dev.to/josh_claunch/scalability-the-lost-level-of-react-state-management-1h8n</link>
      <guid>https://dev.to/josh_claunch/scalability-the-lost-level-of-react-state-management-1h8n</guid>
      <description>&lt;p&gt;&lt;a href="https://media.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%2Fpy56kqkg6f57gp90zxay.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fpy56kqkg6f57gp90zxay.jpg" alt="cover photo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Original photo by &lt;a href="https://unsplash.com/@trails?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;trail&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/yN1qXz6Hrfw?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(originally posted on &lt;a href="https://hackernoon.com/scalability-the-lost-level-of-react-state-management" rel="noopener noreferrer"&gt;HackerNoon&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;In a recent conversation about state management in React, the topic of scalability came up. I was disappointed, but not at all surprised to hear it written off as an unimportant buzzword.&lt;/p&gt;

&lt;p&gt;Having been a fly on the React state management wall for 8 years, I've seen this general sentiment expressed over and over: All popular React state management solutions are "scalable" and that's all you need to know.&lt;/p&gt;

&lt;p&gt;It isn't.&lt;/p&gt;

&lt;p&gt;Join me for a minute and I'll tell you why.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Scalability?
&lt;/h2&gt;

&lt;p&gt;I hate to be one of those blogs that starts off a lawn-treatment article by defining grass. But the very definition of scalability when it comes to state management has been lost in the React community.&lt;/p&gt;

&lt;p&gt;Allow me to define it.&lt;/p&gt;

&lt;p&gt;"Scalability" refers to a tool's ability to adapt to new challenges over time. Importantly, it goes both ways - scaling up &lt;strong&gt;and scaling down&lt;/strong&gt;. A scalable tool handles every situation well - elegance in every facet of complexity:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fy734us0po46datva39tj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fy734us0po46datva39tj.png" alt="Scalability = Elegance across App Complexities"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This would be a very idealistic state manager. In reality, there is no such silver bullet. Scalability is the artform of getting as close as possible to this perfect plateau.&lt;/p&gt;

&lt;p&gt;Elegance is largely subjective, but there are a few measurable statistics like performance benchmarks, lines of code (read boilerplate), and the number of unique concepts the user is required to know to do a given task. Still, some state managers will be more "scalable" &lt;em&gt;for you&lt;/em&gt;. And that's fine! Just keep this in mind:&lt;/p&gt;

&lt;h3&gt;
  
  
  The Criteria
&lt;/h3&gt;

&lt;p&gt;Apps grow. Teams grow. A scalable state management tool needs to elegantly solve a number of problems at every stage of an app's growth. These problems include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Performance. Bigger apps need to move data quickly and prevent unnecessary rerenders.&lt;/li&gt;
&lt;li&gt;Debugging. Are updates predictable? Can you track down unexpected changes?&lt;/li&gt;
&lt;li&gt;Boilerplate. Tiny apps don't need a bazooka and big apps don't want thousands of them.&lt;/li&gt;
&lt;li&gt;Side effects. Are these defined clearly? Are they flexible and simple?&lt;/li&gt;
&lt;li&gt;Caching/Memoization. Big apps need control over memory usage and tiny apps don't care.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I'll call these the 5 Great Scalability Criteria. There are more considerations like code splitting, testability, extensibility, interoperability, adoptability, even-more-abilities, and the learning curve. But these 5 will do for this article.&lt;/p&gt;

&lt;p&gt;As a general example, an overtly simple state manager might look something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F6v81nfb4b27ho5hkwlyy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F6v81nfb4b27ho5hkwlyy.png" alt="good for small apps, bad for big apps"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A naive solution becomes less useful over time, requiring you to find uglier and more complex workarounds. Such a tool can start to get in your way more often than it helps.&lt;/p&gt;

&lt;p&gt;A truly scalable state manager doesn't drown a tiny app in config files and boilerplate and &lt;em&gt;at the same time&lt;/em&gt; it doesn't leave a big app stranded in a sea of missing features and unstructured simplicity.&lt;/p&gt;

&lt;p&gt;I'm sure this all sounds pretty straightforward. So why do I feel like this art has been lost?&lt;/p&gt;

&lt;h2&gt;
  
  
  React Context
&lt;/h2&gt;

&lt;p&gt;Don't get me started. Nope, too late. React's new(ish) context API is a thing of beauty and a joy to work with, &lt;strong&gt;if&lt;/strong&gt; you don't use it for state management (directly).&lt;/p&gt;

&lt;p&gt;React context is the quintessential example of a non-scalable React state management solution. As an app grows, React context inevitably leads to performance problems and dev X nightmares like indirection, mystery updates, implicit dependencies, and lots of tightly-coupled logic that makes abstraction, testing, and code splitting difficult.&lt;/p&gt;

&lt;p&gt;React context creates as much lock-in as a real state manager - and even more since you'll write the boilerplate yourself. All that added boilerplate also increases the surface area for bugs.&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;// There are many potential footguns with even this many providers - e.g.&lt;/span&gt;
&lt;span class="c1"&gt;// UserProvider creating an implicit dependency on AuthProvider, Routes&lt;/span&gt;
&lt;span class="c1"&gt;// rerendering due to UserProvider's state updating, etc.&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;AuthProvider&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;UserProvider&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;RouteProvider&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;Routes&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;RouteProvider&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;UserProvider&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;AuthProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've seen this happen countless times on Twitter and Reddit:&lt;/p&gt;

&lt;p&gt;Someone complains about how they regret starting with React context and asks what they should use instead.&lt;/p&gt;

&lt;p&gt;Within a few hours, someone else will say, "Hey, I'm new. Where should I start?" and we all jump on and tell the poor guy to start with React context, dooming him to the same fate. This goes on in an endless loop.&lt;/p&gt;

&lt;h3&gt;
  
  
  Are We Ever Gonna Learn?
&lt;/h3&gt;

&lt;p&gt;The advice to use React context for state management is not malicious. In fact, I believe it comes from good intentions. React devs tend to recommend React context for state management because it sounds like a decent, unopinionated, neutral, objective, diplomatic solution that's readily available.&lt;/p&gt;

&lt;p&gt;And it is all of those things. But it's also really bad advice.&lt;/p&gt;

&lt;p&gt;I know that React state management is a polarizing topic. Avoiding it sounds like smooth sailing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgflip.com%2F7myjxh.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgflip.com%2F7myjxh.jpg" alt="React devs vs state management"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But don't forget that competition breeds innovation. In the last 8 years, React state management has evolved in many, many ways that will benefit &lt;em&gt;you&lt;/em&gt;. Don't ignore it!&lt;/p&gt;

&lt;p&gt;To be clear, I'm not talking about &lt;code&gt;useState&lt;/code&gt; and &lt;code&gt;useReducer&lt;/code&gt; by themselves. Those are great tools for keeping local state tied to a component's lifecycle. You should use these even when you have a good state manager.&lt;/p&gt;

&lt;p&gt;I'm talking about the moment when you need to &lt;a href="https://react.dev/learn/sharing-state-between-components" rel="noopener noreferrer"&gt;lift state up&lt;/a&gt; (which you will need to do). Simple props are totally fine, as shown in that linked doc. But when you run into prop drilling problems, have a plan from the very beginning for what you're going to use. And don't use raw React context. Just don't. You'll thank me.&lt;/p&gt;

&lt;h3&gt;
  
  
  Doing It Right
&lt;/h3&gt;

&lt;p&gt;Now, I'm sure you've heard that React context can be used effectively at scale. And that's true (to an extent), &lt;strong&gt;if&lt;/strong&gt; you do it right.&lt;/p&gt;

&lt;p&gt;The main principle for this is using React context for &lt;a href="https://blog.testdouble.com/posts/2021-03-19-react-context-for-dependency-injection-not-state/" rel="noopener noreferrer"&gt;dependency injection, not state management&lt;/a&gt;. That article is an excellent breakdown of this technique. The gist is that React context is a very powerful &lt;strong&gt;low-level&lt;/strong&gt; model that requires at least a thin wrapper to be used for state management.&lt;/p&gt;

&lt;p&gt;The "thin wrapper" can be as simple as an &lt;a href="https://rxjs.dev/api/index/class/BehaviorSubject" rel="noopener noreferrer"&gt;RxJS BehaviorSubject&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Bad:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;UserProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;({})&lt;/span&gt;

  &lt;span class="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;userContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&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;userContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Good:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;UserProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;subject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;RxJS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BehaviorSubject&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;userContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&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;subject&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&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;userContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&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;This is just the (simplified) Provider. To use React context properly, you also have to learn to colocate logic, create hook wrappers, and create a system for triggering and taming rerenders using the pub/sub or similar model.&lt;/p&gt;

&lt;p&gt;Sound like a lot of work? Good, because it is! To use React context properly, you essentially have to roll your own state manager.&lt;/p&gt;

&lt;p&gt;If you're willing to tackle the 5 Great Scalability Criteria yourself, this is totally fine. Just don't go off the deep end with generators and stage 2 ECMAScript proposals. Keep concepts familiar for the sake of future team members and your own future self.&lt;/p&gt;

&lt;p&gt;Alright, you knew &lt;a href="https://github.com/reduxjs/redux" rel="noopener noreferrer"&gt;Redux&lt;/a&gt; was coming.&lt;/p&gt;

&lt;h2&gt;
  
  
  Redux
&lt;/h2&gt;

&lt;p&gt;I'm talking about Redux pre-&lt;a href="https://github.com/reduxjs/redux-toolkit" rel="noopener noreferrer"&gt;RTK&lt;/a&gt; here. Due primarily to its infamous boilerplate, raw Redux has proven to not be a very scalable solution - it's clumsy for small apps to get started with and at the same time is clunkily verbose for large apps.&lt;/p&gt;

&lt;p&gt;Redux did, however, make long strides over previous Flux implementations in other scalability criteria:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;#2 Debugging - Redux's time travel model was revolutionary&lt;/li&gt;
&lt;li&gt;#4 Side Effects - Redux's middleware finally gave these a comfortable home&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgflip.com%2F7ln21i.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgflip.com%2F7ln21i.jpg" alt="We don't talk about Redux Saga"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Redux Toolkit (RTK) finally fixed many of the boilerplate problems. This alone makes RTK a very "scalable" state management solution.&lt;/p&gt;

&lt;p&gt;Redux excels in the moderate-to-fairly-high complexity range. RTK is an overall improvement, but is still a little verbose for very small apps. Overall, I'd draw its scalability like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fq7g7ob2lw8jm7z9pd4o4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fq7g7ob2lw8jm7z9pd4o4.png" alt="RTK vs Redux"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'll cover that sharp drop at the end in a minute, but first:&lt;/p&gt;

&lt;h2&gt;
  
  
  Zustand
&lt;/h2&gt;

&lt;p&gt;This excellent state manager removed even more of Redux's boilerplate at the cost of some debugging power. You can think of &lt;a href="https://github.com/pmndrs/zustand" rel="noopener noreferrer"&gt;Zustand&lt;/a&gt; like a very simplistic RTK that scales down way better and scales up only a little worse.&lt;/p&gt;

&lt;p&gt;I'll cut to the graph:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fav36mz56rfcbxrsufw4h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fav36mz56rfcbxrsufw4h.png" alt="Zustand vs Redux"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since Zustand covers the full spectrum better, altogether, I would call Zustand more "scalable" than RTK. It's more comfortable for small apps to use and will be a steady companion all the way up almost to the extremes of complexity.&lt;/p&gt;

&lt;p&gt;I've seen many people on the RTK train bewildered about Zustand's success when RTK clearly scales up better. I hope the above graph clears this up a little - many apps don't need to scale up that far. But they do want a state manager that scales all the way &lt;em&gt;down&lt;/em&gt; to elegantly handle state in even the initial PoC/prototype phase.&lt;/p&gt;

&lt;p&gt;Alright, it's time to talk about those steep drops at the end.&lt;/p&gt;

&lt;h2&gt;
  
  
  Singleton vs Atomic
&lt;/h2&gt;

&lt;p&gt;Redux and Zustand use a singleton model. You create one store. All side effects and state derivations (selectors) hook into that store.&lt;/p&gt;

&lt;p&gt;This approach has a hard upper limit. I wrote some thoughts about this on &lt;a href="https://github.com/reduxjs/reselect/discussions/491#discussioncomment-5762615" rel="noopener noreferrer"&gt;this Reselect discussion&lt;/a&gt;. The gist is that an app with lots of fast-moving data can start to bottleneck a store with lots of subscribers, reducers, and deep selector graphs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/facebookexperimental/Recoil" rel="noopener noreferrer"&gt;Recoil&lt;/a&gt; introduced a new pattern for storing state and propagating updates. This atomic model has proven to scale up better than the singleton model at the cost of some hefty learning curves.&lt;/p&gt;

&lt;p&gt;Still, it can be very useful in even very small apps. I'd draw its overall scalability something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fjmnfnjntlt9dqvublyhq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fjmnfnjntlt9dqvublyhq.png" alt="Recoil vs RTK"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That slow tail off at the end is 👌 and is why I believe atomic (or similar) models are the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Jotai
&lt;/h2&gt;

&lt;p&gt;Jotai did to Recoil what Zustand did to Redux; it sacrificed a little long-term power for some short-term elegance. This makes Jotai an excellent all-around tool.&lt;/p&gt;

&lt;p&gt;The graph:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Flzb9x42q14nypexmes2f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Flzb9x42q14nypexmes2f.png" alt="Jotai vs Recoil"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please remember that this is just a dude drawing lines on &lt;a href="https://excalidraw.com/" rel="noopener noreferrer"&gt;Excalidraw&lt;/a&gt;. These lines don't accurately reflect each tool's capabilities in every aspect of every app or in the hands of every programmer. I'm trying to communicate only the general "scalability" of these tools from my experience given the scalability criteria I outlined.&lt;/p&gt;

&lt;p&gt;With its simple APIs and very gracefully declining performance and capabilities, Jotai is the closest any of these tools have come to the "perfect plateau" at the beginning of this article.&lt;/p&gt;

&lt;p&gt;Yes, that's right. Jotai is the most scalable state management solution. The end.&lt;/p&gt;

&lt;p&gt;Well alright, &lt;em&gt;almost&lt;/em&gt; the end. I wouldn't be here if there wasn't a little bit more to the story:&lt;/p&gt;

&lt;h2&gt;
  
  
  We Need To Go Deeper
&lt;/h2&gt;

&lt;p&gt;After encountering the hard limit of the singleton model, a few coworkers and I started studying Recoil and Jotai. We loved the concepts and the performance scalability, but determined that they were lacking in other scalability criteria.&lt;/p&gt;

&lt;p&gt;In 2020, I hatched a plan for an in-house solution that would sacrifice only a tiny bit of Jotai's simplicity in exchange for a much more powerful atomic model. Designed from the ground up with the 5 Great Scalability Criteria (and a lot more) in mind, this model scales up better than any of its predecessors and scales down only a little worse than Jotai.&lt;/p&gt;

&lt;p&gt;This new tool started driving our production apps in early 2021 and has been a lifesaver in the extremes of complexity. 2 years later, we have finally open-sourced this tool as &lt;a href="https://github.com/Omnistac/zedux" rel="noopener noreferrer"&gt;"Zedux"&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Zedux
&lt;/h2&gt;

&lt;p&gt;Besides solving better for all of the 5 Great Scalability Criteria, Zedux added many features over Recoil/Jotai like React Query-esque cache management, cross-window support, real Dependency Injection, evaluation tracing, and atom exports. These are fun to say, but I'll write more about them in a separate article.&lt;/p&gt;

&lt;p&gt;Overall, by design, Zedux's scalability looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fohlumath9z6lapjcquwq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fohlumath9z6lapjcquwq.png" alt="Zedux vs Recoil vs Jotai"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Besides a clear bias and my own experience using Zedux in data-intensive applications, the reason why I draw Zedux's scalability so generously is because of the 5 Great Scalability Criteria:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Performance (benchmarks &lt;a href="https://jsbench.me/zxlh0m1p0z" rel="noopener noreferrer"&gt;here&lt;/a&gt;, &lt;a href="https://jsbench.me/y6lh0mv9oc" rel="noopener noreferrer"&gt;here&lt;/a&gt;, &lt;a href="https://jsbench.me/xulh02zune" rel="noopener noreferrer"&gt;here&lt;/a&gt;, &lt;a href="https://jsbench.me/vllhus9i6t" rel="noopener noreferrer"&gt;here&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://omnistac.github.io/zedux/docs/api/injectors/injectWhy" rel="noopener noreferrer"&gt;Debugging&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://omnistac.github.io/zedux/docs/walkthrough/quick-start" rel="noopener noreferrer"&gt;Boilerplate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Simple but completely powerful &lt;a href="https://omnistac.github.io/zedux/docs/api/injectors/injectEffect" rel="noopener noreferrer"&gt;side effects model&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://omnistac.github.io/zedux/docs/walkthrough/destruction" rel="noopener noreferrer"&gt;Cache control features&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There are many more reasons, but I'll limit it to these 5 for this article. Zedux was designed primarily to manage extremely volatile state in big fintech applications and to scale down &lt;em&gt;fairly&lt;/em&gt; well too. But that doesn't mean it does everything perfectly.&lt;/p&gt;

&lt;p&gt;For small apps, Jotai and Zustand are certainly great options. Beyond that, personal preference can also come into play. As I said at the beginning, some tools are more scalable &lt;em&gt;for you&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does Scalability Matter?
&lt;/h2&gt;

&lt;p&gt;Scalability is a general principle, but that doesn't mean that every extreme applies to every app. You are free to analyze your own application and determine which parts of this discussion matter &lt;em&gt;and will ever matter&lt;/em&gt; for it. Just be sure to leave yourself plenty of leeway in your predictions.&lt;/p&gt;

&lt;p&gt;Anything with live-time data visualization may need to scale up performance-wise beyond what the singleton model can handle. Additionally an app that's "just very, very big" might need more power.&lt;/p&gt;

&lt;p&gt;This is all up to you to decide. But no, not all of this applies to every app.&lt;/p&gt;

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

&lt;p&gt;All modern state managers are solid options for most apps, but they scale up and down differently for each of the 5 Great Scalability Criteria.&lt;/p&gt;

&lt;p&gt;Pick one you like that will suit the relative size and future complexity of your app. And don't use React context for state management unless you really want to make a state manager from scratch.&lt;/p&gt;

&lt;p&gt;If the decision is too hard, here's my recommendation as someone who's been using React for 8 years:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pick either Zustand or Jotai if you want something you can start with quickly and that will scale up to moderate complexity (think an e-commerce or forum app).&lt;/li&gt;
&lt;li&gt;Pick RTK if you want something that will scale &lt;em&gt;up&lt;/em&gt; very well to almost the extremes of complexity and has a solid community.&lt;/li&gt;
&lt;li&gt;Pick Zedux if you want all your bases covered - from simplicity to ultimate complexity and everything in-between.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lastly, I know that I've omitted many great tools like &lt;a href="https://github.com/statelyai/xstate" rel="noopener noreferrer"&gt;XState&lt;/a&gt;, &lt;a href="https://github.com/TanStack/query" rel="noopener noreferrer"&gt;React Query&lt;/a&gt;, and &lt;a href="https://github.com/vercel/swr" rel="noopener noreferrer"&gt;SWR&lt;/a&gt;. These tools are utilities that are very scalable in their own right, but aren't full replacements for a good state manager.&lt;/p&gt;

&lt;p&gt;As for other global, atomic, proxy-based, and signal-based state managers, I'm sorry I couldn't get to them all in this article. Feel free to comment or start a discussion in &lt;a href="https://github.com/Omnistac/zedux/discussions" rel="noopener noreferrer"&gt;the Zedux repo&lt;/a&gt; for better details or more comparisons.&lt;/p&gt;

&lt;p&gt;If you get nothing else from this article, just remember that applications grow. Plan for growth and you'll succeed.&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Zedux: Is This the One?</title>
      <dc:creator>Josh Claunch</dc:creator>
      <pubDate>Mon, 24 Apr 2023 20:27:21 +0000</pubDate>
      <link>https://dev.to/josh_claunch/zedux-is-this-the-one-3la0</link>
      <guid>https://dev.to/josh_claunch/zedux-is-this-the-one-3la0</guid>
      <description>&lt;p&gt;(This is a cross-post from the &lt;a href="https://omnistac.github.io/zedux/blog/zedux-is-this-the-one"&gt;Zedux blog&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Zedux is a molecular state engine for React. After years spent as proprietary software hidden in a private GitHub repo, it's officially open-sourced and version 1.0.0 has been released!&lt;/p&gt;

&lt;p&gt;Here's a simple hello world example to start off:&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;atom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useAtomState&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;@zedux/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;greetingAtom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;atom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;greeting&lt;/span&gt;&lt;span class="dl"&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;Hello, World!&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="nx"&gt;Greeting&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;greeting&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setGreeting&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useAtomState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;greetingAtom&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;input&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;event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setGreeting&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;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;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;greeting&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;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Zedux features a composable store model wrapped in a DI-driven atomic architecture. This article will break down why it exists and what problems it solves. If you don't care about all that and just want to learn Zedux, head to the &lt;a href="https://omnistac.github.io/zedux/docs/walkthrough/quick-start"&gt;quick start&lt;/a&gt; or the &lt;a href="https://omnistac.github.io/zedux/examples"&gt;examples&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Dare You Make Another One Of These Things??
&lt;/h2&gt;

&lt;p&gt;Chill. Zedux has a long history. Read some of it &lt;a href="https://omnistac.github.io/zedux/blog/zedux-open-sourced"&gt;here&lt;/a&gt;. It is only recently open-sourced in its current form.&lt;/p&gt;

&lt;p&gt;We made Zedux primarily to fix performance and maintenance problems in a socket-driven app that previously used - wait for iiiit - &lt;a href="https://redux.js.org/"&gt;Redux&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I want to be clear: I love Redux. I still get chills thinking about time travel and the peace of mind its unidirectional data flow and raw immutability give you. The nature of this article demands some comparison, so &lt;em&gt;reluctantly&lt;/em&gt;, here's what we encountered and why Zedux is different - not just from Redux, but many other tools we tested:&lt;/p&gt;

&lt;h2&gt;
  
  
  Stores
&lt;/h2&gt;

&lt;p&gt;As the name indicates, Zedux's store model is inspired by Redux, with the added features of being both zero-config and composable. These stores are very light-weight and are actually fully compatible with time traveling - including undo/redo and replayable actions.&lt;/p&gt;

&lt;p&gt;The main problems we encountered in Redux were with &lt;a href="https://github.com/reduxjs/reselect"&gt;Reselect&lt;/a&gt;'s peformance, &lt;a href="https://redux-saga.js.org/"&gt;Redux Saga&lt;/a&gt;'s ... everything, and Redux's general indirection.&lt;/p&gt;

&lt;p&gt;Having no control over selector evaluation was Redux's biggest performance bottleneck for us. Zedux's atomic model naturally fixes this. For example, take a large selector tree:&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="c1"&gt;// Reselect:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getEntities&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RootState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entities&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getFruits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getEntities&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;entities&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;entities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fruits&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;getApples&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getFruits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fruits&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;fruits&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;apples&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getRipeApples&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getApples&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;apples&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;apples&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apple&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;apple&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isRipe&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getSortedApples&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getRipeApples&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;apples&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;apples&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="c1"&gt;// Zedux:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entities&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;atom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;entities&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getFruits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;AtomGetters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entities&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;fruits&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getOranges&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;select&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;AtomGetters&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;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getFruits&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;oranges&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getRipeOranges&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;select&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;AtomGetters&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;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getOranges&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orange&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;orange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isRipe&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;getSortedOranges&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;select&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;AtomGetters&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;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getRipeOranges&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the tree and derived data get intensive, there's no way to make a selector in the middle of the tree debounce/throttle/buffer updates with Reselect. There are workarounds (we used several) at the cost of more indirection.&lt;/p&gt;

&lt;p&gt;In Zedux, you can turn any selector anywhere in the tree into an atom:&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="c1"&gt;// before:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getRipeOranges&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;select&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;AtomGetters&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;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getOranges&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orange&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;orange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isRipe&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// after (ions are a type of atom specializing in selection):&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ripeOrangesAtom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ripeOranges&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;select&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;oranges&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getOranges&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;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;injectStore&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="c1"&gt;// a simple debounce:&lt;/span&gt;
  &lt;span class="nx"&gt;injectEffect&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;handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;setTimeout&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="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oranges&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orange&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;orange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isRipe&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;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;clearTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;handle&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;oranges&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;store&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Zedux's injectors are just like React hooks, but for atoms. Yep. There's an &lt;a href="https://omnistac.github.io/zedux/docs/api/injectors/injectMemo"&gt;&lt;code&gt;injectMemo()&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://omnistac.github.io/zedux/docs/api/injectors/injectRef"&gt;&lt;code&gt;injectRef()&lt;/code&gt;&lt;/a&gt;, etc. &lt;a href="https://omnistac.github.io/zedux/docs/api/injectors/injectEffect"&gt;&lt;code&gt;injectEffect()&lt;/code&gt;&lt;/a&gt; behaves exactly like React's &lt;code&gt;useEffect()&lt;/code&gt;. The entire debounce operation can be abstracted to a custom injector too, reducing the code to simply:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ripeOrangesAtom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ripeOranges&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;select&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="c1"&gt;// an example injector that handles everything in the previous example:&lt;/span&gt;
  &lt;span class="nx"&gt;injectDebouncedFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getOranges&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;orange&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;orange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isRipe&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 example also demonstrated the efficiency of Zedux's side effects model. Comparing it to Redux Saga or any Redux side effects model is like comparing apples to oranges (surely you knew that was coming) so I'm not gonna try here.&lt;/p&gt;

&lt;p&gt;Colocating state and its side effects is the dream we've all had for a long time in the React world. Turns out &lt;code&gt;injectEffect()&lt;/code&gt; just demonstrated exactly that. This was the 2nd most important feature for us coming from Redux Saga.&lt;/p&gt;

&lt;p&gt;The 3rd problem was Redux's infamous indirection. To trace an event, you have to globally-grep your codebase for string action types and explore the usages to find what you need. Zedux introduces &lt;a href="https://omnistac.github.io/zedux/docs/walkthrough/atom-apis#exports"&gt;atom exports&lt;/a&gt; which give you automatic go-to-definition and find-all-references support in VS Code, not to mention you write less code, colocate callbacks with state, and get automatic TypeScript support.&lt;/p&gt;

&lt;h2&gt;
  
  
  Atoms
&lt;/h2&gt;

&lt;p&gt;Atoms control the lifecycle and visibility of state. They give you a place to create side effects, callbacks, and suspense promises and manage resource destruction. They also enable Zedux's powerful DI model (similar to &lt;a href="https://angular.io/guide/architecture-modules"&gt;Modules&lt;/a&gt; in Angular).&lt;/p&gt;

&lt;p&gt;Here's another simple example of state + side effect colocation:&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;atom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;injectEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;injectStore&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;@zedux/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;counterAtom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;atom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;counter&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;injectStore&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="nx"&gt;injectEffect&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;handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;setInterval&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;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;clearInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;handle&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="nx"&gt;store&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;atom()&lt;/code&gt; factory returns an "atom template". Zedux uses this to dynamically create atoms (or "atom instances" as we call them). These atom instances are created when the template is used e.g. in a React 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="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Counter&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;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useAtomState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;counterAtom&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;
      Count: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;count&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;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="nx"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&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;Reset&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;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The side effect kicks off as soon as a counter atom is instantiated from the &lt;code&gt;counterAtom&lt;/code&gt; template. &lt;a href="https://omnistac.github.io/zedux/docs/api/hooks/useAtomState"&gt;&lt;code&gt;useAtomState()&lt;/code&gt;&lt;/a&gt; is similar to React's &lt;code&gt;useState()&lt;/code&gt; hook. It subscribes to updates in the counter atom instance's store.&lt;/p&gt;

&lt;p&gt;This effect also cleans up after itself when the atom is destroyed. Besides the colocation, Zedux's side effects model also encourages decoupling side effects from React components - as most side effects should be.&lt;/p&gt;

&lt;p&gt;The atomic model of Zedux is inspired by Recoil and Jotai (the latter of which was in turn inspired by Zustand). We created it after trialing these other tools and determining they weren't stable or powerful enough for what we needed at Omnistac.&lt;/p&gt;

&lt;p&gt;Zedux is more powerful than its atomic predecessors and it isn't close. It boasts many new features like &lt;a href="https://omnistac.github.io/zedux/docs/walkthrough/atom-apis#exports"&gt;atom exports&lt;/a&gt;, &lt;a href="https://omnistac.github.io/zedux/docs/walkthrough/overrides"&gt;real DI&lt;/a&gt;, &lt;a href="https://omnistac.github.io/zedux/docs/walkthrough/query-atoms"&gt;query atoms&lt;/a&gt;, &lt;a href="https://omnistac.github.io/zedux/docs/walkthrough/side-effects#injectwhy"&gt;evaluation tracing&lt;/a&gt;, &lt;a href="https://omnistac.github.io/zedux/docs/walkthrough/react-context"&gt;React context control&lt;/a&gt;, &lt;a href="https://omnistac.github.io/zedux/docs/walkthrough/destruction"&gt;cache management&lt;/a&gt;, &lt;a href="https://omnistac.github.io/zedux/docs/advanced/more-patterns#recursive-atoms"&gt;recursive atoms&lt;/a&gt;, and stable &lt;a href="https://omnistac.github.io/zedux/docs/api/injectors/injectEffect"&gt;side effects&lt;/a&gt; and &lt;a href="https://omnistac.github.io/zedux/docs/advanced/plugins"&gt;plugins&lt;/a&gt; models, just to name a few (yes, there's a lot more).&lt;/p&gt;

&lt;p&gt;The biggest differences conceptually are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Zedux separates the state management layer (stores) from the architecture layer (atoms). This is the secret behind Zedux's powerful DI.&lt;/li&gt;
&lt;li&gt;Zedux atoms always evaluate synchronously. This keeps asynchrony out of the atom graph and gives you complete control over it. Zedux has especially good interoperability with RxJS and websockets. It also supports suspense.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The automatic promise cascading of &lt;a href="https://recoiljs.org/docs/guides/asynchronous-data-queries/"&gt;Recoil&lt;/a&gt; and &lt;a href="https://jotai.org/docs/guides/async"&gt;Jotai&lt;/a&gt; sounds cool - and can be cool, don't ge me wrong. However, in my experience, it is one of the main points of confusion for newcomers to the atomic model. It also makes controlling big selector graphs difficult (just like in Reselect).&lt;/p&gt;

&lt;p&gt;Turns out, that feature is not a requirement for the atomic model to work. A synchronous atom graph is &lt;em&gt;much&lt;/em&gt; simpler to reason about &lt;em&gt;and&lt;/em&gt; gives you more control. If you found the atomic model confusing or unapproachable before, you may have a very different experience with Zedux.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cache Management
&lt;/h2&gt;

&lt;p&gt;We loved &lt;a href="https://tanstack.com/query/latest/docs/react/overview"&gt;React Query&lt;/a&gt;. Really, it's a great tool. The downsides &lt;strong&gt;for us&lt;/strong&gt; were the lack of first-class socket support, the tight-coupling of side effects to React components, and, really, the community that seems insistent on downsizing UI state management to the bare minimum (not an option in our UI-state-intensive applications).&lt;/p&gt;

&lt;p&gt;We didn't need any of React Query's pagination/infinite scroll/etc helpers. But we loved React Query's cache management ideas. We gave atoms the capability of managing promise state, which gave us all the React Query-esque power we needed. Combined with injectors, this model has the potential to support everything React Query can do. Add to that Zedux's powerful DI and natural decoupling from components, and there is a &lt;em&gt;lot&lt;/em&gt; of potential for some powerful cache management. We may just make a &lt;code&gt;@zedux/query&lt;/code&gt; package someday. But I digress.&lt;/p&gt;

&lt;p&gt;Zedux's &lt;a href="https://omnistac.github.io/zedux/docs/walkthrough/ecosystems"&gt;Ecosystems&lt;/a&gt; are patterned after React Query's &lt;a href="https://tanstack.com/query/latest/docs/react/reference/QueryClient"&gt;QueryClient&lt;/a&gt;. These are isolated atom environments that are usable and testable completely outside React and easily plugged into React via an &lt;a href="https://omnistac.github.io/zedux/docs/api/components/EcosystemProvider"&gt;&lt;code&gt;&amp;lt;EcosystemProvider&amp;gt;&lt;/code&gt;&lt;/a&gt;.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ecosystem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createEcosystem&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&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;instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ecosystem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;counterAtom&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newState&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&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;state changed:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newState&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Zedux atoms can be given a &lt;a href="https://omnistac.github.io/zedux/docs/walkthrough/destruction#instance-destruction"&gt;TTL&lt;/a&gt; (Time To Live), which is patterned after React Query's &lt;code&gt;cacheTime&lt;/code&gt;. This applies to all atoms, not just query atoms, meaning you have this powerful cache management for your UI state too.&lt;/p&gt;

&lt;p&gt;Zedux atoms can also be given params. These actually work &lt;em&gt;exactly&lt;/em&gt; like query params in React Query. Different sets of params create different atom instances of an atom template. Reusing the same params (according to a &lt;a href="https://tanstack.com/query/latest/docs/react/guides/query-keys#query-keys-are-hashed-deterministically"&gt;deterministic hash&lt;/a&gt;) tells Zedux to reuse a cached atom instance.&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;atom&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;@zedux/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// a `fetch` wrapper complete with promise state management and destruction&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetcherAtom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;atom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fetcher&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;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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&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="nx"&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="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())),&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// keep stale instances around for 1 minute.&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;FetchTwoUsers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// create 2 different atom instances of the `fetcherAtom` template:&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;joe&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useAtomValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetcherAtom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/users/joe&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;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;bob&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useAtomValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetcherAtom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/users/bob&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the &lt;code&gt;FetchTwoUsers&lt;/code&gt; component unmounts (assuming it's the only place where the &lt;code&gt;fetcherAtom&lt;/code&gt; template is used with these exact params), both of these atoms will become stale. If the component remounts within 1 minute, they'll be revived, otherwise they'll be destroyed 1 minute after the component unmounts.&lt;/p&gt;

&lt;p&gt;In Zedux, atoms go stale as soon as they're no longer in use. There are several ways to force invalidation, reevaluation, and destruction. Check out &lt;a href="https://omnistac.github.io/zedux/docs/walkthrough/query-atoms"&gt;the docs&lt;/a&gt; for more info.&lt;/p&gt;

&lt;p&gt;Zedux atoms excel at managing both UI data (like Zustand and Redux) and server data (like React Query). The automatic integration between both types of state is a huge plus. &lt;strong&gt;However&lt;/strong&gt;, Zedux is not (currently) a full replacement for React Query - it doesn't provide any pagination/refetch/etc helpers out of the box. It is possible to dual-wield both tools like many people do with Zustand + React Query. So. Do you dare wield that much power?&lt;/p&gt;

&lt;h2&gt;
  
  
  Combo Deal
&lt;/h2&gt;

&lt;p&gt;To sum up all these comparisons (and more that I could make but will spare you the melodrama), Zedux is the result of 5+ years of studying the React state management ecosystem. We borrowed (yes, &lt;em&gt;borrowed&lt;/em&gt;) ideas from dozens of tools and put them all together into one powerhouse of a state management library.&lt;/p&gt;

&lt;p&gt;You can think of Zedux atoms as a cross between Recoil's &lt;code&gt;atom&lt;/code&gt;, &lt;code&gt;atomFamily&lt;/code&gt;, &lt;code&gt;selector&lt;/code&gt;, and &lt;code&gt;selectorFamily&lt;/code&gt;, a simplistic version of React Query's queries and mutations, with every capability of Redux and Jotai in there too and then some.&lt;/p&gt;

&lt;p&gt;Zedux is brand new to the open-source scene. No community plugins exist for it yet, but the potential is sky high. We crafted a uniquely powerful foundation with &lt;a href="http://localhost:3000/zedux/docs/about/introduction#standardized-primitives"&gt;standardized state primitives&lt;/a&gt; and &lt;a href="http://localhost:3000/zedux/docs/advanced/plugins"&gt;plugin&lt;/a&gt; support that make it capable of everything any other tool can do and then some. While it's a top contender out of the box, its full potential has not even been realized in all aspects yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design Considerations
&lt;/h2&gt;

&lt;p&gt;All APIs in Zedux were created in TypeScript from the ground up. Zedux exports lots of &lt;a href="https://omnistac.github.io/zedux/docs/advanced/typescript-tips"&gt;utility types&lt;/a&gt; for working with atoms and stores. The docs give several tips for TS users and the API docs include full type defs for the adventurous.&lt;/p&gt;

&lt;p&gt;On top of this, we accounted for many things with Zedux from the very beginning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Minimal boilerplate (yes, even less than Redux Toolkit). Zero config. Plug and play.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scalable performance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Granular control over selector evaluation, memoization details, and component rerenders.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Total control over state Time To Live and destruction.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Full, easy control over side effects - especially good RxJS support.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Incrementally adoptable - we needed to dual-wield Redux and Zedux for a while before we finished migrating to Zedux.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Lazy-loading support - especially the ability to stream and cache data on-demand.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Conducive to micro frontend architectures (really, if your app uses code splitting, you may find Zedux is a joy to work with).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Able to take advantage of React context to control state in different component branches.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Testable. You can use Zedux completely outside React.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SSR-compatible.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Time travel debugging, including replayable actions and undo/redo.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Plugin compatible - there are many, many ways to extend Zedux's functionality - from extending its classes to creating custom injectors to creating a full-fledged ecosystem &lt;a href="https://omnistac.github.io/zedux/docs/advanced/plugins"&gt;plugin&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Plus many, many considerations with specific APIs - including consistent naming conventions, TS support, and keeping the learning curve as small as possible.&lt;/p&gt;

&lt;p&gt;Alright, if you want more wordy stuff, check out the &lt;a href="https://omnistac.github.io/zedux/docs/about/introduction"&gt;introduction&lt;/a&gt;. Or if you want to really learn Zedux, dig into the &lt;a href="https://omnistac.github.io/zedux/docs/advanced/more-patterns#recursive-atoms"&gt;quick start&lt;/a&gt; or the &lt;a href="https://omnistac.github.io/zedux/examples"&gt;examples&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;If you're a React dev, you're no stranger to the morass of state management tooling that we've accumulated over the last 8 years or so. I'm sure you've long-since decided that you're sick of it. What we have is good enough. We don't need to complicate state management any further.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8CIHRqF2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgflip.com/7j9721.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8CIHRqF2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.imgflip.com/7j9721.jpg" alt="My React state manager is better than yours" width="500" height="756"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well, I'd almost like to believe that if a truly amazing tool came along, we would set aside our differences and rejoice that our lives just got a little easier. But such a belief would discredit our differences. Our differences give us a lot to teach each other. Critiquing is great - it stimulates progress. If I could ask one favor though: Be kind (not to me! You can tear me to pieces. Just pretty please don't go rip into Redux after reading this 🙏).&lt;/p&gt;

&lt;p&gt;So is Zedux the one we've all been waiting for? Well, sadly, there is no silver bullet. But if you've read this far, there is a decent chance that it's the state manager &lt;em&gt;you've&lt;/em&gt; been waiting for.&lt;/p&gt;

&lt;p&gt;Either way, that's the wrong question. The next generation of React state management tooling is here. The real question is: What are you going to build with it?&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>react</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Zedux Open-Sourced</title>
      <dc:creator>Josh Claunch</dc:creator>
      <pubDate>Mon, 24 Apr 2023 20:24:05 +0000</pubDate>
      <link>https://dev.to/josh_claunch/zedux-open-sourced-m66</link>
      <guid>https://dev.to/josh_claunch/zedux-open-sourced-m66</guid>
      <description>&lt;p&gt;(This is a cross-post from the &lt;a href="https://omnistac.github.io/zedux/blog/zedux-open-sourced"&gt;Zedux blog&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Zedux: A Molecular State Engine for React is officially open-sourced and version 1.0.0 has been released!&lt;/p&gt;

&lt;p&gt;Check it out on GitHub &lt;a href="https://github.com/Omnistac/zedux"&gt;here&lt;/a&gt;. Check out the docs &lt;a href="https://omnistac.github.io/zedux/"&gt;here&lt;/a&gt;. Check out the introduction &lt;a href="https://omnistac.github.io/zedux/docs/about/introduction"&gt;here&lt;/a&gt;. Check out the quick start &lt;a href="https://omnistac.github.io/zedux/docs/walkthrough/quick-start"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This article will give a brief history of Zedux while trying to stay high-level. If you like your announcements detailed and mixed with a little controversy, check out the follow-up article, &lt;a href="https://omnistac.github.io/zedux/blog/zedux-is-this-the-one"&gt;"Zedux: Is this the one?"&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Wow, we're finally here! It has been a journey. Grab a seat for a minute and I'll tell you about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Journey
&lt;/h2&gt;

&lt;p&gt;It started in 2017 with me. I'd been working with React for several years at that point. I'd also spent inordinate amounts of time studying state management tools in React. It was a topic I loved, and I don't know why.&lt;/p&gt;

&lt;p&gt;I started experimenting with making my own React state library. Not because I was dissatisfied with existing tools - quite the opposite. I wanted to learn everything about them. The best way to learn how an engine works is to break it apart. And that's exactly what I spent way too much time doing. Well, minus the engine.&lt;/p&gt;

&lt;p&gt;Side note before you grab your swords: I'm not gonna mention any React state management tools by name in this article. There'll be places for that. Not here.&lt;/p&gt;

&lt;p&gt;Alright. Back to the story.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Story (still)
&lt;/h2&gt;

&lt;p&gt;Once upon a busy day, an idea for a flux-inspired composable store model came flying into my brain. Several months of prototyping, reworking, and tweaking later, I was sitting on some Rather Cool Composable Code.&lt;/p&gt;

&lt;p&gt;I called it Zedux. Yes. The Z is for Zero-config, which is one of its key features. Really, it had only one problem:&lt;/p&gt;

&lt;p&gt;It was useless. Composable stores were cool, sure. The buzzword status was off the charts. Say it with me: Composable stores. Composable stores. Sounds like a React dev's dream. Oh and don't forget zero-config.&lt;/p&gt;

&lt;p&gt;Well, turns out it wasn't as &lt;em&gt;useful&lt;/em&gt; as it sounded. Don't get me wrong, these composable stores (I said it again, didn't I) were at least as useful as most tools out there at the time. But they didn't really stand out. They didn't fill a niche.&lt;/p&gt;

&lt;p&gt;Long story short, I lost motivation and &lt;em&gt;sat&lt;/em&gt; on that code. There we were, the code and I, just &lt;em&gt;sitting&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sitting
&lt;/h2&gt;

&lt;p&gt;In the middle of landing my dream job in 2018, the recruiter mentioned that Zedux might be useful in their state-intensive bond trading software. Well, maybe. But I wasn't done sitting yet.&lt;/p&gt;

&lt;p&gt;Over the next few years, I worked with other state management tools and examined them in our demanding apps - taking notes when they fell short in a big codebase and with highly volatile state.&lt;/p&gt;

&lt;p&gt;Fast forward to 2020. Our demanding application was starting to demand too much of our tooling. Our selectors were slow. Our side effects were everywhere. We needed something with more scalable Dev X and runtime perf.&lt;/p&gt;

&lt;p&gt;A few of us spent lots of time playing with other tools in our app. We &lt;em&gt;loved&lt;/em&gt; the atomic model and server cache management tools. We tested several thoroughly, but finally concluded that there was nothing stable and powerful enough to satisfy our socket-driven, very volatile app's needs.&lt;/p&gt;

&lt;p&gt;After a series of fortuitous events, Zedux came up, and I realized that I was done sitting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resurrecting Zedux
&lt;/h2&gt;

&lt;p&gt;The atomic model was exactly what the composable store model was missing. After lots of discussion and prototyping, we dedicated 3 months to building an atomic architecture around Zedux's existing composable store model. This had many requirements, and in the end the whole codebase was completely reworked.&lt;/p&gt;

&lt;p&gt;A few months later, Zedux became a roaring success. It took its place at the helm, powering all our apps in early 2021.&lt;/p&gt;

&lt;p&gt;Turns out, the separation between the state management layer (stores) and the architecture layer (atoms) is the key to unlocking powerful DI and inter-store communication the likes of which no flux-based app has ever seen.&lt;/p&gt;

&lt;p&gt;We got permission to open-source the new library later that year. A few API iterations and way too much documentation-writing later, we find ourselves in 2023 with a long-overdue announcement:&lt;/p&gt;

&lt;h2&gt;
  
  
  Finale
&lt;/h2&gt;

&lt;p&gt;As of today, April 24, 2023, Zedux is officially open-sourced and stable at version 1.0.0!&lt;/p&gt;

&lt;p&gt;Check out the project on GitHub &lt;a href="https://github.com/Omnistac/zedux"&gt;here&lt;/a&gt;. All feedback is very welcome. Tell us what you think! You can &lt;a href="https://github.com/Omnistac/zedux/discussions"&gt;open a discussion&lt;/a&gt; on GitHub or &lt;a href="https://twitter.com/josh_claunch"&gt;tag/DM me&lt;/a&gt; on twitter. Depending on interest, we may start a discord community too.&lt;/p&gt;

&lt;p&gt;If you were hoping this article would get down to the nitty-gritty details and throw some mud at other tools, you may be interested in the longer follow-up article, &lt;a href="https://omnistac.github.io/zedux/blog/zedux-is-this-the-one"&gt;"Zedux: Is this the one?"&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you read this history lesson all the way to the end, my hat is all the way off to you. It has been quite a journey so far, but it's really only just beginning. Here's to all the mind-blowing tools we'll build together 🥂&lt;/p&gt;

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