<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Mark&#39;s Dev Blog</title>
    <link>https://blog.isquaredsoftware.com/tags/redux/index.xml</link>
    <description>Recent content on Mark&#39;s Dev Blog</description>
    <generator>Hugo -- gohugo.io</generator>
    <atom:link href="https://blog.isquaredsoftware.com/tags/redux/index.xml" rel="self" type="application/rss+xml" />
    
    <item>
      <title>Presentations: Maintaining a Library and a Community</title>
      <link>https://blog.isquaredsoftware.com/2024/11/presentations-maintaining-community/</link>
      <pubDate>Thu, 21 Nov 2024 21:00:00 -0500</pubDate>
      
      <guid>https://blog.isquaredsoftware.com/2024/11/presentations-maintaining-community/</guid>
      <description>&lt;p&gt;&lt;/p&gt;

&lt;p&gt;The main thesis of this talk is that maintainers do a &lt;em&gt;lot&lt;/em&gt; more than just write features and fix bugs, and that really most of what we do involves thinking about and interacting with the users of our library. That includes answering support questions, writing docs, thinking about how versioning and backwards compatibility will impact people, and a lot more.&lt;/p&gt;

&lt;p&gt;Hopefully this proves useful to other maintainers, and peels back the curtain on what it&#39;s like to maintain a widely-used library.&lt;/p&gt;

&lt;h2 id=&#34;react-summit-us-2024&#34;&gt;React Summit US 2024&lt;/h2&gt;

&lt;p&gt;I first did this talk at React Summit US in November 2024:&lt;/p&gt;

&lt;h3 id=&#34;maintaining-a-library-and-a-community-video-https-gitnation-com-contents-maintaining-a-library-and-a-community&#34;&gt;&lt;a href=&#34;https://gitnation.com/contents/maintaining-a-library-and-a-community&#34;&gt;Maintaining a Library and a Community - video&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;And here&#39;s the slides:&lt;/p&gt;

&lt;h3 id=&#34;maintaining-a-library-and-a-community-slides-presentations-2024-11-maintaining-community&#34;&gt;&lt;a href=&#34;https://blog.isquaredsoftware.com/presentations/2024-11-maintaining-community/&#34;&gt;Maintaining a Library and a Community - slides&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;I also got to participate in a panel discussion on &amp;quot;The Future of React&amp;quot;, with members of the React team and the community. This was a pretty good discussion:&lt;/p&gt;

&lt;h3 id=&#34;the-future-of-react-panel-video-https-gitnation-com-contents-panel-discussion-future-of-react&#34;&gt;&lt;a href=&#34;https://gitnation.com/contents/panel-discussion-future-of-react&#34;&gt;The Future of React panel - video&lt;/a&gt;&lt;/h3&gt;

&lt;h2 id=&#34;ndc-oslo-2025&#34;&gt;NDC Oslo 2025&lt;/h2&gt;

&lt;p&gt;For NDC Oslo, I significantly expanded and updated the content and slides. Part of that is that I had an entire hour time slot instead of just 20 minutes, but also I thought of a lot more things to say :)&lt;/p&gt;

&lt;h3 id=&#34;maintaining-a-library-and-a-community-slides-presentations-2025-05-maintaining-community&#34;&gt;&lt;a href=&#34;https://blog.isquaredsoftware.com/presentations/2025-05-maintaining-community/&#34;&gt;Maintaining a Library and a Community - slides&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;Video should be available online about a month after the conference, and I&#39;ll update this post once it&#39;s available.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://www.youtube.com/live/42kUqbV3wIc?si=asXlCveJ_o660adX&#34;&gt;https://www.youtube.com/live/42kUqbV3wIc?si=asXlCveJ_o660adX&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>React Advanced 2024: Designing Effective Documentation</title>
      <link>https://blog.isquaredsoftware.com/2024/11/presentations-designing-documentation/</link>
      <pubDate>Thu, 21 Nov 2024 20:00:00 -0500</pubDate>
      
      <guid>https://blog.isquaredsoftware.com/2024/11/presentations-designing-documentation/</guid>
      <description>&lt;p&gt;&lt;/p&gt;

&lt;p&gt;My remote presentation for React Advanced 2024 was on &amp;quot;Designing Effective Documentation: Lessons Learned Writing the Redux Docs&amp;quot;.&lt;/p&gt;

&lt;p&gt;I&#39;ve spent a &lt;em&gt;lot&lt;/em&gt; of my time working on the Redux docs, and it is in fact &lt;a href=&#34;https://blog.isquaredsoftware.com/2016/09/how-i-got-here-my-journey-into-the-world-of-redux-and-open-source/&#34;&gt;how I got my start working on Redux!&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Hopefully this proves useful to other folks writing docs for tools and libraries.&lt;/p&gt;

&lt;h3 id=&#34;designing-effective-documentation-video-https-gitnation-com-contents-designing-effective-documentation-lessons-learned-building-the-redux-docs&#34;&gt;&lt;a href=&#34;https://gitnation.com/contents/designing-effective-documentation-lessons-learned-building-the-redux-docs&#34;&gt;Designing Effective Documentation - video&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;And here&#39;s the slides:&lt;/p&gt;

&lt;h3 id=&#34;designing-effective-documentation-slides-presentations-2024-10-designing-documentation&#34;&gt;&lt;a href=&#34;https://blog.isquaredsoftware.com/presentations/2024-10-designing-documentation/&#34;&gt;Designing Effective Documentation - slides&lt;/a&gt;&lt;/h3&gt;</description>
    </item>
    
    <item>
      <title>React Summit 2024: Why Use Redux Today?</title>
      <link>https://blog.isquaredsoftware.com/2024/07/presentations-why-use-redux/</link>
      <pubDate>Tue, 09 Jul 2024 23:00:00 -0500</pubDate>
      
      <guid>https://blog.isquaredsoftware.com/2024/07/presentations-why-use-redux/</guid>
      <description>&lt;p&gt;&lt;/p&gt;

&lt;p&gt;I had the chance to speak at both React Connection Paris in April and React Summit Amsterdam in June, and at both conferences I gave a talk on &amp;quot;Why Use Redux Today?&amp;quot;.&lt;/p&gt;

&lt;p&gt;Both of those had the same core content, but I tweaked and updated the slides for React Summit.&lt;/p&gt;

&lt;h3 id=&#34;why-use-redux-today-video-https-portal-gitnation-org-contents-why-you-should-use-redux-in-2024&#34;&gt;&lt;a href=&#34;https://portal.gitnation.org/contents/why-you-should-use-redux-in-2024&#34;&gt;Why Use Redux Today? - video&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;and the earlier livestream:&lt;/p&gt;

&lt;h3 id=&#34;why-use-redux-today-livestream-https-www-youtube-com-live-zzl5rejx3-q-si-szox9hgk4kkr1-wn-t-11802&#34;&gt;&lt;a href=&#34;https://www.youtube.com/live/ZZl5REJX3-Q?si=sZox9HGk4KKr1-Wn&amp;amp;t=11802&#34;&gt;Why Use Redux Today? - livestream&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;And here&#39;s the slides:&lt;/p&gt;

&lt;h3 id=&#34;why-use-redux-today-slides-presentations-2024-06-why-use-redux-today&#34;&gt;&lt;a href=&#34;https://blog.isquaredsoftware.com/presentations/2024-06-why-use-redux-today/&#34;&gt;Why Use Redux Today? - slides&lt;/a&gt;&lt;/h3&gt;</description>
    </item>
    
    <item>
      <title>React Summit US 2023: What&#39;s New in Redux Toolkit 2.0</title>
      <link>https://blog.isquaredsoftware.com/2023/11/presentations-rtk-2.0-new/</link>
      <pubDate>Mon, 13 Nov 2023 23:00:00 -0500</pubDate>
      
      <guid>https://blog.isquaredsoftware.com/2023/11/presentations-rtk-2.0-new/</guid>
      <description>&lt;p&gt;&lt;/p&gt;

&lt;p&gt;I gave a lightning talk at React Summit US, and did a quick run-through of what&#39;s changing in RTK 2.0.&lt;/p&gt;

&lt;p&gt;I&#39;ve linked the livestream at the right timestamp for now, and will link the final video when it&#39;s live.&lt;/p&gt;

&lt;h3 id=&#34;what-s-new-in-redux-toolkit-2-0-video-https-www-youtube-com-live-gk3ilmqkmg4-si-ulkg2j5rgrc00mr3-t-17718&#34;&gt;&lt;a href=&#34;https://www.youtube.com/live/gk3ILmQKMg4?si=UlKg2j5rgrc00Mr3&amp;amp;t=17718&#34;&gt;What&#39;s New in Redux Toolkit 2.0 - video&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;And here&#39;s the slides:&lt;/p&gt;

&lt;h3 id=&#34;what-s-new-in-redux-toolkit-2-0-slides-presentations-2023-11-rtk-2-0-new&#34;&gt;&lt;a href=&#34;https://blog.isquaredsoftware.com/presentations/2023-11-rtk-2.0-new/&#34;&gt;What&#39;s New in Redux Toolkit 2.0 - slides&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;(and totally unrelated to my talk, had the chance to lead a Carnival Parade of costumed characters as part of Kathleen McMahon&#39;s demonstration of &amp;quot;why you should never include a carousel in your design system!&amp;quot;)&lt;/p&gt;

&lt;h3 id=&#34;kathleen-mcmahon-carnival-video-https-www-youtube-com-live-pnptgva7pqk-si-03rvfmqn20-lzlzx-t-22261&#34;&gt;&lt;a href=&#34;https://www.youtube.com/live/PNPtgva7PQk?si=03RvfmqN20_lZLzX&amp;amp;t=22261&#34;&gt;Kathleen McMahon: Carnival!!! - video&lt;/a&gt;&lt;/h3&gt;</description>
    </item>
    
    <item>
      <title>React Rally 2023 - A (Brief) Guide to React Rendering Behavior</title>
      <link>https://blog.isquaredsoftware.com/2023/08/presentations-react-rendering-behavior/</link>
      <pubDate>Wed, 16 Aug 2023 14:00:00 -0500</pubDate>
      
      <guid>https://blog.isquaredsoftware.com/2023/08/presentations-react-rendering-behavior/</guid>
      <description>&lt;p&gt;&lt;/p&gt;

&lt;p&gt;My extensive post &lt;a href=&#34;https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-mostly-complete-guide-to-react-rendering-behavior/&#34;&gt;&amp;quot;A (Mostly) Complete Guide to React Rendering Behavior&amp;quot;&lt;/a&gt; is the most popular and widely appreciated post I&#39;ve written. After the recent updates to cover React 18, it&#39;s now around 10,900 words long!&lt;/p&gt;

&lt;p&gt;I recently had a chance to give a talk based on that post for React Advanced in 2022 and React Rally in 2023.&lt;/p&gt;

&lt;h2 id=&#34;react-rally-2023&#34;&gt;React Rally 2023&lt;/h2&gt;

&lt;p&gt;I&#39;ll link the video once it&#39;s available.&lt;/p&gt;

&lt;h3 id=&#34;a-brief-guide-to-react-rendering-behavior-slides-presentations-2023-08-guide-react-rendering-behavior&#34;&gt;&lt;a href=&#34;https://blog.isquaredsoftware.com/presentations/2023-08-guide-react-rendering-behavior&#34;&gt;A (Brief) Guide to React Rendering Behavior - slides&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id=&#34;a-brief-guide-to-react-rendering-behavior-livestream-video-https-www-youtube-com-live-pxcs9iahcie-si-ld9pmqdagb5gi0w7-t-20666&#34;&gt;&lt;a href=&#34;https://www.youtube.com/live/pxCs9IAhciE?si=lD9PmQdaGB5GI0W7&amp;amp;t=20666&#34;&gt;A (Brief) Guide to React Rendering Behavior - livestream video&lt;/a&gt;&lt;/h3&gt;

&lt;h2 id=&#34;react-advanced-2022&#34;&gt;React Advanced 2022&lt;/h2&gt;

&lt;h3 id=&#34;a-brief-guide-to-react-rendering-behavior-video-https-portal-gitnation-org-contents-a-guide-to-react-rendering-behavior&#34;&gt;&lt;a href=&#34;https://portal.gitnation.org/contents/a-guide-to-react-rendering-behavior&#34;&gt;A (Brief) Guide to React Rendering Behavior - video&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;And here&#39;s the slides:&lt;/p&gt;

&lt;h3 id=&#34;a-brief-guide-to-react-rendering-behavior-slides-presentations-2022-10-guide-react-rendering-behavior&#34;&gt;&lt;a href=&#34;https://blog.isquaredsoftware.com/presentations/2022-10-guide-react-rendering-behavior/&#34;&gt;A (Brief) Guide to React Rendering Behavior - slides&lt;/a&gt;&lt;/h3&gt;</description>
    </item>
    
    <item>
      <title>Presentations: 2022 Podcasts</title>
      <link>https://blog.isquaredsoftware.com/2022/12/presentations-2022-podcasts/</link>
      <pubDate>Sun, 11 Dec 2022 12:00:00 -0500</pubDate>
      
      <guid>https://blog.isquaredsoftware.com/2022/12/presentations-2022-podcasts/</guid>
      <description>&lt;p&gt;&lt;/p&gt;

&lt;p&gt;I had the opportunity to talk on a number of different podcasts and interviews over the course of the year. I&#39;ve gathered them here as a directory.&lt;/p&gt;

&lt;h2 id=&#34;thisdot-tracy-lee-how-to-contribute-to-redux&#34;&gt;ThisDot / Tracy Lee: How to Contribute to Redux&lt;/h2&gt;

&lt;p&gt;Tracy Lee is a prolific podcaster, and her company ThisDot creates numerous shows related to the JS ecosystem. She had me on as part of a new &amp;quot;How to Contribute...&amp;quot; series, where I talked about ways to contribute to the Redux libraries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=rPJmnxDX9lY&#34;&gt;https://www.youtube.com/watch?v=rPJmnxDX9lY&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&#34;kevin-ghadyani-redux-vs-react-context&#34;&gt;Kevin Ghadyani: Redux vs React Context&lt;/h2&gt;

&lt;p&gt;Kevin and I have done a couple long-form video discussions in the last couple years, and he&#39;s split those out into several separate videos.&lt;/p&gt;

&lt;p&gt;This discussion was actually recorded in mid-2021, but just recently got published. We talked about the actual differences between Context and Redux, and why so many people get confused about the differences.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=1bIdaWVMFkc&#34;&gt;https://www.youtube.com/watch?v=1bIdaWVMFkc&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&#34;podrocket-time-travel-debugging-with-replay-with-jason-laster&#34;&gt;PodRocket: Time-Travel Debugging with Replay (with Jason Laster)&lt;/h2&gt;

&lt;p&gt;I previously was a guest on the PodRocket podcast to discuss Redux topics. Shortly after I started working at Replay, they had me on again along with our CEO Jason Laster. We talked about the differences between Replay and &amp;quot;session recording&amp;quot; tools, the internals of how Replay works, and potential future use cases for time-travel debugging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&#34;https://podrocket.logrocket.com/replay&#34;&gt;https://podrocket.logrocket.com/replay&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&#34;guild-community-tour-mark-erikson&#34;&gt;Guild Community Tour: Mark Erikson&lt;/h2&gt;

&lt;p&gt;Taz Singh does a lot of interviews with folks in the JS community to help share their stories. He interviewed several folks at the Reactathon conference this year, and we had a chance to sit down and chat. We made a bunch of jokes about my Simpsons avatar, and talked about my early dev career, my time teaching in China, how I got involved in Redux, what prompted some of my blog posts, and the magic of time travel debugging with Replay.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&#34;https://beta.guild.host/presentations/episode-23-mark-erikson-bczaup&#34;&gt;https://beta.guild.host/presentations/episode-23-mark-erikson-bczaup&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&#34;peerlist-the-mindset-of-a-redux-maintainer&#34;&gt;Peerlist: The Mindset of a Redux Maintainer&lt;/h2&gt;

&lt;p&gt;This was a meetup-type presentation of the &amp;quot;Modern Redux&amp;quot; talk I&#39;ve done a couple times, but there were also some good questions asked afterwards.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=MbREkTZCcpc&#34;&gt;https://www.youtube.com/watch?v=MbREkTZCcpc&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&#34;20-minute-js-redux-toolkit-and-state-management-in-react&#34;&gt;20 Minute JS: Redux Toolkit and State Management in React&lt;/h2&gt;

&lt;p&gt;Fernando Doglio had me on to discuss some of the basics around &amp;quot;state management&amp;quot;, why Redux became popular and then got a bad reputation, comparisons with other state libraries, and how we&#39;ve modernized it with Redux Toolkit&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&#34;https://podcast.20minjs.com/1952066/10665172-episode-12-redux-toolkit-and-state-management-in-react-with-mark-erikson&#34;&gt;https://podcast.20minjs.com/1952066/10665172-episode-12-redux-toolkit-and-state-management-in-react-with-mark-erikson&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&#34;react-native-radio-a-redux-maintainer-s-thoughts-on-redux-toolkit-vs-mobx-state-tree&#34;&gt;React Native Radio: A Redux Maintainer&#39;s Thoughts on Redux Toolkit vs Mobx-State-Tree&lt;/h2&gt;

&lt;p&gt;Earlier this year the React Native Radio podcast hosts did a show on &lt;a href=&#34;https://reactnativeradio.com/episodes/rnr-241-redux-toolkit-vs-mobx-state-tree-showdown&#34;&gt;Redux Toolkit vs Mobx-State-Tree&lt;/a&gt;. It was actually a very fair and balanced discussion, with a bit of a twist because host Jamon Holmgren is the maintainer of MST.&lt;/p&gt;

&lt;p&gt;After listening to that episode, I had some thoughts on some of the topics they discussed, and reached out to Jamon. They brought me on for a follow-up episode where we dove into some of the points they&#39;d brought up and I talked about &lt;em&gt;why&lt;/em&gt; Redux is designed this way, including the purpose of immutability, pain points with selectors, and other tradeoffs. Really good discussion!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&#34;https://reactnativeradio.com/episodes/rnr-249-a-redux-maintainers-thoughts-on-rtk-vs-mst&#34;&gt;https://reactnativeradio.com/episodes/rnr-249-a-redux-maintainers-thoughts-on-rtk-vs-mst&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&#34;merged-leading-tech-communities&#34;&gt;Merged: Leading Tech Communities&lt;/h2&gt;

&lt;p&gt;Iddan Aaronsohn had me on the &amp;quot;Merged&amp;quot; podcast to discuss a number of topics around my involvement in the tech community. We talked about dealing with social media, sharing knowledge, working with open source, and more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&#34;https://open.spotify.com/episode/67jTvkMB2vf7AYD5d8KSEs?si=DmNMOIuOSjaheIRI09-G_A&amp;amp;nd=1&#34;&gt;https://open.spotify.com/episode/67jTvkMB2vf7AYD5d8KSEs?si=DmNMOIuOSjaheIRI09-G_A&amp;amp;nd=1&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&#34;reactiflux-office-hours-lenz-weber-tronic-and-mark-erikson&#34;&gt;Reactiflux Office Hours: Lenz Weber-Tronic and Mark Erikson&lt;/h2&gt;

&lt;p&gt;Carl Vitullo, one of the other admins in the Reactiflux Discord, held an &amp;quot;office hours&amp;quot; interview with myself and my RTK co-maintainer Lenz Weber-Tronic. We chatted about how we got involved with Redux, how users depend on implicit behavior of libraries, complexities of maintaining TypeScript libraries, the job searches we went through this year, and ways that people can get involved with open source.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&#34;https://open.spotify.com/episode/1xKYFWjPmtYwNY3DauQ2JB?si=LOhGBIrDQ5KI3034b6xvBQ&#34;&gt;https://open.spotify.com/episode/1xKYFWjPmtYwNY3DauQ2JB?si=LOhGBIrDQ5KI3034b6xvBQ&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Blogged Answers: How I Estimate NPM Package Market Share (and how Redux usage compares to other libraries)</title>
      <link>https://blog.isquaredsoftware.com/2022/07/npm-package-market-share-estimates/</link>
      <pubDate>Wed, 06 Jul 2022 01:00:00 -0500</pubDate>
      
      <guid>https://blog.isquaredsoftware.com/2022/07/npm-package-market-share-estimates/</guid>
      <description>&lt;p&gt;&lt;/p&gt;

&lt;h2 id=&#34;introduction&#34;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;For the last couple years, I&#39;ve thrown around the statements that &amp;quot;Redux is used by 45-50% of React apps&amp;quot;, and &amp;quot;Redux is by far the most widely used state management tool for React apps&amp;quot;.&lt;/p&gt;

&lt;p&gt;I&#39;ve had a couple people ask how I&#39;m coming up with those numbers. I&#39;d written a couple prior comments about this - I tried to &lt;a href=&#34;https://www.reddit.com/r/reactjs/comments/lcgqnd/state_management/glzrax4/&#34;&gt;estimate React &amp;quot;state management market share&amp;quot; in Feb 2021&lt;/a&gt;, and &lt;a href=&#34;https://www.reddit.com/r/reactjs/comments/skbyb1/the_most_popular_react_tech_stack_in_professional/hvlwdpw/&#34;&gt;talked some about the flaws in NPM stats as a metric in Feb 2022&lt;/a&gt;, but figured it was worth putting into a blog post for posterity.&lt;/p&gt;

&lt;p&gt;I&#39;ll go through the sources I use, discuss the &lt;em&gt;many&lt;/em&gt; caveats and limitations with those sources, and then look at Redux and other state management libraries as an example.&lt;/p&gt;

&lt;h2 id=&#34;primary-source-npm-package-download-stats&#34;&gt;Primary Source: NPM Package Download Stats&lt;/h2&gt;

&lt;p&gt;The most obvious source, and the one that everyone including myself automatically turns to, is NPM&#39;s stats on how many times a package is downloaded.&lt;/p&gt;

&lt;p&gt;That info is available through several different pages and aggregators.&lt;/p&gt;

&lt;h3 id=&#34;npm-package-stats-sources&#34;&gt;NPM Package Stats Sources&lt;/h3&gt;

&lt;h4 id=&#34;npmjs-com&#34;&gt;NPMJS.com&lt;/h4&gt;

&lt;p&gt;The first is NPM&#39;s standard package description page. This shows a &amp;quot;downloads per week&amp;quot; graph in the right sidebar:&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog.isquaredsoftware.com/images/2022-07-npm-package-market-share/npm-package-page.png&#34; alt=&#34;NPM package page&#34; /&gt;&lt;/p&gt;

&lt;p&gt;Until recently, there was no way to know how many times a given package version was being downloaded. Those weekly numbers count &lt;em&gt;all&lt;/em&gt; versions of the package combined.&lt;/p&gt;

&lt;p&gt;But, within the last year, NPM &lt;em&gt;fiiiinally&lt;/em&gt; started publishing download stats per version. The limitation is that the data is only provided for the last 7 days worth of downloads.&lt;/p&gt;

&lt;p&gt;Those per-version numbers are available on the &amp;quot;Versions&amp;quot; tab for a package on the NPM site:&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog.isquaredsoftware.com/images/2022-07-npm-package-market-share/npm-version-downloads.png&#34; alt=&#34;NPM version downloads&#34; /&gt;&lt;/p&gt;

&lt;p&gt;Happily, these per-version numbers are also available as an API, with a URL of &lt;code&gt;https://api.npmjs.org/versions/$PACKAGE_NAME/last-week&lt;/code&gt;, such as &lt;a href=&#34;https://api.npmjs.org/versions/redux/last-week&#34;&gt;https://api.npmjs.org/versions/redux/last-week&lt;/a&gt; :&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt;{
  &amp;quot;package&amp;quot;: &amp;quot;redux&amp;quot;,
  &amp;quot;downloads&amp;quot;: {
    &amp;quot;4.1.0-alpha.0&amp;quot;: 48,
    &amp;quot;4.1.0&amp;quot;: 1603217,
    &amp;quot;4.1.2&amp;quot;: 1869219,
    &amp;quot;4.1.1&amp;quot;: 499937,
    &amp;quot;5.0.0-alpha.0&amp;quot;: 220,
    &amp;quot;4.2.0&amp;quot;: 2087907,
    &amp;quot;4.0.0&amp;quot;: 114988
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;(Note: It would be &lt;em&gt;really&lt;/em&gt; neat if someone would create a tool that would start automatically scraping these per-version downloads stats and save them to allow comparisons over time...)&lt;/p&gt;

&lt;h4 id=&#34;npm-download-comparison-tools&#34;&gt;NPM Download Comparison Tools&lt;/h4&gt;

&lt;p&gt;There&#39;s several sites out there that let you type in a few different package names, and it will query NPM for download stats over time and graph them.&lt;/p&gt;

&lt;p&gt;My go-to site is &lt;strong&gt;&lt;a href=&#34;https://npm-stat.com&#34;&gt;https://npm-stat.com&lt;/a&gt;&lt;/strong&gt;. It lets you enter up to 5 package names (although I wish it allowed more), and shows graphs for daily/weekly/monthly/yearly download trends. Here&#39;s an example weekly graph from a query for &lt;a href=&#34;https://npm-stat.com/charts.html?package=mobx&amp;amp;package=%40reduxjs%2Ftoolkit&amp;amp;package=react-query&amp;amp;from=2021-01-01&amp;amp;to=2022-06-30&#34;&gt;Redux Toolkit, Mobx, and React Query since 2021&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog.isquaredsoftware.com/images/2022-07-npm-package-market-share/npm-stat-weekly-graph.png&#34; alt=&#34;NPM-Stat weekly graph&#34; /&gt;&lt;/p&gt;

&lt;p&gt;Other available sites are &lt;a href=&#34;https://npmtrends.com&#34;&gt;https://npmtrends.com&lt;/a&gt; , &lt;a href=&#34;http://npm-stats.org/&#34;&gt;http://npm-stats.org/&lt;/a&gt;, and &lt;a href=&#34;https://npmcharts.com/&#34;&gt;https://npmcharts.com/&lt;/a&gt; .&lt;/p&gt;

&lt;p&gt;I also have seen &lt;a href=&#34;https://moiva.io/&#34;&gt;https://moiva.io/&lt;/a&gt; , which lets you do some similar NPM download stat trend comparisons for selected libraries, and also shows a variety of other stats: month-over-month download growth, recent releases, number of contributors, Github stars, bundle sizes, and more. A typical comparison page looks like this: &lt;a href=&#34;https://moiva.io/?npm=@reduxjs/toolkit+mobx+react-query+redux+xstate&#34;&gt;https://moiva.io/?npm=@reduxjs/toolkit+mobx+react-query+redux+xstate&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog.isquaredsoftware.com/images/2022-07-npm-package-market-share/moiva-graphs.png&#34; alt=&#34;Moiva comparisons and graphs&#34; /&gt;&lt;/p&gt;

&lt;p&gt;I haven&#39;t actually used Moiva that much thus far, but looking at it again now there&#39;s a lot of good info here and I ought to refer to it more often.&lt;/p&gt;

&lt;h2 id=&#34;alternate-stat-sources&#34;&gt;Alternate Stat Sources&lt;/h2&gt;

&lt;p&gt;There&#39;s a couple other actual stats sources I look at as well.&lt;/p&gt;

&lt;h3 id=&#34;github-dependency-lists&#34;&gt;Github Dependency Lists&lt;/h3&gt;

&lt;p&gt;Github knows how to parse all the various package manager formats used in different language ecosystems, and uses that for many different features. One of those is the &amp;quot;Dependency Graph &amp;gt; Dependents&amp;quot; list, buried under the top &amp;quot;Insights&amp;quot; tab. It shows a count of how many repos and ecosystem packages depend on the package in this repo:&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog.isquaredsoftware.com/images/2022-07-npm-package-market-share/github-dep-graph.png&#34; alt=&#34;Github dependents list&#34; /&gt;&lt;/p&gt;

&lt;p&gt;There&#39;s also Github stars. Frankly, I&#39;ve never bothered using them as a proxy for actual &lt;em&gt;usage&lt;/em&gt;, only vague popularity scales (&amp;quot;this just got posted&amp;quot;, &amp;quot;a few people have looked at it&amp;quot;, &amp;quot;this is actually being used&amp;quot;, &amp;quot;this is widely used&amp;quot;, &amp;quot;this is a major player in the ecosystem&amp;quot;, etc).&lt;/p&gt;

&lt;h3 id=&#34;online-polls-and-social-media-discussions&#34;&gt;Online Polls and Social Media Discussions&lt;/h3&gt;

&lt;p&gt;The other vaguely statistical values I refer to are the occasional &amp;quot;what tools are you using?&amp;quot; polls that pop up on Twitter or Reddit, and particularly in relation to state management libraries. These produce some actual numbers in the poll results, as well as associated discussion. A few examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://twitter.com/dabit3/status/1186980916213755904&#34;&gt;Twitter: Oct 2019&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.reddit.com/poll/jzz8si&#34;&gt;Reddit: Nov 2020&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.reddit.com/poll/sv0gnv&#34;&gt;Reddit: Feb 2022&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://twitter.com/TAbrodi/status/1495087372240928769&#34;&gt;Twitter: Feb 2022&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://twitter.com/hashnode/status/1515638734133280770&#34;&gt;Twitter: Apr 2022&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are a much less objective set of numbers, but looking at both the poll numbers and discussion can help give a sense of what&#39;s being used. On the other hand, polls are also inconsistent in which tools they even list in the first place, which makes them harder to compare to each other.&lt;/p&gt;

&lt;p&gt;Similarly, it&#39;s not actual hard stats, I do get a sense of what people may be using or finding interesting from general chatter on social media: Twitter, Reddit, HN, Reactiflux, etc.&lt;/p&gt;

&lt;h2 id=&#34;caveats-with-package-stats-and-sources&#34;&gt;Caveats with Package Stats and Sources&lt;/h2&gt;

&lt;p&gt;Settle in, because there&#39;s a lot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Every single one of these metrics and sources is &lt;em&gt;horribly&lt;/em&gt; flawed in multiple ways!&lt;/strong&gt;&lt;/p&gt;

&lt;h3 id=&#34;npm-download-stats-flaws&#34;&gt;NPM Download Stats Flaws&lt;/h3&gt;

&lt;p&gt;Let&#39;s start with the biggest source.&lt;/p&gt;

&lt;p&gt;The first issue is what&#39;s even triggering all these downloads in the first place. &lt;a href=&#34;https://twitter.com/seldo/status/1334703879431196674&#34;&gt;Per Laurie Voss, who co--founded NPM&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;They are mostly CI machines. Since package lock, updates don&#39;t really cause download spikes any more. The overwhelming majority of downloads are builds, not individual developers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This brings up a &lt;em&gt;lot&lt;/em&gt; of questions and possible influences on these numbers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A team with a bunch of CI jobs that keep redownloading the same packages is going to have an outsized influence on the absolute number of downloads&lt;/li&gt;
&lt;li&gt;There&#39;s very clearly a long tail of downloads for older versions that hang around indefinitely, so older packages seem to have a sustained base of download size. So, how many of these are for old versions vs new versions?&lt;/li&gt;
&lt;li&gt;The NPM download stats don&#39;t account for internal package hosting/caching servers like Artifactory or NexusRepository. For example, I used to work on a team that used an internal NexusRepo instance that caches packages, so none of our usage would ever get counted publicly.&lt;/li&gt;
&lt;li&gt;What about scripts hosted on a CDN? One of Vue&#39;s selling points is that it&#39;s very &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;-tag friendly&lt;/li&gt;
&lt;li&gt;For that matter, Vue is very popular in China, and there&#39;s a Chinese mirror of NPM (&lt;code&gt;cnpm&lt;/code&gt;) that I&#39;ve heard is typically used by Chinese devs. Downloads from that won&#39;t be reflected in NPM&#39;s stats.&lt;/li&gt;
&lt;li&gt;It&#39;s possible to game the numbers. I saw one package that had only been published for the first time a few days before, and yet it somehow spiked to over 1M+ downloads a day within the next couple days. Clearly impossible, and it&#39;s likely this was due to a bot of some kind faking downloads.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For that matter, NPM&#39;s own stats aren&#39;t always reliable. There have been times when NPM recorded 0 downloads for a widely used package for a few days, or other times when there&#39;s a giant spike in downloads that can&#39;t possibly be real. For example, NPM claims that downloads of XState somehow tripled for a couple months before going back to normal, and downloads of Mobx went up by 75% for two weeks:&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog.isquaredsoftware.com/images/2022-07-npm-package-market-share/npm-graph-weirdness.png&#34; alt=&#34;NPM graph weirdness&#34; /&gt;&lt;/p&gt;

&lt;p&gt;Given the consistent download levels before and after those periods, the spikes in numbers should be ignored as outliers.&lt;/p&gt;

&lt;p&gt;Again &lt;a href=&#34;https://twitter.com/seldo/status/1334707386968227841&#34;&gt;quoting Laurie Voss&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;They are meaningful relative measures -- a package with more downloads than another package is more popular -- but they have never meant anything as absolute numbers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Laurie also covered this in &lt;a href=&#34;https://youtu.be/gChULw-uEjY?t=1241&#34;&gt;a talk on the NPM registry in 2019&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As part of that talk, he described a metric called &amp;quot;share of registry&amp;quot;, which points out that the &lt;em&gt;absolute&lt;/em&gt; size of registry downloads is always growing. This means that a lib could experience an absolute increase in downloads, but its &lt;em&gt;relative&lt;/em&gt; usage share could decrease. So, by checking &amp;quot;number of downloads for this package&amp;quot; vs &amp;quot;total downloads for all the registry&amp;quot; over time, you get a more normalized value.&lt;/p&gt;

&lt;p&gt;I asked Laurie for some feedback on this post, and he reported that the NPM API offers the &amp;quot;total registry downloads&amp;quot; numbers for a given time range, such as &lt;a href=&#34;https://api.npmjs.org/downloads/range/2015-01-01:2015-12-31&#34;&gt;https://api.npmjs.org/downloads/range/2015-01-01:2015-12-31&lt;/a&gt; .&lt;/p&gt;

&lt;p&gt;Sadly, I don&#39;t know if the &amp;quot;all registry downloads&amp;quot; numbers that Laurie used to generate that graph are available publicly, and even if they are that would take some work to pull together and calculate. So, us amateurs are stuck with just looking at the absolute values and guesstimating comparisons.&lt;/p&gt;

&lt;p&gt;So, like I said, &lt;em&gt;tons&lt;/em&gt; of flaws. But at the same time, NPM&#39;s download stats &lt;em&gt;are&lt;/em&gt; basically the only hard objective numbers we as a community have to look at and infer actual usage numbers in any way. So, to some extent we end up having to look at those and make inferences, however flawed they might be.&lt;/p&gt;

&lt;h3 id=&#34;other-caveats-and-concerns&#34;&gt;Other Caveats and Concerns&lt;/h3&gt;

&lt;p&gt;I have no idea whether that Github &amp;quot;Dependents&amp;quot; list counts private repos. My guess is that it doesn&#39;t.&lt;/p&gt;

&lt;p&gt;Any kind of an online poll is statistically meaningless - there&#39;s no guarantee of who saw it, who chose to answer, etc, and there&#39;s a lot of ways you could answer. What if I use Redux for my day job, and Mobx when I work on a side project at home? What answer would I choose?&lt;/p&gt;

&lt;p&gt;What does &amp;quot;usage&amp;quot; mean anyway? :) Does 30 clones of a starter kit/boilerplate count as exactly 30 times as many &amp;quot;usages&amp;quot; as a team working on a single large project for multiple years? What about a project that pushes to CI dozens of times a day and rebuilds from scratch every time?&lt;/p&gt;

&lt;p&gt;For the case of &amp;quot;React state management&amp;quot; specifically... React has its own built-in component state handling. There&#39;s &lt;em&gt;no&lt;/em&gt; way to measure how much that&#39;s being used as the sole state management tool, beyond trying to somehow subtract &amp;quot;downloads of all state libs&amp;quot; from &amp;quot;all React downloads&amp;quot;.&lt;/p&gt;

&lt;h2 id=&#34;estimating-react-state-management-market-share&#34;&gt;Estimating React State Management Market Share&lt;/h2&gt;

&lt;p&gt;So having just listed a ton of ways that this process is flawed and hopeless, I&#39;m now going to turn around and try to use it anyway :)&lt;/p&gt;

&lt;p&gt;I&#39;m going to walk through some of the steps I go through to come up with estimates of &amp;quot;React state management library&amp;quot; market share. This is largely because it&#39;s the area I&#39;m most familiar with, but also frankly it&#39;s the area that seems to come up the most :) (although my perception here is also affected because I&#39;m active in the React community &lt;em&gt;and&lt;/em&gt; the maintainer of Redux, so of course this is a topic I would seem to see frequently.)&lt;/p&gt;

&lt;p&gt;I&#39;m going to compare Redux and RTK vs Mobx, Zustand, XState, and React Query, and also toss in a second set of comparisons with Recoil, Jotai, Valtio, and Mobx-State-Tree. (It would be interesting to add in Apollo, SWR, and Urql as additional data fetching libraries, but c&#39;mon I can only put so much time into this blog post :) )&lt;/p&gt;

&lt;p&gt;Now, I know that these are actually somewhat different libraries - React Query isn&#39;t &amp;quot;client state management&amp;quot;, but given that it has a somewhat similar usage rate to Redux Toolkit (and folks sometimes migrate from Redux to React Query to manage server state), it often gets thrown around by people as a competitor.&lt;/p&gt;

&lt;h3 id=&#34;comparing-npm-stats&#34;&gt;Comparing NPM Stats&lt;/h3&gt;

&lt;p&gt;Generally, my first step is to go to &lt;a href=&#34;https://npm-stat.com&#34;&gt;https://npm-stat.com&lt;/a&gt;, type in the name of several libraries, and view stats for the last couple years. This starts to give us a sense of scale and relative comparisons.&lt;/p&gt;

&lt;p&gt;For example, here&#39;s Redux Toolkit, Mobx, XState, React Query, and Zustand since the start of 2020:&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog.isquaredsoftware.com/images/2022-07-npm-package-market-share/state-libs-2020-2022.png&#34; alt=&#34;State management libraries - 2020-2022&#34; /&gt;&lt;/p&gt;

&lt;p&gt;But what happens when you do RTK, Redux core, React, and React-Redux?&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog.isquaredsoftware.com/images/2022-07-npm-package-market-share/react-redux-2020-2022.png&#34; alt=&#34;React and Redux - 2020-2022&#34; /&gt;&lt;/p&gt;

&lt;p&gt;So, both relative growth rates and sense of scale and magnitude are useful pieces of info here.&lt;/p&gt;

&lt;p&gt;For additional comparison, here&#39;s RTK vs several other libraries:&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://blog.isquaredsoftware.com/images/2022-07-npm-package-market-share/more-state-libs-2020-2022.png&#34; alt=&#34;More state management libraries - 2020-2022&#34; /&gt;&lt;/p&gt;

&lt;p&gt;Pulling out some of those numbers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React: 16.0M&lt;/li&gt;
&lt;li&gt;Redux: 7.7M&lt;/li&gt;
&lt;li&gt;Redux Toolkit: 1.64M&lt;/li&gt;
&lt;li&gt;React Query: 1.46M&lt;/li&gt;
&lt;li&gt;XState: 1.11M&lt;/li&gt;
&lt;li&gt;Mobx: 891K&lt;/li&gt;
&lt;li&gt;Zustand: 550K&lt;/li&gt;
&lt;li&gt;Recoil: 267K&lt;/li&gt;
&lt;li&gt;Jotai: 117K&lt;/li&gt;
&lt;li&gt;Mobx-State-Tree: 76K&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For point of reference, when &lt;a href=&#34;https://www.reddit.com/r/reactjs/comments/lcgqnd/state_management/glzrax4/&#34;&gt;I did a similar comparison in Feb 2021&lt;/a&gt;, the numbers were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React: 9.5M&lt;/li&gt;
&lt;li&gt;Redux: 5M&lt;/li&gt;
&lt;li&gt;Apollo: 1.3M (combined across @apollo/client and react-apollo)&lt;/li&gt;
&lt;li&gt;XState: 750K&lt;/li&gt;
&lt;li&gt;MobX: 600K&lt;/li&gt;
&lt;li&gt;Redux Toolkit: 450K&lt;/li&gt;
&lt;li&gt;React Query: 250K&lt;/li&gt;
&lt;li&gt;SWR: 250K&lt;/li&gt;
&lt;li&gt;Recoil: 60K&lt;/li&gt;
&lt;li&gt;Zustand: 40K&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(Yes, I realize there&#39;s some differences in which libs are in each list.)&lt;/p&gt;

&lt;p&gt;What can we learn from these graphs?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clearly the Redux core package has vastly more downloads than any of the other state libs&lt;/li&gt;
&lt;li&gt;RTK has passed Mobx and XState&lt;/li&gt;
&lt;li&gt;Remember that RTK has a hard dependency on the &lt;code&gt;redux&lt;/code&gt; core, so all RTK downloads are also Redux downloads&lt;/li&gt;
&lt;li&gt;React Query has really taken off in the last year, and is gaining downloads at a slightly faster pace than RTK&lt;/li&gt;
&lt;li&gt;React-Redux has almost as many downloads as the Redux core, which means that most Redux users are using React&lt;/li&gt;
&lt;li&gt;React-Redux has historically been in the vicinity of 45-50% of React downloads, but React&#39;s growth rate has accelerated in the last two years.&lt;/li&gt;
&lt;li&gt;Redux and React-Redux are seeing growth in absolute terms, but not as much as React.&lt;/li&gt;
&lt;li&gt;I&#39;m actually a bit surprised that the download rates for React-Redux and Redux core have started to diverge a bit - perhaps we&#39;re getting more non-React users?&lt;/li&gt;
&lt;li&gt;Zustand has seen quite a bit of growth&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I have to say some of the results here kind of surprise me. Given the hype around Recoil early on, you&#39;d think that it would have more usage. RTK seems to be actively growing, while the other libs are seeing very slow growth. (Also remember that we&#39;re at the &amp;quot;smaller state libs&amp;quot; scale, and the &amp;quot;React and Redux&amp;quot; scale dwarfs any of these.)&lt;/p&gt;

&lt;h3 id=&#34;comparing-github-dependents&#34;&gt;Comparing Github Dependents&lt;/h3&gt;

&lt;p&gt;I just now went through the repos for all these libraries, and manually looked up the stats on the &amp;quot;Dependents&amp;quot; page for each repo, and have them sorted by the &amp;quot;number of repos using this package&amp;quot; count:&lt;/p&gt;

&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;th&gt;Repos&lt;/th&gt;
&lt;th&gt;Packages&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;

&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;React&lt;/td&gt;
&lt;td&gt;10,614,469&lt;/td&gt;
&lt;td&gt;231,415&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;Redux&lt;/td&gt;
&lt;td&gt;2,279,295&lt;/td&gt;
&lt;td&gt;23,163&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;React-Redux&lt;/td&gt;
&lt;td&gt;2,117,795&lt;/td&gt;
&lt;td&gt;18,916&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;Redux Toolkit&lt;/td&gt;
&lt;td&gt;275,624&lt;/td&gt;
&lt;td&gt;1,635&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;XState&lt;/td&gt;
&lt;td&gt;133,285&lt;/td&gt;
&lt;td&gt;633&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;Mobx&lt;/td&gt;
&lt;td&gt;106,102&lt;/td&gt;
&lt;td&gt;4,585&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;React Query&lt;/td&gt;
&lt;td&gt;75,383&lt;/td&gt;
&lt;td&gt;919&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;Zustand&lt;/td&gt;
&lt;td&gt;30,465&lt;/td&gt;
&lt;td&gt;481&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;Recoil&lt;/td&gt;
&lt;td&gt;26,159&lt;/td&gt;
&lt;td&gt;369&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;Mobx-State-Tree&lt;/td&gt;
&lt;td&gt;6,829&lt;/td&gt;
&lt;td&gt;360&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;Jotai&lt;/td&gt;
&lt;td&gt;4,065&lt;/td&gt;
&lt;td&gt;170&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;Valtio&lt;/td&gt;
&lt;td&gt;1,612&lt;/td&gt;
&lt;td&gt;106&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Some thoughts here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It&#39;s amazing to see the scale of React itself. It just dwarfs everything else&lt;/li&gt;
&lt;li&gt;There&#39;s a very interesting difference in ratios between the NPM download stats and the Github repo dependents stats. Redux is just about 50% of React in terms of downloads, but just barely over 20% in terms of repos.&lt;/li&gt;
&lt;li&gt;Redux and React-Redux are much closer in terms of repos than downloads, although perhaps Github doesn&#39;t count RTK-using repos as depending on &lt;code&gt;redux&lt;/code&gt;, in which case their relative ratio widens a bit&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;poll-results&#34;&gt;Poll Results&lt;/h3&gt;

&lt;p&gt;Similarly, I&#39;m going to go through some of those polls I linked earlier and pull out the numbers for libraries over time:&lt;/p&gt;

&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;th&gt;Twitter 2019-10&lt;/th&gt;
&lt;th&gt;Reddit 2020-11&lt;/th&gt;
&lt;th&gt;Reddit 2022-02&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;

&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Redux&lt;/td&gt;
&lt;td&gt;48%&lt;/td&gt;
&lt;td&gt;42%&lt;/td&gt;
&lt;td&gt;67%&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;Mobx&lt;/td&gt;
&lt;td&gt;9%&lt;/td&gt;
&lt;td&gt;4.5%&lt;/td&gt;
&lt;td&gt;5.8%&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;Context&lt;/td&gt;
&lt;td&gt;32%&lt;/td&gt;
&lt;td&gt;25%&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;React state&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;22%&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;Recoil&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;4%&lt;/td&gt;
&lt;td&gt;6.3%&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;Unstated&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;2%&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;Zustand&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;12.5%&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;Jotai&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;4.4%&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;XState&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;3.4%&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;&amp;quot;Other&amp;quot;&lt;/td&gt;
&lt;td&gt;11%&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Clearly there&#39;s a lot of inconsistency here in terms of which libs are even being voted on, much less which ones are being used, plus there&#39;s a split between &amp;quot;using React state&amp;quot; and &amp;quot;using a library&amp;quot;. So, we definitely can&#39;t use these for real meaningful statistical comparisons, but you can still get a &lt;em&gt;vague&lt;/em&gt; sense of usage here. The main takeaway I would get out of this is that, yes, Redux is still pretty strong here, and there&#39;s a lot of split beyond that.&lt;/p&gt;

&lt;h2 id=&#34;conclusions&#34;&gt;Conclusions&lt;/h2&gt;

&lt;p&gt;Looking at these numbers now, with fresh eyes, here&#39;s my takeaways:&lt;/p&gt;

&lt;h3 id=&#34;react-s-growth-continues&#34;&gt;React&#39;s Growth Continues&lt;/h3&gt;

&lt;p&gt;I didn&#39;t do comparisons vs Vue, Angular, Ember, and Svelte here, but you can see by just looking at &amp;quot;React vs any other React ecosystem lib&amp;quot; that &lt;strong&gt;React is getting downloaded and used &lt;em&gt;lot&lt;/em&gt;, and shows no signs of stopping&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Ironically, I &lt;em&gt;did&lt;/em&gt; just pull up some links to framework-level usage comparisons based on similar statistics to answer a comment earlier, and they definitely do show React&#39;s lead. I&#39;ll link those here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://gist.github.com/tkrotoff/b1caa4c3a185629299ec234d2314e190&#34;&gt;https://gist.github.com/tkrotoff/b1caa4c3a185629299ec234d2314e190&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://w3techs.com/technologies/comparison/js-angularjs,js-react,js-vuejs&#34;&gt;https://w3techs.com/technologies/comparison/js-angularjs,js-react,js-vuejs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://npmtrends.com/@angular/core-vs-react-vs-vue&#34;&gt;https://npmtrends.com/@angular/core-vs-react-vs-vue&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;redux-is-still-number-one-in-state-management&#34;&gt;Redux is Still Number One In State Management&lt;/h3&gt;

&lt;p&gt;Clearly, &lt;strong&gt;Redux is still by far the most widely used state management library with React apps&lt;/strong&gt;. No other library comes close in terms of NPM downloads and dependent repos, and even Redux Toolkit by itself is beating the other libraries handily. Additionally, the anecdata of the polls and discussions shows a lot of people saying &amp;quot;use RTK&amp;quot;, which is a noticeable change from 2018-2019 when a lot of folks were chattering about &amp;quot;Redux boilerplate&amp;quot; and &amp;quot;Redux is dead&amp;quot;.&lt;/p&gt;

&lt;h3 id=&#34;redux-s-total-market-share-has-dropped-vs-react&#34;&gt;Redux&#39;s Total Market Share has Dropped vs React&lt;/h3&gt;

&lt;p&gt;I said at the start of this post that I&#39;ve been repeating &amp;quot;Redux is 45-50% of React apps&amp;quot; for a while now.&lt;/p&gt;

&lt;p&gt;Honestly, after seeing these numbers, I think I need to revise that at least partially.&lt;/p&gt;

&lt;p&gt;If we look at just React, Redux, React-Redux, and RTK:&lt;/p&gt;

&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;th&gt;DLs/wk&lt;/th&gt;
&lt;th&gt;Dependents&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;

&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;React&lt;/td&gt;
&lt;td&gt;15.97M&lt;/td&gt;
&lt;td&gt;10.61M&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;Redux&lt;/td&gt;
&lt;td&gt;7.45M&lt;/td&gt;
&lt;td&gt;2.28M&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;React-Redux&lt;/td&gt;
&lt;td&gt;5.62M&lt;/td&gt;
&lt;td&gt;2.11M&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;Redux Toolkit&lt;/td&gt;
&lt;td&gt;1.57M&lt;/td&gt;
&lt;td&gt;0.27M&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Based on NPM downloads, Redux itself is still around 45% of React, but React-Redux is only 35%.&lt;/p&gt;

&lt;p&gt;Where it gets really different is in terms of dependent repos. There, Redux is only 20% of React.&lt;/p&gt;

&lt;p&gt;To be honest, I&#39;m really not sure what to make of the split between numbers here. If we go back to Laurie Voss&#39;s point that &amp;quot;NPM downloads are primarily driven by CI&amp;quot;, I suppose some interpretations here might be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There&#39;s a ton of repos that depend on React, but not Redux. It&#39;s &lt;em&gt;possible&lt;/em&gt; that a noticeable percentage of those are &amp;quot;starting to learn React&amp;quot; repos from beginners, and thus might be less likely to depend on Redux&lt;/li&gt;
&lt;li&gt;It&#39;s also very likely that a larger number of new apps today are indeed less likely to add Redux, because they don&#39;t feel it&#39;s necessary&lt;/li&gt;
&lt;li&gt;It&#39;s possible that larger professional apps &lt;em&gt;may&lt;/em&gt; be relatively more likely to be using Redux, and thus installing it when building in CI&lt;/li&gt;
&lt;li&gt;Libraries that depend on React may also contribute to the number of React downloads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To be clear, &lt;strong&gt;I don&#39;t know if any of these guesses are correct!&lt;/strong&gt; I&#39;m just brainstorming right now as I look at these numbers, trying to come up with possible explanations.&lt;/p&gt;

&lt;p&gt;If I were to revise my previous &amp;quot;45-50% of React&amp;quot; estimate... the obvious potential bounds here are 20-35%, depending on how you balance &amp;quot;downloads&amp;quot; vs &amp;quot;dependent repos&amp;quot;.&lt;/p&gt;

&lt;p&gt;On the other hand, as I asked earlier: how should we weigh &amp;quot;usage&amp;quot; between &amp;quot;thousands of starter app clones&amp;quot; and &amp;quot;real apps&amp;quot;? Raw numbers? Does 1 real app === 10/100/1000 starter clones? How would we even figure that out how many repos fit in those categories without completely scraping Github? :)&lt;/p&gt;

&lt;p&gt;I&#39;m going to try to be intellectually honest with myself here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I&#39;m going to revise my prior &amp;quot;45-50% of React apps use Redux&amp;quot; estimate, and lower it to &amp;quot;roughly 33% of React apps use Redux&amp;quot;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I think that&#39;s a fair-ish estimate based on the combination of downloads + dependents, tweaked by the anecdata.&lt;/p&gt;

&lt;h3 id=&#34;wishlist&#34;&gt;Wishlist&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;It would be &lt;em&gt;really&lt;/em&gt; nice if someone could put together a new NPM package comparison site that automates the &amp;quot;share of registry&amp;quot; metric for a given set of packages&lt;/strong&gt;. Given that you can query for &amp;quot;all registry downloads&amp;quot; and &amp;quot;per-package downloads&amp;quot; for a given time range, this seems pretty feasible.&lt;/p&gt;

&lt;p&gt;It would be nice if there was also a way to query Github&#39;s API for the &amp;quot;Dependents&amp;quot; info, but it doesn&#39;t look like that&#39;s available. I did find &lt;a href=&#34;https://stackoverflow.com/questions/58734176/how-to-use-github-api-to-get-a-repositorys-dependents-information-in-github&#34;&gt;a Stack Overflow answer showing how to scrape that info from repo pages&lt;/a&gt;, so I guess that could be a fallback option.&lt;/p&gt;

&lt;p&gt;Moiva.io seems like it does a good portion of that already, and there &lt;em&gt;is&lt;/em&gt; &lt;a href=&#34;https://github.com/aantipov/moiva/issues/521&#34;&gt;an open issue to add dependents as a metric&lt;/a&gt;. I also just filed an issue asking for &lt;a href=&#34;https://github.com/aantipov/moiva/issues/559&#34;&gt;adding &amp;quot;share of registry&amp;quot; as a comparison metric&lt;/a&gt; as well.&lt;/p&gt;

&lt;h2 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h2&gt;

&lt;p&gt;Hopefully the info I&#39;ve provided here is informative and useful for folks trying to make comparisons between different packages.&lt;/p&gt;

&lt;p&gt;And yes, I &lt;em&gt;did&lt;/em&gt; intentionally write this post to show some stats behind my repeated statements that &amp;quot;Redux is still most widely used&amp;quot; and &amp;quot;45-50% of React apps&amp;quot;. Looking at these numbers, they definitely back up the &amp;quot;most widely used&amp;quot;, but I did have to revise the &amp;quot;45-50%&amp;quot; down to about &amp;quot;33%&amp;quot; - I hadn&#39;t realized just how much the gap in downloads between React and React-Redux has grown in the last year.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Presentations: Modern Redux with Redux Toolkit</title>
      <link>https://blog.isquaredsoftware.com/2022/06/presentations-modern-redux-rtk/</link>
      <pubDate>Mon, 27 Jun 2022 20:00:00 -0500</pubDate>
      
      <guid>https://blog.isquaredsoftware.com/2022/06/presentations-modern-redux-rtk/</guid>
      <description>&lt;p&gt;&lt;/p&gt;

&lt;p&gt;I&#39;ve frequently talked about why we created Redux Toolkit and what APIs it includes as part of my &amp;quot;State of Redux&amp;quot; talks and other similar presentations. I recently had the chance to talk at a couple of online meetups, and put together an updated and consolidated version of that material as a complete &amp;quot;Modern Redux with Redux Toolkit&amp;quot; presentation:&lt;/p&gt;

&lt;h3 id=&#34;modern-redux-with-redux-toolkit-video-https-www-youtube-com-watch-v-14xgphtow1y&#34;&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=14XGPHtoW1Y&#34;&gt;Modern Redux with Redux Toolkit - video&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id=&#34;modern-redux-with-redux-toolkit-slides-presentations-2022-06-modern-redux-rtk&#34;&gt;&lt;a href=&#34;https://blog.isquaredsoftware.com/presentations/2022-06-modern-redux-rtk/&#34;&gt;Modern Redux with Redux Toolkit - slides&lt;/a&gt;&lt;/h3&gt;</description>
    </item>
    
    <item>
      <title>Reactathon 2022: The Evolution of Redux Async Logic</title>
      <link>https://blog.isquaredsoftware.com/2022/05/presentations-evolution-redux-async-logic/</link>
      <pubDate>Wed, 04 May 2022 12:00:00 -0500</pubDate>
      
      <guid>https://blog.isquaredsoftware.com/2022/05/presentations-evolution-redux-async-logic/</guid>
      <description>&lt;p&gt;&lt;/p&gt;

&lt;p&gt;I&#39;ve spoken at Reactathon in past years, and this year I had the chance to give a pre-recorded talk. I also ended up giving the same talk live as well after filling in as a backup speaker.&lt;/p&gt;

&lt;p&gt;My talk looked at why we use middleware for side effects in Redux, the major libraries (thunks, sagas, observables), why we&#39;ve recommended thunks, introduces RTK Query and the &amp;quot;listener&amp;quot; middleware, and gives our current recommendations for what tools to use in different scenarios today.&lt;/p&gt;

&lt;h3 id=&#34;the-evolution-of-redux-async-logic-slides-presentations-2022-05-evolution-redux-async-logic&#34;&gt;&lt;a href=&#34;https://blog.isquaredsoftware.com/presentations/2022-05-evolution-redux-async-logic/&#34;&gt;The Evolution of Redux Async Logic - slides&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;The video should be online in the next few weeks, but for now you can see me give the talk as part of the Day 3 livestream:&lt;/p&gt;

&lt;h3 id=&#34;the-evolution-of-redux-async-logic-livestream-video-https-youtu-be-0qk2-wi4t3k-t-11266&#34;&gt;&lt;a href=&#34;https://youtu.be/0qK2_wi4t3k?t=11266&#34;&gt;The Evolution of Redux Async Logic - livestream video&lt;/a&gt;&lt;/h3&gt;</description>
    </item>
    
    <item>
      <title>Idiomatic Redux: Designing the Redux Toolkit Listener Middleware</title>
      <link>https://blog.isquaredsoftware.com/2022/03/designing-rtk-listener-middleware/</link>
      <pubDate>Fri, 18 Mar 2022 10:00:00 -0500</pubDate>
      
      <guid>https://blog.isquaredsoftware.com/2022/03/designing-rtk-listener-middleware/</guid>
      <description>&lt;p&gt;&lt;/p&gt;

&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;

&lt;p&gt;In &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/releases/tag/v1.8.0&#34;&gt;Redux Toolkit 1.8&lt;/a&gt;, we released &lt;a href=&#34;https://redux-toolkit.js.org/api/createListenerMiddleware&#34;&gt;a new &amp;quot;listener&amp;quot; side effects middleware&lt;/a&gt; that is intended to be a lightweight alternative to more widely used Redux async middleware like sagas and observables.&lt;/p&gt;

&lt;p&gt;The final API and usage of the middleware looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt;import { configureStore, createListenerMiddleware } from &#39;@reduxjs/toolkit&#39;;

import todosReducer, {
  todoAdded,
  todoToggled,
  todoDeleted,
} from &#39;../features/todos/todosSlice&#39;;

// Create the middleware instance and methods
const listenerMiddleware = createListenerMiddleware();

// Add one or more listener entries that look for specific actions.
// They may contain any sync or async logic, similar to thunks.
listenerMiddleware.startListening({
  actionCreator: todoAdded,
  effect: async (action, listenerApi) =&amp;gt; {
    // Run whatever additional side-effect-y logic you want here
    console.log(&#39;Todo added: &#39;, action.payload.text);

    // Can cancel other running instances
    listenerApi.cancelActiveListeners();

    // Run async logic
    const data = await fetchData();

    // Pause until action dispatched or state changed
    if (await listenerApi.condition(matchSomeAction)) {
      // Use the listener API methods to dispatch, get state,
      // unsubscribe the listener, start child tasks, and more
      listenerApi.dispatch(todoAdded(&#39;Buy pet food&#39;));

      // Spawn &amp;quot;child tasks&amp;quot; that can do more work and return results
      const task = listenerApi.fork(async (forkApi) =&amp;gt; {
        // Can pause execution
        await forkApi.delay(5);
        // Complete the child by returning a value
        return 42;
      });

      const result = await task.result;
      // Unwrap the child result in the listener
      if (result.status === &#39;ok&#39;) {
        // Logs the `42` result value that was returned
        console.log(&#39;Child succeeded: &#39;, result.value);
      }
    }
  },
});

const store = configureStore({
  reducer: {
    todos: todosReducer,
  },
  // Add the listener middleware to the store.
  // NOTE: Since this can receive actions with functions inside,
  // it should go before the serializability check middleware
  middleware: (getDefaultMiddleware) =&amp;gt;
    getDefaultMiddleware().prepend(listenerMiddleware.middleware),
});
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;But how did we end up with that API design?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Trust me, it didn&#39;t appear out of thin air :)&lt;/p&gt;

&lt;p&gt;The development of this new API was a long and winding process that dates back 2.5 years, and involved significant amounts of iteration to determine what use cases it should cover, what the public API should look like, and how to implement the functionality.&lt;/p&gt;

&lt;p&gt;I&#39;d like to recap that process, as I think it&#39;s a good example of how to work through designing library APIs and there may be useful lessons for other developers.&lt;/p&gt;

&lt;h2 id=&#34;why-create-a-new-middleware&#34;&gt;Why Create a New Middleware?&lt;/h2&gt;

&lt;h3 id=&#34;background-redux-side-effects-approaches&#34;&gt;Background: Redux Side Effects Approaches&lt;/h3&gt;

&lt;p&gt;Redux was originally designed to &lt;a href=&#34;https://blog.isquaredsoftware.com/presentations/2017-09-might-need-redux-ecosystem/#/9&#34;&gt;use middleware for customizing side effects behavior with your choice of syntax&lt;/a&gt;. Dan and Andrew specifically didn&#39;t want to lock users into having to use a single built-in API for async logic, or require users to learn a complex library like RxJS.&lt;/p&gt;

&lt;p&gt;The Redux community jumped on this idea, and began cranking out dozens of addon libraries for managing side effects (to the point that less than a year after Redux&#39;s release, there was &lt;a href=&#34;https://medium.com/javascript-and-opinions/redux-side-effects-and-you-66f2e0842fc3&#34;&gt;a post complaining there were too many side effects libraries&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;But, by late 2017, &lt;a href=&#34;https://blog.isquaredsoftware.com/presentations/2017-09-might-need-redux-ecosystem/#/37&#34;&gt;the ecosystem had mostly coalesced to a few major options&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://redux.js.org/usage/writing-logic-thunks&#34;&gt;Thunks&lt;/a&gt;: dispatch a function, get &lt;code&gt;(dispatch, getState)&lt;/code&gt; as arguments, run whatever logic you want in that function&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://redux-saga.js.org/&#34;&gt;Sagas&lt;/a&gt;: write generator functions that respond to dispatched actions and return descriptions of side effects (&amp;quot;call this function with these args&amp;quot;), and let the saga middleware do the actual work&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://redux-observable.js.org/&#34;&gt;Observables&lt;/a&gt;: write RxJS observable pipelines that respond to dispatched actions and execute side effects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In practice, thunks have always been considered the default. Sagas have been fairly popular. Observables have been somewhat less common - they solve the same use case as sagas, but are mostly used by people who prefer RxJS as an API for declarative behavior rather than the more imperative generator function syntax of sagas.&lt;/p&gt;

&lt;h3 id=&#34;redux-toolkit-and-thunks&#34;&gt;Redux Toolkit and Thunks&lt;/h3&gt;

&lt;p&gt;When we designed Redux Toolkit, we specifically wanted to have a good store setup out of the box. We decided early on to &lt;a href=&#34;https://redux.js.org/tutorials/fundamentals/part-8-modern-redux#using-configurestore&#34;&gt;automatically set up the thunk middleware by default&lt;/a&gt;, because thunks have always been the most common approach for writing async logic. Additionally, &lt;a href=&#34;https://redux.js.org/style-guide/style-guide#use-thunks-for-async-logic&#34;&gt;we&#39;ve specifically recommended thunks as the default side effects approach&lt;/a&gt; for years, and RTK includes &lt;a href=&#34;https://redux-toolkit.js.org/api/createAsyncThunk&#34;&gt;a &lt;code&gt;createAsyncThunk&lt;/code&gt; API&lt;/a&gt; that abstracts the standard pattern for dispatching actions based on a promise&#39;s lifecycle.&lt;/p&gt;

&lt;p&gt;RTK&#39;s &lt;code&gt;configureStore&lt;/code&gt; API allows full customization of the middleware, including replacing the defaults entirely. So, you&#39;ve always been able to add sagas, observables, or custom middleware to the store, or turn off thunks entirely.&lt;/p&gt;

&lt;h3 id=&#34;concerns-with-sagas-and-observables&#34;&gt;Concerns with Sagas and Observables&lt;/h3&gt;

&lt;p&gt;Over the years, we&#39;ve &lt;a href=&#34;https://redux.js.org/style-guide/style-guide&#34;&gt;become more opinionated in our advice about how to use Redux&lt;/a&gt;. As part of that, we&#39;ve progressed from &amp;quot;use whatever side effects library you want&amp;quot;, to actively &lt;em&gt;discouraging&lt;/em&gt; people from using sagas unless absolutely necessary.&lt;/p&gt;

&lt;p&gt;We&#39;ve been asked if we would ever add direct support for sagas to RTK itself, either by including them as a default or having some saga-specific API included. I answered this in my post &lt;a href=&#34;https://blog.isquaredsoftware.com/2020/02/blogged-answers-why-redux-toolkit-uses-thunks-for-async-logic/&#34;&gt;Why Redux Toolkit Uses Thunks For Async Logic&lt;/a&gt;, which points out that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sagas require understanding both generator functions and the specific &lt;code&gt;redux-saga&lt;/code&gt; &amp;quot;effects&amp;quot; APIs&lt;/li&gt;
&lt;li&gt;Some saga patterns like &amp;quot;watcher&amp;quot; vs &amp;quot;worker&amp;quot; sagas lead to extra boilerplate as well as making logic hard to follow&lt;/li&gt;
&lt;li&gt;Sagas don&#39;t work well with TypeScript&lt;/li&gt;
&lt;li&gt;Sagas are good for very complex async workflows, but overkill for basic data fetching scenarios&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The same concerns apply to observables as well, just with &amp;quot;RxJS operators&amp;quot; instead of &amp;quot;generator functions and saga effects&amp;quot;.&lt;/p&gt;

&lt;p&gt;These factors all played a role in the eventual development of the listener middleware.&lt;/p&gt;

&lt;h2 id=&#34;original-inspiration&#34;&gt;Original Inspiration&lt;/h2&gt;

&lt;p&gt;In October 2019, I filed an issue for &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/issues/237&#34;&gt;&amp;quot;Add an action listener callback middleware&amp;quot;&lt;/a&gt;. In that issue, I wrote:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Thunks are easy to use and a good default, but the biggest weakness is that they don&#39;t let you respond to dispatched actions. Sagas and observables are very powerful (too powerful for most apps), but they do let you kick off additional logic in response to actions.&lt;/p&gt;

&lt;p&gt;I&#39;ve been considering adding some kind of middleware that would be in between - something that lets you run callback functions in response to specific actions, but without the complex overhead of sagas and observables.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In that issue, I specifically linked &lt;a href=&#34;https://gist.github.com/modernserf/e890a43887b1f7a80e27300481bffb4b&#34;&gt;a &amp;quot;handler&amp;quot; middleware&lt;/a&gt; written by Justin Falcone during his time at Glitch. Justin commented with some details on why it was being used:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There are essentially two schools of thought for working with side effects with redux.&lt;/p&gt;

&lt;p&gt;The first approach is to put the side effects in the actions -- e.g. instead of dispatching a simple value, dispatch a promise, or a function that calls side effects. Middleware like redux-promise and redux-thunk intercept these non-value actions and run them.&lt;/p&gt;

&lt;p&gt;The second approach is to put the side effects directly into the middleware -- the actions are always simple values, but the middleware listens to these actions, and can run side effects in response to them. This is the approach used by redux-saga and redux-loop; it&#39;s also how logging and metrics-reporting middleware work.&lt;/p&gt;

&lt;p&gt;I prefer the second approach because it encourages you to keep the &amp;quot;how&amp;quot; with the state, and the &amp;quot;what&amp;quot; with the UI. However, the tools associated with this approach, particularly redux-saga, tend to have a pretty steep learning curve. The approach I&#39;m using here is less expressive, but much closer to the complexity of redux-thunk.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I replied with:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Yep, that&#39;s the idea I&#39;m going for here. One possible improvement I&#39;d like to consider is adding and removing subscriptions at runtime. The likely approach would be &lt;code&gt;dispatch(addActionListener(type, callback))&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This formed the basis for the initial design of the middleware: &lt;strong&gt;the ability to run callbacks in response to dispatched actions, with a simpler API than sagas or observables, and to add and remove &amp;quot;listener&amp;quot; callbacks at runtime via dispatching&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&#34;initial-design-and-implementation-ideas&#34;&gt;Initial Design and Implementation Ideas&lt;/h2&gt;

&lt;p&gt;The same thread then went off into some early design discussions. I tried to lay out some initial constraints and API usage ideas:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;There should be multiple possible handlers for a single action type. The Glitch middleware linked in the original post does that.&lt;/li&gt;
&lt;li&gt;It should be possible to add more handlers at runtime by dispatching a &lt;code&gt;&amp;quot;listeners/addListener&amp;quot;&lt;/code&gt; action that contains a callback function as the payload, and is explicitly stopped by the middleware from proceeding to the reducers. There should also be some way to remove a listener using the same approach, whether it be based on a returned ID or a function reference equality check.&lt;/li&gt;
&lt;li&gt;Other than the &amp;quot;add/remove listener&amp;quot; actions, the middleware should probably pass the action through to the reducers first before executing the listeners, same as how sagas work. (I could hypothetically imagine including a way for the callbacks to say &amp;quot;stop this action from proceeding&amp;quot;, but it&#39;d be a lot simpler if we don&#39;t worry about that.)&lt;/li&gt;
&lt;li&gt;We&#39;re definitely going to pass the &amp;quot;store API&amp;quot; &lt;code&gt;{dispatch, getState}&lt;/code&gt; object into the callbacks. Perhaps we should also pass in &lt;code&gt;{addListener, removeListener}&lt;/code&gt; as part of that param as well.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;At the same time, Lenz Weber tried to put together &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/pull/272&#34;&gt;an initial proof of concept PR&lt;/a&gt; just to get something going. Lenz&#39;s POC relied on providing a lookup table of action types as another field inside of &lt;code&gt;createSlice&lt;/code&gt;, and generated a separate listener middleware instance per slice.&lt;/p&gt;

&lt;p&gt;As the discussion continued over the next few days, we debated whether it was better to define listeners inside or outside of slices, whether dynamically adding listeners at runtime via dispatching was a good idea, and the possibility of users tying listeners to components. In the process, I also tossed out an initial suggestion for a &amp;quot;predicate API&amp;quot; that would look like &lt;code&gt;(action, currentState, prevState) =&amp;gt; boolean&lt;/code&gt;, as a way to check for state changes in addition to specific actions. I also pointed out that a &lt;code&gt;dispatch&lt;/code&gt;-based approach for dynamically adding listeners made it the most flexible and easily accessible. If we tried adding a new method to the store itself, the UI wouldn&#39;t have access to it, whereas &lt;code&gt;dispatch&lt;/code&gt; is universally available. Similarly, &lt;code&gt;dispatch&lt;/code&gt; is UI-agnostic and thus would work with any view layer.&lt;/p&gt;

&lt;h2 id=&#34;second-pr-and-use-case-bikeshedding&#34;&gt;Second PR and Use Case Bikeshedding&lt;/h2&gt;

&lt;h3 id=&#34;initial-pr-contents&#34;&gt;Initial PR Contents&lt;/h3&gt;

&lt;p&gt;The original discussion thread continued intermittently for several months. In May 2020, Lenz put together &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/pull/547&#34;&gt;&lt;strong&gt;a second implementation attempt in PR #547&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This attempt started with a few interesting ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding listeners either via &lt;code&gt;middleware.addListener&lt;/code&gt; or &lt;code&gt;dispatch(addListener())&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Matching actions either via type string or by providing an RTK action creator&lt;/li&gt;
&lt;li&gt;One-shot listeners by providing a &lt;code&gt;once&lt;/code&gt; option&lt;/li&gt;
&lt;li&gt;The ability to actually stop an action from being processed by any other listeners or the reducers, by providing a &lt;code&gt;preventPropagation&lt;/code&gt; option&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;condition&lt;/code&gt; callback that could be used to further determine if a listener should run or not&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Having a concrete code implementation to look at was great... and it promptly sparked a major wave of bikeshedding :)&lt;/p&gt;

&lt;p&gt;One quirk of the initial PR was that the &amp;quot;add listener&amp;quot; action object was actually being passed onwards to the reducers. Since the action originally contained the listener callback function, and those aren&#39;t serializable, the middleware was deleting the callback before forwarding the action onwards. The idea was that seeing the &amp;quot;some listener got added&amp;quot; action in the DevTools might be useful for visibility purposes.&lt;/p&gt;

&lt;h3 id=&#34;pr-iteration-and-brainstorming&#34;&gt;PR Iteration and Brainstorming&lt;/h3&gt;

&lt;p&gt;We debated the value of this for a few days, and at one point Lenz started thinking about dispatching some kind of a &amp;quot;started listening&amp;quot; action. However, &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/pull/547#discussion_r422663364&#34;&gt;he quickly realized this led to too much complexity&lt;/a&gt;, and we decided to just drop forwarding the &amp;quot;add&amp;quot; action entirely.&lt;/p&gt;

&lt;p&gt;At the same time, a user tried out the PR prototype build, and interestingly jumped straight to trying to add a listener by dispatching from within a component. They ran into a types issue where TS thought that the value returned from &lt;code&gt;dispatch&lt;/code&gt; was the plain &amp;quot;add&amp;quot; action object, rather than the &lt;code&gt;unsubscribe&lt;/code&gt; callback that should be getting returned. I realized that &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/pull/547#issuecomment-626269557&#34;&gt;this was actually a repeat of a types problem Lenz and I had run into a year earlier&lt;/a&gt; - trying to get TS to acknowledge that a middleware may alter the return value of &lt;code&gt;dispatch&lt;/code&gt; when a specific action is dispatched. (Lenz noted that TS &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/pull/547#issuecomment-626395178&#34;&gt;specifically had issues when other fields were attached to the middleware&lt;/a&gt;, and said he had &amp;quot;fixed it by brute force&amp;quot;. Spoiler: this would come back to bite us later!)&lt;/p&gt;

&lt;p&gt;The same user noted that the initial implementation actually ran the listeners &lt;em&gt;before&lt;/em&gt; the reducers processed the action, and that meant they couldn&#39;t ever see the updated state. Lenz suggested &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/pull/547#discussion_r422673466&#34;&gt;adding a &lt;code&gt;when: &#39;before&#39; | &#39;after&#39;&lt;/code&gt; option&lt;/a&gt;, and added that to the PR. However, I pointed out that &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/pull/547#discussion_r422712739&#34;&gt;both the saga and observable middleware handle effects &lt;em&gt;after&lt;/em&gt; the reducers run&lt;/a&gt;, and so that should probably be the default.&lt;/p&gt;

&lt;p&gt;There was a flurry of discussion activity over the next few days, which led to several PR updates: dropping the &lt;code&gt;once&lt;/code&gt; and &lt;code&gt;condition&lt;/code&gt; options, defaulting &lt;code&gt;when&lt;/code&gt; to &lt;code&gt;&#39;after&#39;&lt;/code&gt;, adding &lt;code&gt;listenerApi.unsubscribe&lt;/code&gt;, and a consolidation of internal handling for the &amp;quot;before/after&amp;quot; logic. We also did &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/pull/547#issuecomment-627392827&#34;&gt;some review and comparison of how other Redux-based libraries and similar custom middleware&lt;/a&gt;, to see what their APIs looked like and what capabilities were included.&lt;/p&gt;

&lt;p&gt;In the process, I started having some ideas about what methods should be exposed as part of the &amp;quot;listener API&amp;quot;, like adding a &lt;code&gt;getPreviousState&lt;/code&gt; method:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;By passing in &lt;code&gt;getState&lt;/code&gt; to the listener, we enable them to read the &amp;quot;current&amp;quot; state at any point in the future, ie, in the middle of some &lt;code&gt;async/await stuff&lt;/code&gt;. So yeah, we definitely want &lt;code&gt;getState&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What doesn&#39;t give you is the ability to know what the state was before the listener even got triggered at all, which is why I was thinking about &lt;code&gt;getPreviousState&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As a side note: these listeners are definitely way weaker than sagas and observables, in that I don&#39;t see how you&#39;d listen for multiple distinct actions in sequence. I&#39;m okay with that, as this is deliberately intended to be a much more scoped API anyway.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;(Narrator: This Changed)&lt;/p&gt;

&lt;p&gt;At the same time, &lt;code&gt;@mpeyper&lt;/code&gt; suggested adding &lt;code&gt;subscribe/unsubscribe&lt;/code&gt; methods, and we both came up with the idea of adding an &lt;code&gt;extraArgument&lt;/code&gt; similar to the thunk middleware, with the typical use case of injecting a service layer into the middleware. Lenz suggested adding a type guard to enable better typed matching against certain actions, and another user mentioned the idea of waiting for a condition to evaluate to true based on an &lt;code&gt;(action, state)&lt;/code&gt; combination. Although we didn&#39;t know it at the time, these would all end up becoming parts of the final listener middleware API, albeit after some of these were forgotten and independently reinvented later :)&lt;/p&gt;

&lt;h3 id=&#34;stalemate&#34;&gt;Stalemate&lt;/h3&gt;

&lt;p&gt;By late 2020, the conversation on the PR had died off due to a lack of clarity about what the runtime semantics of the middleware should be. The Redux team moved on to working on other new features, including RTK Query. PR #547 was still open, but fell off into the background. Lenz did suggest possibly publishing it as a standalone package under our &lt;code&gt;@rtk-incubator&lt;/code&gt; package scope, just to give people a chance to try it out separately.&lt;/p&gt;

&lt;p&gt;A couple other users did comment expressing interest. One comment in Dec 2020 said &amp;quot;we&#39;re using sagas, but looking for alternatives because those are too complicated&amp;quot;. In June 2021, another user dropped by Reactiflux asking some questions, and I pointed them to the PR as a possible solution. They tried it out and left some suggestions on both internal implementation and APIs for adding listeners.&lt;/p&gt;

&lt;p&gt;A few days later, a user named Faber Vitale commented and offered suggestions on adding error handling, pointing out that currently any thrown error would kill the entire processing chain. Faber had actually just &lt;a href=&#34;https://github.com/reduxjs/redux-devtools/pull/750&#34;&gt;written the new RTK Query monitor for the Redux DevTools&lt;/a&gt;. Faber would end up adding huge contributions to the final version of the middleware.&lt;/p&gt;

&lt;p&gt;In late September, another user listed some reasons for wanting to drop sagas:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Poor TypeScript support and will never get official TypeScript support according to maintainers.&lt;/li&gt;
&lt;li&gt;Generator syntax isn&#39;t as straightforward as normal JavaScript&lt;/li&gt;
&lt;li&gt;Thunk actions (action creators that return promises) are sweet and Saga doesn&#39;t support&lt;/li&gt;
&lt;li&gt;No longer actively maintained&lt;/li&gt;
&lt;li&gt;Redux maintainers generally advise against using it whenever possible unless dealing with really complex asynchronous flows where handling timing overlap of actions is needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In general, having some sort of takeLatest/takeLeading esque option on a per function basis would be a game changer. If that were the case there would be practically no reason to use Saga for almost any use case (except maybe a complex auth flow, for example).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It was clear that there was &lt;em&gt;some&lt;/em&gt; interest in this middleware - it just needed some actual attention and effort to push it forward.&lt;/p&gt;

&lt;h2 id=&#34;restarting-the-development-effort&#34;&gt;Restarting the Development Effort&lt;/h2&gt;

&lt;h3 id=&#34;extracting-a-standalone-implementation&#34;&gt;Extracting a Standalone Implementation&lt;/h3&gt;

&lt;p&gt;In September 2021, I was working on adding some analytics tracking to my day job&#39;s app. I specifically wanted to be able to save some analytics events based on dispatched actions. I knew that other analytics-related middleware existed, but I remembered the WIP &amp;quot;action listener&amp;quot; middleware PR and figured it might be a good fit.&lt;/p&gt;

&lt;p&gt;The original PR had long grown stale compared to the current RTK release, so I ended up copy-pasting the entire middleware source from the PR directly into our own app. In the process, I also hacked in the ability to pass in a &amp;quot;matcher&amp;quot; function as well to allow matching against multiple possible action types, not even remembering that Lenz had suggested something similar earlier. (I ended up doing this by adding a separate code path for &amp;quot;matchers&amp;quot; vs &amp;quot;type&amp;quot;-based comparisons, which required a chunk of duplicated code at the time.)&lt;/p&gt;

&lt;p&gt;Since I&#39;d done the work to make the middleware run as a standalone implementation, I went ahead and &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/pull/547#issuecomment-915612643&#34;&gt;pasted the source into a comment in the original PR&lt;/a&gt; in case anyone else wanted to try it.&lt;/p&gt;

&lt;h3 id=&#34;creating-a-new-incubator-package&#34;&gt;Creating a New Incubator Package&lt;/h3&gt;

&lt;p&gt;Another couple months went by. In early November, I remembered the middleware again, and finally decided to do something about pushing the effort forward.&lt;/p&gt;

&lt;p&gt;By this time we&#39;d turned the RTK repo into a monorepo, containing the original RTK package plus a couple of auxiliary packages related to RTK Query code generation. I copy-pasted the build setup from one of those, and published the standalone middleware source as &lt;code&gt;@rtk-incubator/action-listener-middleware&lt;/code&gt; v0.1.0. I then put up &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/discussions/1648&#34;&gt;&lt;strong&gt;RTK discussion #1648: New experimental &amp;quot;action listener&amp;quot; middleware&lt;/strong&gt;&lt;/a&gt; as a thread to house discussion and further iteration on the middleware.&lt;/p&gt;

&lt;h3 id=&#34;updating-the-design-goals&#34;&gt;Updating the Design Goals&lt;/h3&gt;

&lt;p&gt;I took the time to go back through all the prior existing discussions in the issues and PRs, and &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/discussions/1648#discussioncomment-1598002&#34;&gt;assembled a list of relevant comments and suggestions on use cases and possible API features&lt;/a&gt;. I then wrote an extended comment where I &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/discussions/1648#discussioncomment-1599805&#34;&gt;summarized the existing API, listed remaining open questions, and offered my suggestions for design behavior&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I got a couple other comments with some excellent suggestions, like removing the &lt;code&gt;stopPropagation&lt;/code&gt; method on the grounds that it was confusing semantically.&lt;/p&gt;

&lt;h2 id=&#34;shaping-the-api&#34;&gt;Shaping the API&lt;/h2&gt;

&lt;h3 id=&#34;v0-2-finding-the-right-primitives&#34;&gt;v0.2: Finding the Right Primitives&lt;/h3&gt;

&lt;p&gt;I spent the next couple nights hacking on several of those ideas, and &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/discussions/1648#discussioncomment-1603347&#34;&gt;published v0.2.0&lt;/a&gt; containing the updates.&lt;/p&gt;

&lt;p&gt;v0.2 had several important changes: it dropped the &lt;code&gt;stopPropagation&lt;/code&gt; method, added an &lt;code&gt;extra&lt;/code&gt; argument to middleware creation, added &lt;code&gt;getOriginalState&lt;/code&gt; to the listener API, and added some basic &lt;code&gt;try/catch&lt;/code&gt; error handling.&lt;/p&gt;

&lt;p&gt;However, as I was working on these changes, I already had bigger ideas in mind for further down the road. We&#39;d said from the beginning that &amp;quot;listeners&amp;quot; weren&#39;t supposed to replace sagas. After all, sagas were meant for really complex async workflows, and you couldn&#39;t do most of the really powerful saga operations like cancellation, throttling, debouncing, forked child jobs, long-running async workflows, or &lt;code&gt;takeLatest&lt;/code&gt; without using generator functions.&lt;/p&gt;

&lt;p&gt;Or... &lt;em&gt;could&lt;/em&gt; you? &lt;strong&gt;What if we could replicate &lt;em&gt;most&lt;/em&gt; of the functionality of sagas, by adding some key primitives to the listener API design?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This thought started running through my head, and I began having some admittedly grandiose ideas for what this &amp;quot;action listener&amp;quot; middleware could someday become.&lt;/p&gt;

&lt;p&gt;At the same time, I happened to be chatting with &lt;a href=&#34;https://twitter.com/swyx&#34;&gt;Shawn Swyx Wang&lt;/a&gt;. Shawn was working at a company called &lt;a href=&#34;https://temporal.io&#34;&gt;Temporal.io&lt;/a&gt;, which lets devs write very long-running async workflows as code (like a monthly billing cycle implemented as &lt;code&gt;while(true) { sleep(&amp;quot;30days&amp;quot;); billUser(); })&lt;/code&gt;). I figured he might have some useful input, given the conceptual similarity of &amp;quot;async workflows&amp;quot;.&lt;/p&gt;

&lt;p&gt;Shawn specifically pointed me to one of Temporal&#39;s workflow APIs as a possible enhancement: &lt;a href=&#34;https://docs.temporal.io/docs/typescript/workflows/#condition&#34;&gt;&lt;code&gt;condition&lt;/code&gt;&lt;/a&gt;. Temporal&#39;s &lt;code&gt;condition&lt;/code&gt; method lets workflows pause themselves until the provided callback function returns &lt;code&gt;true&lt;/code&gt;. It also accepts an optional timeout, and returns a &lt;code&gt;Promise&amp;lt;boolean&amp;gt;&lt;/code&gt;. That means it can be used as &lt;code&gt;if (await condition(someCheck, timeout))&lt;/code&gt;, allowing for both async behavior &lt;em&gt;and&lt;/em&gt; easy use with conditional checks.&lt;/p&gt;

&lt;p&gt;After looking at that, it was pretty easy to add an equivalent function to the listener API. I wrote it in about 30 lines, and implemented it as a one-shot listener that resolved a &amp;quot;succeeded&amp;quot; promise, raced against a &amp;quot;timeout&amp;quot; promise.&lt;/p&gt;

&lt;p&gt;I published the &lt;code&gt;condition&lt;/code&gt; method as part of v0.2, and specifically pointed to that in the announcement as an example of enabling &amp;quot;long-running async workflows&amp;quot;.&lt;/p&gt;

&lt;h3 id=&#34;v0-3-fixing-listener-syntax-and-types&#34;&gt;v0.3: Fixing Listener Syntax and Types&lt;/h3&gt;

&lt;p&gt;At this time, the API for adding a listener still looked like:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-js&#34;&gt;middleware.addListener(typeOrActionCreatorOrMatcher, listenerCallback, options);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I wanted to get the &lt;code&gt;action&lt;/code&gt; correctly typed inside of listeners, but this wasn&#39;t providing type inference because of the overload behavior.&lt;/p&gt;

&lt;p&gt;I decided it was best to switch &lt;code&gt;addListener&lt;/code&gt; to take a single options object containing several different possible fields for matching against an action. The question was how to get the type inference working correctly. I also wanted to be able to use the same options object typedefs for both &lt;code&gt;middleware.addListener&lt;/code&gt; and the &amp;quot;add&amp;quot; action creator. Additionally, at this time the &lt;code&gt;listenerApi.getState&lt;/code&gt; method was still hard-typed to return &lt;code&gt;unknown&lt;/code&gt;, because there was no way of knowing the actual &lt;code&gt;RootState&lt;/code&gt; type before the middleware was created. I figured our best bet was to provide support for declaring &amp;quot;pre-typed&amp;quot; versions of both those functions, similar to how React-Redux exports a &lt;code&gt;TypedUseSelectorHook&lt;/code&gt; type.&lt;/p&gt;

&lt;p&gt;I was able to get the actual code rearranged to take an options object pretty easily. However, the type inference was refusing to cooperate. I spent a couple days banging my head against it before I acknowledged that my TS-fu simply wasn&#39;t strong enough to solve this one myself.&lt;/p&gt;

&lt;p&gt;I &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/discussions/1648#discussioncomment-1623549&#34;&gt;posted a comment with my WIP code, described the issue, and begged for help&lt;/a&gt;. Happily, within the next couple days, I got the assistance I needed. Josh DeGraw gave me an example of extracting the common options as a separate type, and Lenz figured out how to get the overload type inference working correctly.&lt;/p&gt;

&lt;p&gt;With that hurdle gone, I &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/discussions/1648#discussioncomment-1635092&#34;&gt;published v0.3 with the switch to an options object for adding listeners&lt;/a&gt;, as well as the ability to pre-type the &amp;quot;add&amp;quot; methods.&lt;/p&gt;

&lt;p&gt;v0.3 also included a couple other key new capabilities. The &lt;code&gt;listenerApi&lt;/code&gt; object already had an &lt;code&gt;unsubscribe&lt;/code&gt; method. A user suggested adding &lt;code&gt;subscribe&lt;/code&gt; as well. This would allow listener callbacks to mimic the &lt;code&gt;takeLeading&lt;/code&gt; effect from sagas, by calling &lt;code&gt;unsubscribe&lt;/code&gt; at the start to prevent any other instances of that listener from running, and re-subscribing at the end of the callback.&lt;/p&gt;

&lt;h3 id=&#34;v0-4-child-job-support&#34;&gt;v0.4: &amp;quot;Child Job&amp;quot; Support&lt;/h3&gt;

&lt;p&gt;From the beginning, we&#39;d said that this &amp;quot;action listener&amp;quot; middleware wasn&#39;t supposed to be a full replacement for sagas or observables. However, the addition of the &lt;code&gt;condition&lt;/code&gt; and &lt;code&gt;subscribe/unsubscribe&lt;/code&gt; methods had gotten me wondering if it &lt;em&gt;might&lt;/em&gt; be possible to match the capabilities of sagas after all. I figured it was worth at least doing some research and exploration to see what was feasible, and to do so at this point while the middleware was still in alpha, rather than release something &amp;quot;final&amp;quot; and realize we&#39;d missed some key use cases.&lt;/p&gt;

&lt;p&gt;I spent several days doing some significant research on the APIs and capabilities of multiple libraries across the JS ecosystem: &lt;code&gt;redux-saga&lt;/code&gt;, &lt;code&gt;redux-observable&lt;/code&gt;, &lt;code&gt;redux-logic&lt;/code&gt;, &lt;code&gt;xstate&lt;/code&gt;, &lt;code&gt;ember-concurrency&lt;/code&gt;, and several others, then &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/discussions/1648#discussioncomment-1708301&#34;&gt;wrote up my research notes as an extended comment&lt;/a&gt;. My conclusions were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The current &amp;quot;action listener&amp;quot; middleware API let us do the equivalent of &lt;code&gt;takeEvery&lt;/code&gt; and &lt;code&gt;takeLeading&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;takeLatest&lt;/code&gt; would require cancellation support&lt;/li&gt;
&lt;li&gt;Every library that realistically supported cancellation required use of generator functions, because promises are not inherently cancellable&lt;/li&gt;
&lt;li&gt;sagas and &lt;code&gt;ember-concurrency&lt;/code&gt; both supported &amp;quot;child tasks&amp;quot;, and those also required cancellation support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I really didn&#39;t want to turn the listener middleware into another generator-based implementation. At that point it would just be a cheap knockoff of sagas, and there&#39;d be no point in it. But, promise cancellation is a quagmire topic - hundreds of people much smarter than me have fought over that in API designs for years, with no good resolution.&lt;/p&gt;

&lt;p&gt;A search of NPM for &amp;quot;async cancellation&amp;quot;-related libs &lt;em&gt;did&lt;/em&gt; turn up a couple of interesting possibilities. One was &lt;a href=&#34;https://github.com/ethossoftworks/job-ts&#34;&gt;https://github.com/ethossoftworks/job-ts&lt;/a&gt; , a library that provided a &amp;quot;job&amp;quot; abstraction with promise-based cancellation support. I also found a gist at &lt;a href=&#34;https://gist.github.com/andrewcourtice/ef1b8f14935b409cfe94901558ba5594&#34;&gt;https://gist.github.com/andrewcourtice/ef1b8f14935b409cfe94901558ba5594&lt;/a&gt; with a &amp;quot;task&amp;quot; API that used &lt;code&gt;AbortController&lt;/code&gt; internally.&lt;/p&gt;

&lt;p&gt;After playing around with the &lt;code&gt;job-ts&lt;/code&gt; library, I decided its &amp;quot;job&amp;quot; abstraction seemed like a good match for how saga child tasks worked. I ended up copying the source over into the middleware package, and wrapped each listener invocation in a &lt;code&gt;Job&lt;/code&gt; instance. That enabled me to add a form of cancellation for each running listener instance, and also let listeners kick off child jobs as well.]&lt;/p&gt;

&lt;p&gt;Meanwhile, Faber Vitale pointed out that &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/discussions/1648#discussioncomment-1717640&#34;&gt;we didn&#39;t have an equivalent of the saga &lt;code&gt;take&lt;/code&gt; API&lt;/a&gt;. The &lt;code&gt;condition&lt;/code&gt; method we&#39;d added was very similar, but it returned a boolean and not the matched action. I agreed that would be useful, and Faber filed a PR to add &lt;code&gt;take&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/discussions/1648#discussioncomment-1748104&#34;&gt;released v0.4&lt;/a&gt; with &lt;code&gt;take&lt;/code&gt;, listener cancellation, and &amp;quot;job&amp;quot; support in early December 2021. With those APIs now available, I was then able to write &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/blob/18cf0cb1eff4e50363cc3d90804010885087a2e0/packages/action-listener-middleware/src/tests/effectScenarios.test.ts&#34;&gt;a test file demonstrating equivalent behavior to the &lt;code&gt;takeLatest&lt;/code&gt;, &lt;code&gt;takeLeading&lt;/code&gt;, &lt;code&gt;throttle&lt;/code&gt;, &lt;code&gt;debounce&lt;/code&gt;, &lt;code&gt;fork + join&lt;/code&gt;, and &lt;code&gt;fork + cancel&lt;/code&gt; saga effects&lt;/a&gt;, showing that the WIP &amp;quot;action listener&amp;quot; middleware could now do most of the same things as sagas.&lt;/p&gt;

&lt;h3 id=&#34;v0-5-tasks-and-abortcontroller&#34;&gt;v0.5: &amp;quot;Tasks&amp;quot; and &lt;code&gt;AbortController&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;The &amp;quot;jobs&amp;quot; API worked, but it added a bit more bundle size than I wanted. The implementation was based on a class, and I&#39;d had success shaving some bytes in React-Redux by converting a class to a closure for better minification results. I gave that a shot here, but didn&#39;t see any real improvement.&lt;/p&gt;

&lt;p&gt;At the same time, Faber decided to try replacing the &amp;quot;jobs&amp;quot; API entirely. Instead, he reimplemented cancellation behavior and the &lt;code&gt;take/condition&lt;/code&gt; methods using a &amp;quot;task&amp;quot; abstraction that was built around &lt;code&gt;AbortController&lt;/code&gt; instead. Because &lt;code&gt;fork/delay/pause&lt;/code&gt; had been part of the &lt;code&gt;jobs-ts&lt;/code&gt; API, we ended up adding those as methods inside of &lt;code&gt;listenerApi&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;This shaved off about 2K min, leaving the middleware around 4k min in total. I had liked the &amp;quot;jobs&amp;quot; API and concept, but smaller code is a big win, so &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/discussions/1648#discussioncomment-1810315&#34;&gt;I shipped that as v0.5&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&#34;polish-and-refinement&#34;&gt;Polish and Refinement&lt;/h2&gt;

&lt;p&gt;At this point it felt like we&#39;d settled on the right capabilities, use case support, and general design for the API. The &amp;quot;action listener&amp;quot; middleware could now do most of the same things as sagas, but with a much smaller API and bundle size.&lt;/p&gt;

&lt;p&gt;I tried asking for user feedback on Twitter, but wasn&#39;t really getting much of a response. So, it was up to us to figure out what else needed tweaking.&lt;/p&gt;

&lt;h3 id=&#34;v0-6-simplifying-behavior&#34;&gt;v0.6: Simplifying Behavior&lt;/h3&gt;

&lt;p&gt;Faber pointed out that the original &lt;code&gt;when&lt;/code&gt; option was basically useless now that there was no longer an option to cancel action processing. Lenz and I agreed, so I went ahead and ripped that out.&lt;/p&gt;

&lt;p&gt;In the process, I noted that &lt;code&gt;listenerApi.cancelPrevious()&lt;/code&gt; was misleading, because it actually cancelled &lt;em&gt;all&lt;/em&gt; other running instances of that listener regardless of ordering. I ended up renaming it &lt;code&gt;listenerApi.cancelActiveListeners()&lt;/code&gt; for clarity.&lt;/p&gt;

&lt;p&gt;Those changes went out in v0.6.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/discussions/1648#discussioncomment-1850282&#34;&gt;I summarized the open questions at that point&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;We need to review the types for &lt;code&gt;extra&lt;/code&gt; and figure out how to get that to carry through properly&lt;/li&gt;
&lt;li&gt;Not happy with how &lt;code&gt;removeListener&lt;/code&gt; is solely based on action type right now&lt;/li&gt;
&lt;li&gt;Should passing in an entry with the same listener reference still skip adding a new entry and return the existing one, or should we actually have two distinct entries added?&lt;/li&gt;
&lt;li&gt;Should we add some kind of a &amp;quot;bulk add listeners&amp;quot; API somehow? Is that worth it?&lt;/li&gt;
&lt;li&gt;Are we happy with the likely usage pattern of having a &lt;code&gt;listenerMiddleware.ts&lt;/code&gt; file that creates the middleware and exports it, and then slices would have to import that and call &lt;code&gt;middleware.addListener()&lt;/code&gt;? Is there a better setup pattern more akin to &lt;code&gt;createSlice + configureStore()&lt;/code&gt;, where a slice file can define an entry that can be imported into &lt;code&gt;listenerMiddleware.ts&lt;/code&gt; and added?&lt;/li&gt;
&lt;li&gt;The middleware return types still aren&#39;t working right - &lt;code&gt;const unsubscribe = store.dispatch(addListenerAction())&lt;/code&gt; results in a &lt;code&gt;{type: string, payload: ListenerEntry|&lt;/code&gt; type, when it should be an &lt;code&gt;Unsubscribe&lt;/code&gt; type. Not sure if this is due to the listener middleware types, &lt;code&gt;configureStore&lt;/code&gt; types, or thunk types.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3 id=&#34;v0-7-cleanup-improvements&#34;&gt;v0.7: Cleanup Improvements&lt;/h3&gt;

&lt;p&gt;A user asked if &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/discussions/1648#discussioncomment-2009989&#34;&gt;there was a way to clear all listeners entirely&lt;/a&gt;, particularly for use with cleaning up a shared store instance during tests.&lt;/p&gt;

&lt;p&gt;Faber filed a PR to add &lt;code&gt;middleware.clear()&lt;/code&gt;. We&#39;d also noted that the &amp;quot;remove&amp;quot; functions only allowed matching based on exact action type, and Faber was able to rewrite them to accept the same options object as the &amp;quot;add&amp;quot; functions for consistency.&lt;/p&gt;

&lt;p&gt;We also noted that &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/discussions/1648#discussioncomment-1932820&#34;&gt;the existing behavior for the &lt;code&gt;getOriginalState&lt;/code&gt; method could potentially lead to memory leaks&lt;/a&gt;. &lt;code&gt;getOriginalState&lt;/code&gt; captures the value of &lt;code&gt;state&lt;/code&gt; &lt;em&gt;before&lt;/em&gt; the reducer has a chance to process the action. Since that was always being passed in to each callback as part of &lt;code&gt;listenerApi&lt;/code&gt;, that meant that the state reference was being kept around as long as any listener callback was still running.&lt;/p&gt;

&lt;p&gt;We fixed that issue by reworking the internals of the middleware so that &lt;code&gt;getOriginalState&lt;/code&gt; can only be called synchronously during the original &lt;code&gt;dispatch&lt;/code&gt; call stack. If a listener really wants to have access to that value later, it needs to call &lt;code&gt;getOriginalState()&lt;/code&gt; right away and save the reference explicitly.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/discussions/1648#discussioncomment-2078720&#34;&gt;Those fixes went out as v0.7&lt;/a&gt; at the end of January 2022.&lt;/p&gt;

&lt;h3 id=&#34;typescript-issues&#34;&gt;TypeScript Issues&lt;/h3&gt;

&lt;p&gt;I had noted much earlier that there were a couple remaining TS-related issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;createActionListenerMiddleware()&lt;/code&gt; accepted an &lt;code&gt;extra&lt;/code&gt; argument, but &lt;code&gt;listenerApi.extra&lt;/code&gt; was always typed as &lt;code&gt;unknown&lt;/code&gt; - the provided value&#39;s type wasn&#39;t being carried through the rest of the middleware types&lt;/li&gt;
&lt;li&gt;&lt;code&gt;const unsubscribe = dispatch(addListener())&lt;/code&gt; was typed as returning the &lt;code&gt;{type: &#39;alm/addListener&#39;}&lt;/code&gt; action object, when the code actually returned an &lt;code&gt;Unsubscribe&lt;/code&gt; callback&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fixing the &lt;code&gt;extra&lt;/code&gt; type behavior was easy. However, as I dug into the &lt;code&gt;unsubscribe&lt;/code&gt; type behavior, I just couldn&#39;t understand why TS was failing to recognize that the middleware&#39;s types changed the return of &lt;code&gt;dispatch&lt;/code&gt; when that particular action was passed through.&lt;/p&gt;

&lt;p&gt;Lenz took a look, and after some discussion we concluded that there were really two parts of the problem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The types for &lt;code&gt;getDefaultMiddleware()&lt;/code&gt; were not doing a sufficient job of inferring any available overrides of &lt;code&gt;dispatch&lt;/code&gt;. There was some hardcoded handling internally for the thunk middleware&#39;s types specifically, but if any other middleware tried to augment &lt;code&gt;dispatch&lt;/code&gt;, those types weren&#39;t getting included&lt;/li&gt;
&lt;li&gt;&lt;code&gt;createActionListenerMiddleware()&lt;/code&gt; created and returned the actual middleware instance, but also attached the &lt;code&gt;{addListener, removeListener}&lt;/code&gt; methods directly onto the middleware instance itself. Lenz had said earlier that he&#39;d &amp;quot;solved the problem with brute force&amp;quot;, but we determined that this somehow changed the type of the middleware enough that TS could no longer infer any &lt;code&gt;dispatch&lt;/code&gt; augmentation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I ended up &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/pull/2001&#34;&gt;rewriting the types for RTK&#39;s &lt;code&gt;getDefaultMiddleware&lt;/code&gt; completely&lt;/a&gt;. We already had an internal &lt;code&gt;MiddlewareArray&lt;/code&gt; type that provided custom &lt;code&gt;concat/prepend&lt;/code&gt; methods for improved typing, since &lt;code&gt;[...getDefaultMiddleware()]&lt;/code&gt; seemed to lose types. I was able to borrow some advanced TS type inference techniques I&#39;d learned while working on Reselect 4.1, and upgrade its ability to extract the exact type of each middleware. I then rewrote the &lt;code&gt;concat/prepend&lt;/code&gt; methods to return concatenated TS tuple types with the exact types of the added middleware, rather than a much looser &lt;code&gt;Middleware[]&lt;/code&gt; array type. This preserved any potential &lt;code&gt;dispatch&lt;/code&gt; augmentation type info.&lt;/p&gt;

&lt;p&gt;Those changes went up as a PR for RTK itself, and I put them on an integration branch that would later become RTK 1.8.&lt;/p&gt;

&lt;p&gt;For the middleware methods, we decided that our best option was to change the return structure of the &amp;quot;create middleware&amp;quot; function. Instead of returning the middleware directly and attaching the methods, we decided we&#39;d have to &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/pull/2005&#34;&gt;return an object &lt;em&gt;containing&lt;/em&gt; the instance and the methods&lt;/a&gt;, like &lt;code&gt;{middleware, addListener, removeListener}&lt;/code&gt;. It was a bit annoying to have to do that just to placate TS, but we felt it was necessary to get the right typing behavior and developer usage.&lt;/p&gt;

&lt;h3 id=&#34;v0-8-bikeshedding-naming&#34;&gt;v0.8: Bikeshedding Naming!&lt;/h3&gt;

&lt;p&gt;Faber had suggested earlier that &amp;quot;action listener middleware&amp;quot; and &lt;code&gt;createActionListenerMiddleware&lt;/code&gt; were too long, and I agreed.&lt;/p&gt;

&lt;p&gt;Alongside the return value changes, I proposed that we change the creation function name to &lt;code&gt;createListenerMiddleware&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The other naming concern I had was that the naming clash between &lt;code&gt;middleware.addListener()&lt;/code&gt; and the separate &lt;code&gt;addListener&lt;/code&gt; action creator. I wanted there to be some kind of difference in the naming scheme to avoid confusion.&lt;/p&gt;

&lt;p&gt;Lenz and I started bikeshedding some naming ideas, and &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/discussions/1648#discussioncomment-2121249&#34;&gt;I laid out a list of some possibilities&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The main questions are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What should we call the object returned from the &lt;code&gt;create()&lt;/code&gt; function?

&lt;ul&gt;
&lt;li&gt;Related to this, Lenz isn&#39;t keen on having the examples destructure &lt;code&gt;{middleware, addListener}&lt;/code&gt;, and would prefer something like &lt;code&gt;obj.middleware&lt;/code&gt; instead&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Lenz suggested renaming the main API to &lt;code&gt;createListener()&lt;/code&gt;, so that you have &lt;code&gt;listener.middleware&lt;/code&gt;, etc

&lt;ul&gt;
&lt;li&gt;Problem is that we&#39;ve been referring to the actual callback functions as &amp;quot;listeners&#39;, so that would require coming up with a different name for those&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;We do want to differentiate between the &amp;quot;instance methods&amp;quot; for adding and removing entries, vs the action creators&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we were to go with createListener, then some options are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;methods:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;add/remove&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;addX/removeX&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;attach/detach&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;subscribe/unsubscribe&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;actions:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;addX/removeX&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;xAdded/yRemoved&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;subscribeListener/unsubscribeListener&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;field/functions:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;listener&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;callback&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;effect&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;I put out a request for feedback on Twitter, but got no response. A week later I followed up with my own preferences:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Keep &lt;code&gt;createListenerMiddleware&lt;/code&gt;. It makes it clear what we&#39;re doing, and it matches the &lt;code&gt;createSagaMiddleware&lt;/code&gt; / &lt;code&gt;createEpicMiddleware&lt;/code&gt; naming pattern from those libraries.&lt;/li&gt;
&lt;li&gt;Keep the existing &lt;code&gt;listener&lt;/code&gt; name for the function field&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That means we just need to come up with a consistent name for the object returned from &lt;code&gt;createListenerMiddleware&lt;/code&gt; that contains &lt;code&gt;{middleware, addListener, removeListener}&lt;/code&gt;, and settle on a name for the action creators.&lt;/p&gt;

&lt;p&gt;One option would be to give the action creators all &amp;quot;past tense&amp;quot; names: &lt;code&gt;listenerAdded&lt;/code&gt;, &lt;code&gt;listenerRemoved&lt;/code&gt;, &lt;code&gt;listenersCleared&lt;/code&gt;. That sort of aligns with the &amp;quot;actions as events&amp;quot; mindset.... except that these aren&#39;t even actions that are meant to update state, they really are direct commands to the middleware itself. So, that slightly bothers me too. But it would at least give us a naming convention to distinguish the action creators from the instance methods.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I linked the comment over in Reactiflux and mentioned it on Twitter. This time, &lt;a href=&#34;https://discord.com/channels/102860784329052160/103538784460615680/942121668522958910&#34;&gt;we actually got a good discussion going in the Reactiflux &lt;code&gt;#redux&lt;/code&gt; channel&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A couple hours of intense debate followed, with numerous naming options thrown around. We finally settled on a naming scheme that had several changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API: &lt;code&gt;createListenerMiddleware&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;return object: &lt;code&gt;{middleware, startListening, stopListening, clearListeners}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Docs examples show the object as &lt;code&gt;listenerMiddleware&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;One entry: &amp;quot;listener&amp;quot;&lt;/li&gt;
&lt;li&gt;Field in that listener entry: &lt;code&gt;effect&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Action creators: &lt;code&gt;addListener&lt;/code&gt;, &lt;code&gt;removeListener&lt;/code&gt;, &lt;code&gt;removeAllListeners&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;pre&gt;&lt;code class=&#34;language-ts&#34;&gt;const listenerMiddleware = createListenerMiddleware();

listenerMiddleware.startListening({ type, effect });
listenerMiddleware.stopListening({ type, effect });

configureStore({
  middleware: (gDM) =&amp;gt; gDM().prepend(listenerMiddleware.middleware),
});

const unsubscribe = store.dispatch(addListener({ type, effect }));
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;With all those names finalized, I went ahead and made the naming and return value changes and released it as v0.8.&lt;/p&gt;

&lt;h2 id=&#34;finalizing-the-release&#34;&gt;Finalizing the Release&lt;/h2&gt;

&lt;h3 id=&#34;moving-the-source&#34;&gt;Moving the Source&lt;/h3&gt;

&lt;p&gt;The listener middleware had been published as a standalone &lt;code&gt;@rtk-incubator/action-listener-middleware&lt;/code&gt; package this whole time, but the plan was always to move it over into the actual RTK package for a final release. I had originally planned to do that in RTK 1.8, and figured that 1.8 would also include several other changes (like new RTK Query options). But, after Lenz said he was busy and wouldn&#39;t have time to work on RTKQ for a while, I decided it was best to just release 1.8 with only the new listener middleware.&lt;/p&gt;

&lt;p&gt;I threw together a PR that moved the middleware source over into the actual RTK package folder. At the same time, Faber filed a couple more PRs to tweak remaining edge cases, like the resolved promise value of child tasks.&lt;/p&gt;

&lt;h3 id=&#34;last-minute-bugs&#34;&gt;Last-Minute Bugs&lt;/h3&gt;

&lt;p&gt;I wanted to make sure that the middleware was actually getting published correctly as part of the main RTK package. I use &lt;a href=&#34;https://www.npmjs.com/package/yalc&#34;&gt;a tool called &lt;code&gt;yalc&lt;/code&gt; that lets me do local &amp;quot;publishes&amp;quot; of WIP packages&lt;/a&gt;. This gives much more consistent and realistic behavior for testing packages than trying to do &lt;code&gt;npm link&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;All of our unit tests were passing, but to be realistic I figured it was worth installing the local RTK build into a real project. I set up a new Vite project and copy-pasted in the source for the &amp;quot;counter&amp;quot; example that Faber had added to the RTK repo.&lt;/p&gt;

&lt;p&gt;This turned out to be a very smart decision, because &lt;strong&gt;the middleware was completely broken in the example app!&lt;/strong&gt; I could add it to the store and call &lt;code&gt;listenerMiddleware.startListening()&lt;/code&gt;, but none of the listener effects were running when I clicked buttons.&lt;/p&gt;

&lt;p&gt;I tried debugging with the browser devtools. Somehow it looked like the debugger was hitting the line that looped over the available listener entries... but never actually checking to see if any of them should run.&lt;/p&gt;

&lt;p&gt;At this point I was seriously confused. Fortunately, I had another trick up my sleeve. I had recently announced that &lt;a href=&#34;https://blog.isquaredsoftware.com/2022/02/joining-replay/&#34;&gt;I was joining Replay.io&lt;/a&gt;. &lt;strong&gt;&lt;a href=&#34;https://replay.io&#34;&gt;Replay&lt;/a&gt; is a true &amp;quot;time-traveling debugger&amp;quot; - it lets you record app usage, and then investigate the app&#39;s behavior and execution at any point in that recording&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I recorded a replay of the counter app behavior and opened it up. I saw the same bizarre behavior where the loop body wasn&#39;t actually getting executed. I even pulled in &lt;a href=&#34;https://twitter.com/jlaster11&#34;&gt;Jason Laster, CEO of Replay&lt;/a&gt; to look at it, and he saw the same thing.&lt;/p&gt;

&lt;p&gt;I finally happened to switch from looking at the sourcemapped view of the original code over to the transpiled view of what was actually being run by the browser.... and &lt;em&gt;there&lt;/em&gt; I saw what was going on. The middleware used a JS &lt;code&gt;Map&lt;/code&gt; internally to keep track of listener entries, and tried to use a &lt;code&gt;for..of&lt;/code&gt; loop to loop over those entries for conditional checks. Somehow that loop&#39;s comparison was failing, and it truly wasn&#39;t stepping into the loop body.&lt;/p&gt;

&lt;p&gt;Fortunately, I remembered that &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/commit/be6e198fecbc22c95b030e6796a947ff78c0888e&#34;&gt;we&#39;d seen the same bug a year earlier, and it turned out to be an ESBuild+TS transpilation issue&lt;/a&gt; . The workaround was easy, albeit annoying: &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/commit/1ddda6c11303c838d5abbf8b7a40f4ee0f37c95c&#34;&gt;copy the entries into an array first, then loop over that&lt;/a&gt;. (You can &lt;a href=&#34;https://app.replay.io/recording/rtk-listener-middleware-not-updating--1bb6f325-3e75-4958-83b5-1d87719d4b6c&#34;&gt;&lt;strong&gt;see the replay of the actual recorded bug here&lt;/strong&gt;&lt;/a&gt;.)&lt;/p&gt;

&lt;h3 id=&#34;final-name-and-behavior-changes&#34;&gt;Final Name and Behavior Changes&lt;/h3&gt;

&lt;p&gt;As I was getting ready to actually publish 1.8, I noted that &lt;code&gt;middleware.clearListeners()&lt;/code&gt; cancelled any running instances, but &lt;code&gt;middleware.stopListening()&lt;/code&gt; and &lt;code&gt;dispatch(removeListener())&lt;/code&gt; did not. I updated those to accept an optional &lt;code&gt;{cancelActive?: true}&lt;/code&gt; parameter to enable cancellation upon removal.&lt;/p&gt;

&lt;p&gt;Finally, Faber pointed out that the &lt;code&gt;removeAllListeners&lt;/code&gt; action creator should be more distinct and consistent with the instance methods, so we renamed it to &lt;code&gt;clearAllListeners&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;And with that, &lt;a href=&#34;https://github.com/reduxjs/redux-toolkit/releases/tag/v1.8.0&#34;&gt;&lt;strong&gt;I finally released the listener middleware live in RTK v1.8.0!&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&#34;conclusions&#34;&gt;Conclusions&lt;/h2&gt;

&lt;p&gt;When I filed the original issue in 2019, I had no idea that the process would take over two years, or that we&#39;d end up effectively replacing sagas :) But, all the time, effort, iteration, and bikeshedding paid off, and I&#39;m very happy with the final result.&lt;/p&gt;

&lt;h3 id=&#34;reception&#34;&gt;Reception&lt;/h3&gt;

&lt;p&gt;I &lt;a href=&#34;https://twitter.com/acemarke/status/1498040623487758339&#34;&gt;announced the release of RTK 1.8 on February 27&lt;/a&gt; and also &lt;a href=&#34;https://twitter.com/acemarke/status/1498040623487758339&#34;&gt;linked the release notes on Reddit&lt;/a&gt;. To be honest I actually didn&#39;t expect it to be that big a deal - I figured this was more of a niche API to begin with, so I wasn&#39;t expecting a big response.&lt;/p&gt;

&lt;p&gt;To my very pleasant surprise, we&#39;ve already gotten a bunch of very positive feedback. Some examples:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;I just cobbled up something that will replace a complete set of sagas and all their associated r-r boilerplate with a single small listener middleware. Ridiculously easy!&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;This is great 🙌. Listening to state changes is something I&#39;ve been missing from thunks / sagas. If your actions are event-based and business logic is in the reducer, listening to actions is not enough.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;I literally am working on an app with where I need this feature (since I&#39;m using RTK), I check my phone and I see this update. Mind blown. I will give this a shot.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Quick shout out to you and your fellow RTK devs! I&#39;ve just implemented the new listener middleware for a feature that persists specific slices of redux to indexedDB via Dexie, and it was practically painless. Listen for changes to the slice in a prev/next predicate, ignore it if it&#39;s the rehydrate action, fire a side effect that updates the changed property in indexedDB, and... &lt;strong&gt;it just works. The docs are clear and concise as usual, the syntax is easy to grasp, and it&#39;s easily as powerful as sagas or rxjs epics&lt;/strong&gt; (at least how I&#39;ve seen them used) &lt;strong&gt;without hardly any of the cognitive overhead&lt;/strong&gt;. With this on top of RTK Query, I can finally get rid of all the clunky hard-to-test observables in this app and the weird actions firing actions firing actions spaghetti that&#39;s been built up over the last few years. Seriously, thank you so much. You&#39;ve all made my job a lot easier.&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is extremely exciting as a maintainer - it tells me that the new API &lt;em&gt;is&lt;/em&gt; solving the right use cases, and that the time and effort we put into it was worth it.&lt;/p&gt;

&lt;h3 id=&#34;key-api-design-moments&#34;&gt;Key API Design Moments&lt;/h3&gt;

&lt;p&gt;Looking back, there were several key ideas and sources of inspiration that led to the final design:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The original vision of &amp;quot;provide callbacks that run in response to specific actions&amp;quot; was solid&lt;/li&gt;
&lt;li&gt;&amp;quot;Add listeners at runtime with &lt;code&gt;dispatch(addListener())&lt;/code&gt;&amp;quot; was also there at the beginning and was a key part of the design the entire way through&lt;/li&gt;
&lt;li&gt;I had the idea for the &lt;code&gt;(action, currState, prevState) =&amp;gt; boolean&lt;/code&gt; &amp;quot;predicate&amp;quot; option right away, which turned out to enable running logic in response to state changes and not just actions&lt;/li&gt;
&lt;li&gt;Lenz&#39;s second PR ultimately formed the basis of the rest of the implementation&lt;/li&gt;
&lt;li&gt;The &amp;quot;run listeners before or after?&amp;quot; discussion led to adding &lt;code&gt;getOriginalState&lt;/code&gt;, as well as the &lt;code&gt;subscribe/unsubscribe&lt;/code&gt; methods&lt;/li&gt;
&lt;li&gt;My interest in using the listener middleware in my work app prompted me to add &lt;code&gt;matcher&lt;/code&gt; as an option, as well as extracting the existing PR code to work standalone and restarting the development work&lt;/li&gt;
&lt;li&gt;Faber argued for dropping &lt;code&gt;stopPropagation&lt;/code&gt; and &lt;code&gt;when&lt;/code&gt;, which simplified the design, and also came up with all the error handling concepts&lt;/li&gt;
&lt;li&gt;Shawn Swyx Wang suggested adding &lt;code&gt;condition&lt;/code&gt; based on Temporal&#39;s API (and amusingly my implementation of &lt;code&gt;condition(predicate, timeout?)&lt;/code&gt; prompted Temporal to rearrange their function&#39;s arguments order to match ours)&lt;/li&gt;
&lt;li&gt;That led to me thinking about expanding the scope of the middleware to try to match saga capabilities, and investigating topics like promise cancellation and child task support&lt;/li&gt;
&lt;li&gt;I had the idea for changing from positional parameters in &lt;code&gt;addListener&lt;/code&gt; to an options object, but Lenz Weber and Josh DeGraw came up with the TS solutions that unblocked my attempt to rewrite the types&lt;/li&gt;
&lt;li&gt;Faber suggested &lt;code&gt;take&lt;/code&gt; and &lt;code&gt;middleware.clearListeners&lt;/code&gt;, and rewrote the initial &amp;quot;jobs&amp;quot; API to provide similar cancellation behavior based on &lt;code&gt;AbortController&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Lenz and I investigated the &lt;code&gt;const unsubscribe = dispatch(addListener())&lt;/code&gt; TS issues. I was able to leverage my experience from Reselect 4.1 to rewrite the &lt;code&gt;MiddlewareArray&lt;/code&gt; and &lt;code&gt;getDefaultMiddleware&lt;/code&gt; types.&lt;/li&gt;
&lt;li&gt;Multiple people participated in the final name bikeshedding discussion&lt;/li&gt;
&lt;li&gt;I noted the lack of cancel-on-remove right at the end&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Clearly, this was a very iterative process! The end result definitely fulfilled my original vision, but it was &lt;em&gt;much&lt;/em&gt; more comprehensive than I could ever have imagined at the start.&lt;/p&gt;

&lt;h3 id=&#34;lessons-and-takeaways&#34;&gt;Lessons and Takeaways&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Hopefully this gives some insight into some of the process for designing a new library API&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I think there&#39;s several lessons that library maintainers can take away from this process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Have a clear vision and purpose for the API&lt;/strong&gt;: From the very beginning, I had a specific goal that I wanted this new API to accomplish. I saw a gap in RTK&#39;s functionality, and I specifically wanted to fill that gap. Additionally, while user feedback was absolutely critical (as discussed below), it was also important to keep the goals and implementation scoped and on track.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Design and discuss in public&lt;/strong&gt;: Lenz and I did the bulk of the implementation work. However, many of the key design decisions only happened because of specific discussions with interested community members, as they described potential use cases and offered suggestions for API design and behavior. There&#39;s no way Lenz and I could have worked out the final design on our own - that back-and-forth discussion was critical, and that was only possible because we did the entire process in public from day 1. (That&#39;s not to say that internal discussions or design work are bad / should be avoided, but the more you can get the community involved, the better.)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Running code encourages discussion&lt;/strong&gt;: Having a couple prototype PRs was critical. They gave us running examples to try out, and concrete code implementations to review and critique.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Get code in user hands ASAP, and iterate&lt;/strong&gt;: Early on, having CodeSandbox CI set up to publish installable RTK packages from the PR branches made it possible for people to try out the initial builds. Later on, the use of a separate &amp;quot;incubator&amp;quot; package plus an ongoing Github Discussions thread helped us iterate on development and nail down the final behavior.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tests and example use cases are valuable&lt;/strong&gt;: The unit tests for the middleware served multiple purposes. Besides actual code coverage, they acted as &amp;quot;type tests&amp;quot; to verify the TS compilation behavior. We also added tests that demonstrated specific use cases, like implementing various Redux-Saga effects and some other example usage scenarios. This helped us verify that the API could do what we wanted, and gave us ideas for how to tweak things. (And in the case of the final pre-release checks, checking behavior in a real app helped me find a critical transpilation/publish bug.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of course other tools and communities will have different experiences. I can actually point to two other fairly contrasting experiences with React-Redux:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Before we started dev work on React-Redux v8, I wanted the codebase converted to TS. I &lt;a href=&#34;https://github.com/reduxjs/react-redux/issues/1737&#34;&gt;created an issue asking for community participation&lt;/a&gt; and put out a call for help on Twitter, and over the next few weeks random community members helped convert the codebase a couple files at a time&lt;/li&gt;
&lt;li&gt;On the other hand, when I moved on to reworked React-Redux v8 to support React 18, &lt;a href=&#34;https://github.com/reduxjs/react-redux/pull/1808&#34;&gt;&lt;em&gt;I&lt;/em&gt; did all the dev work for the &lt;code&gt;useSyncExternalStore&lt;/code&gt; conversion myself&lt;/a&gt;, because I was the only one with the background knowledge and I knew what needed to be done.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As always, YMMV, but I think these principles can be useful for library maintainers that are looking to design new APIs.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Blogged Answers: The Evolution of Redux Testing Approaches</title>
      <link>https://blog.isquaredsoftware.com/2021/06/the-evolution-of-redux-testing-approaches/</link>
      <pubDate>Tue, 22 Jun 2021 22:00:00 -0500</pubDate>
      
      <guid>https://blog.isquaredsoftware.com/2021/06/the-evolution-of-redux-testing-approaches/</guid>
      <description>&lt;p&gt;&lt;/p&gt;

&lt;h2 id=&#34;introduction&#34;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;I was just asked a question about how testing works for Redux applications:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;For a beginner front-end developer, what would you expect them to know about Redux testing? (techniques and patterns specifically)&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;What - if any - tools are industry-standard for Redux testing? I&#39;ve seen some tutorials using redux-mock-store, fetchMock, redux-testkit, redux-action-assertions, etc.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;I ended up writing an extended response about the history of testing Redux apps, the two major styles of testing that I&#39;ve seen, and how those approaches have evolved over time - clearly worthy of being reposted publicly.&lt;/p&gt;

&lt;h2 id=&#34;testing-redux&#34;&gt;Testing Redux&lt;/h2&gt;

&lt;h3 id=&#34;what-should-a-beginning-dev-know-about-testing-redux&#34;&gt;What should a beginning dev know about testing Redux?&lt;/h3&gt;

&lt;p&gt;For #1, I wouldn&#39;t necessarily expect a beginner to know much testing at all, really :)  I actually just spent the last couple weeks helping guide a pair of interns through writing their first React component tests with React Testing Library, and it was a bit eye-opening when I had to explain so many different moving pieces: Jest, RTL, &lt;code&gt;describe&lt;/code&gt;, &lt;code&gt;it&lt;/code&gt;, &lt;code&gt;jest.fn()&lt;/code&gt;, &lt;code&gt;render()&lt;/code&gt;, &lt;code&gt;expect()&lt;/code&gt;, and even just &amp;quot;write multiple tests that feed in different inputs, and check to see what the output is&amp;quot;.  It&#39;s not an easy or natural thing, and I&#39;d kinda forgotten that.&lt;/p&gt;

&lt;p&gt;All that said: the bare basic bit of testing I&#39;d expect a beginner to be able to at least try or hopefully understand is testing reducers, because those are the simplest possible things to test: pure functions, whose entire logic depends on &lt;code&gt;(state, action)&lt;/code&gt;, and it&#39;s just &lt;code&gt;expect(actual).toEqual(expected)&lt;/code&gt; in some way&lt;/p&gt;

&lt;h3 id=&#34;what-s-standard-for-redux-testing&#34;&gt;What&#39;s standard for Redux testing?&lt;/h3&gt;

&lt;p&gt;For #2: this has varied a lot, and I think it&#39;s changed some over the last couple years.&lt;/p&gt;

&lt;p&gt;The real question here is about how you test your Redux code.  Do you test pieces in isolation?  reducers, selectors, thunks, sagas, etc.  Or do you test your Redux logic integrated into the rest of the app?&lt;/p&gt;

&lt;p&gt;Our docs have always taught the &amp;quot;isolation&amp;quot; approach, and that does especially make sense for reducers and selectors.  The &amp;quot;integration&amp;quot; approach was in a minority.&lt;/p&gt;

&lt;p&gt;But, RTL and Kent C Dodds have drastically changed the mindset and approach for testing in the React ecosystem. The patterns I see now are about &amp;quot;integration&amp;quot;-style tests - large chunks of code, working together, as they&#39;d be used in a real app.&lt;/p&gt;

&lt;p&gt;These two approaches lead to very different styles of writing tests.&lt;/p&gt;

&lt;h4 id=&#34;isolation-style-tests&#34;&gt;&amp;quot;Isolation&amp;quot;-style tests&lt;/h4&gt;

&lt;p&gt;With the &amp;quot;isolation&amp;quot; approach, again, writing tests for reducers and selectors is trivial - pure functions.&lt;/p&gt;

&lt;p&gt;Testing thunks and components, on the other hand, got a lot more complicated.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;redux-mock-store&lt;/code&gt;&#39;s primary purpose, as I understand it, is to collect a list of dispatched actions for later assertions: &amp;quot;yes, we did in fact dispatch &lt;code&gt;todoAdded&lt;/code&gt; with this text&amp;quot;, etc.  That makes sense when you&#39;re testing things in isolation.&lt;/p&gt;

&lt;p&gt;Testing thunks has always been really tricky for that approach, though.  You have to configure the mock store with the thunk middleware. And what about any async calls? Thunks tend to directly import AJAX libs or client API layers - Angular-style DI has never been a big practice in the React ecosystem. So, that makes it harder to test thunks that make async calls.  For that matter, thunks can dispatch &lt;em&gt;other&lt;/em&gt; thunks too.  So, testing dispatched actions is understandable here given those limitations.&lt;/p&gt;

&lt;p&gt;The thunk middleware &lt;em&gt;does&lt;/em&gt; have an &amp;quot;extra argument&amp;quot; that can be defined at middleware setup time, and that has been used for injecting some service layer for API calls that can be swapped with a mock version in tests.&lt;/p&gt;

&lt;p&gt;This also gets very tricky when looking at components, especially when using the &lt;code&gt;connect&lt;/code&gt; API with Enzyme.  One of the reasons for the popularity of the &amp;quot;container/presentational&amp;quot; pattern is that it made it very easy to test presentational components. They&#39;re props-only, no logic other than formatting, no dependencies on any external APIs or behavior.&lt;/p&gt;

&lt;p&gt;That concept is still a valid thing to do, but the React ecosystem has strongly moved away from &amp;quot;containers&amp;quot; thanks to hooks. I talked about this in my post &lt;a href=&#34;https://blog.isquaredsoftware.com/2019/07/blogged-answers-thoughts-on-hooks/&#34;&gt;Thoughts on React Hooks, Redux, and Separation of Concerns&lt;/a&gt;, and my talk &lt;a href=&#34;https://blog.isquaredsoftware.com/2019/09/presentation-hooks-hocs-tradeoffs/&#34;&gt;ReactBoston 2019: Hooks, HOCs, and Tradeoffs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;connect&lt;/code&gt; does have an option to pass a Redux store instance directly as a prop named &lt;code&gt;store&lt;/code&gt;. That works okay if you&#39;ve only got one level of connected component being tested, but if you&#39;re doing an Enzyme &lt;code&gt;mount()&lt;/code&gt;, which is a full render of the component tree, and there are &lt;em&gt;other&lt;/em&gt; connected components in that subtree, they don&#39;t get access to the store correctly.  So, you occasionally saw people creating real Redux stores and using a &lt;code&gt;&amp;lt;Provider&amp;gt;&lt;/code&gt; in their component tests, but it was definitely a rarer thing&lt;/p&gt;

&lt;h4 id=&#34;integration-style-tests&#34;&gt;&amp;quot;Integration&amp;quot;-style tests&lt;/h4&gt;

&lt;p&gt;Well, that has flipped around over the last couple years with the arrival of RTL and hooks.  Granted, I don&#39;t spend a lot of time reading other people&#39;s tests, and a lot of what I&#39;m about to describe is my &lt;em&gt;own&lt;/em&gt; experience working on some apps and working with other members of the Redux team.  But, what I&#39;m seeing is that &amp;quot;integration&amp;quot; style tests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;always create a real Redux store in a test and wrap the component under test in a &lt;code&gt;&amp;lt;Provider&amp;gt;&lt;/code&gt;. Typically there&#39;s a customized &lt;code&gt;render()&lt;/code&gt; function that wraps the RTL &lt;code&gt;render&lt;/code&gt; method, accepts a store as an option or creates one internally if none was passed in, and automatically does the provider wrapping.&lt;/li&gt;
&lt;li&gt;either fill the store in with fake data on creation, or dispatch some actions to load it up&lt;/li&gt;
&lt;li&gt;don&#39;t care about what actions were dispatched - what matters is &amp;quot;I click the &#39;Add Todo&#39; button, and another item shows up in the list&amp;quot;&lt;/li&gt;
&lt;li&gt;mock async requests at the &lt;code&gt;fetch/xhr&lt;/code&gt; level using tools like &lt;code&gt;msw&lt;/code&gt;, &lt;code&gt;miragejs&lt;/code&gt;, &lt;code&gt;jest-mock-fetch&lt;/code&gt;, or similar.  that way, none of the thunk logic has to change in a test - the thunk still tries to make a &amp;quot;real&amp;quot; async request, it just gets intercepted&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&#34;testing-sagas&#34;&gt;Testing Sagas&lt;/h4&gt;

&lt;p&gt;I know that a number of people have chosen to use sagas specifically because they &lt;em&gt;don&#39;t&lt;/em&gt; actually make real async requests in the saga function - it&#39;s just a stream of descriptions that the saga middleware is supposed to execute.  in theory, that makes them much more testable.&lt;/p&gt;

&lt;p&gt;having said that, I&#39;ve seen discussions showing some annoyance with testing sagas, because you can effectively end up testing internal implementation details: &amp;quot;first it yields X, then it yields Y&amp;quot;, etc.&lt;/p&gt;

&lt;p&gt;I believe &lt;a href=&#34;https://github.com/jfairbank/redux-saga-test-plan&#34;&gt;https://github.com/jfairbank/redux-saga-test-plan&lt;/a&gt; picked up some traction as a potentially better way to test sagas, but I haven&#39;t tried to do any of that myself&lt;/p&gt;

&lt;h3 id=&#34;recommendations&#34;&gt;Recommendations&lt;/h3&gt;

&lt;p&gt;I can say that we did update the Redux docs &amp;quot;Testing&amp;quot; page to show more of an &amp;quot;integration&amp;quot;-type approach for testing connected components recently, although I think we ought to make that a bit more clear.&lt;/p&gt;

&lt;p&gt;The couple projects I&#39;ve been on in the last couple years haven&#39;t had tons of tests, but the integration-style approach seems to work out very well for us.&lt;/p&gt;

&lt;p&gt;The new RTK Query APIs in Redux Toolkit are &lt;em&gt;entirely&lt;/em&gt; &amp;quot;integration&amp;quot;-style tests, using MSW for all the API calls, and that&#39;s worked out fantastically well.&lt;/p&gt;

&lt;p&gt;So. if you&#39;re going to teach anything, I&#39;d say:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;teach how to do basic tests for pure functions (reducers, selectors)&lt;/li&gt;
&lt;li&gt;teach integration tests for everything working together (&lt;code&gt;&amp;lt;Provider&amp;gt;&lt;/code&gt; + store wrapped around component, clicking a button does whatever real Redux logic, API calls are mocked out so app code doesn&#39;t have to change, assert UI is updated appropriately)&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    
    <item>
      <title>Presentations: Learn Modern Redux Livestream</title>
      <link>https://blog.isquaredsoftware.com/2021/05/learn-modern-redux-livestream/</link>
      <pubDate>Sat, 29 May 2021 16:00:00 -0500</pubDate>
      
      <guid>https://blog.isquaredsoftware.com/2021/05/learn-modern-redux-livestream/</guid>
      <description>&lt;p&gt;&lt;/p&gt;

&lt;p&gt;This week I had the chance to appear on &lt;a href=&#34;https://www.learnwithjason.dev/&#34;&gt;Jason Lengstorf&#39;s &amp;quot;Learn with Jason&amp;quot; livestream show&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The topic was &amp;quot;Learn Modern Redux&amp;quot;. We started by recapping some of the usual topics (&amp;quot;what is Redux?&amp;quot;, &amp;quot;why do some people dislike Redux?&amp;quot;, &amp;quot;how has Redux changed?&amp;quot;), and then dove into live-coding a real Redux app.  We showed how to create a new React+TS project using the &lt;a href=&#34;https://vitejs.dev&#34;&gt;Vite&lt;/a&gt; build tool (similar to Create-React-App), add the Redux packages, and set up Redux Toolkit and React-Redux from scratch (including our recommended TS hooks configuration).&lt;/p&gt;

&lt;p&gt;We then showed how to use the &lt;a href=&#34;https://deploy-preview-1016--redux-starter-kit-docs.netlify.app/rtk-query/overview&#34;&gt;upcoming RTK Query data fetching API&lt;/a&gt; to request a list of dog breeds from a public API, and display that data in our UI.&lt;/p&gt;

&lt;p&gt;I had a blast being on this show, and I think it was a great chance to show folks how we recommend writing Redux code today.&lt;/p&gt;

&lt;p&gt;The episode page on the &amp;quot;Learn with Jason&amp;quot; site has the embedded video, transcript, and show notes links:&lt;/p&gt;

&lt;h3 id=&#34;learn-modern-redux-episode-page-video-embed-and-transcript-https-www-learnwithjason-dev-let-s-learn-modern-redux&#34;&gt;&lt;a href=&#34;https://www.learnwithjason.dev/let-s-learn-modern-redux&#34;&gt;Learn Modern Redux - episode page, video embed, and transcript&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;The example app is available as a live demo and the source is in a Github repo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Running app: &lt;a href=&#34;https://lets-learn-redux-toolkit.netlify.app/&#34;&gt;https://lets-learn-redux-toolkit.netlify.app/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Source code: &lt;a href=&#34;https://github.com/learnwithjason/lets-learn-redux-toolkit&#34;&gt;https://github.com/learnwithjason/lets-learn-redux-toolkit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    
    <item>
      <title>Presentations: The State of Redux, May 2021</title>
      <link>https://blog.isquaredsoftware.com/2021/05/state-of-redux-may-2021/</link>
      <pubDate>Sat, 29 May 2021 16:00:00 -0500</pubDate>
      
      <guid>https://blog.isquaredsoftware.com/2021/05/state-of-redux-may-2021/</guid>
      <description>&lt;p&gt;&lt;/p&gt;

&lt;p&gt;I&#39;ve done several prior iterations of my &amp;quot;State of Redux&amp;quot; talk, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://blog.isquaredsoftware.com/2018/10/presentation-state-of-redux/&#34;&gt;React Boston 2018&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://blog.isquaredsoftware.com/2019/03/presentation-state-of-redux/&#34;&gt;Reactathon 2019&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://blog.isquaredsoftware.com/2020/10/presentation-state-of-redux-2020/&#34;&gt;Global React Meetup&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I recently had a chance to record an updated version of the talk for an internal conference, and have posted the video for this one myself.&lt;/p&gt;

&lt;p&gt;This iteration of the talk featured updates to the section on Redux Toolkit,  an introduction to RTK Query, and info on how we recommend using Redux and TypeScript together.&lt;/p&gt;

&lt;h3 id=&#34;the-state-of-redux-may-2021-video-https-youtu-be-odqg53ioub4&#34;&gt;&lt;a href=&#34;https://youtu.be/oDqg53iOub4&#34;&gt;The State of Redux, May 2021 - video&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id=&#34;the-state-of-redux-may-2021-slides-presentations-2021-05-state-of-redux-may-2021&#34;&gt;&lt;a href=&#34;https://blog.isquaredsoftware.com/presentations/2021-05-state-of-redux-may-2021/&#34;&gt;The State of Redux, May 2021 - slides&lt;/a&gt;&lt;/h3&gt;</description>
    </item>
    
    <item>
      <title>Presentations: Podcast Appearances in 2021</title>
      <link>https://blog.isquaredsoftware.com/2021/05/presentations-2021-podcasts/</link>
      <pubDate>Sat, 29 May 2021 15:00:00 -0500</pubDate>
      
      <guid>https://blog.isquaredsoftware.com/2021/05/presentations-2021-podcasts/</guid>
      <description>&lt;p&gt;&lt;/p&gt;

&lt;h2 id=&#34;intro&#34;&gt;Intro&lt;/h2&gt;

&lt;p&gt;As with last year, I&#39;ve continued to get a number of invites to appear as a guest on various podcasts.&lt;/p&gt;

&lt;p&gt;I&#39;ll link them chronologically.&lt;/p&gt;

&lt;h3 id=&#34;march-react-roundup&#34;&gt;March: React Roundup&lt;/h3&gt;

&lt;p&gt;First off for this year was the React Roundup podcast. We covered most of the usual topics like &amp;quot;Redux isn&#39;t dead&amp;quot; and &amp;quot;RTK is useful&amp;quot;, as well as how I got into open source and my goal of creating a &amp;quot;React community tools and practices&amp;quot; site to help provide ecosystem guidance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://devchat.tv/react-round-up/rru-135-redux-redux-toolkit-oss-and-more-with-mark-erikson/&#34;&gt;&lt;strong&gt;React Roundup 135: Redux, Redux Toolkit, OSS and More with Mark Erikson&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;march-fullstack-jam&#34;&gt;March: FullStack Jam&lt;/h3&gt;

&lt;p&gt;This discussion covered many of the usual topics: Redux&#39;s usage and current status, how Redux Toolkit makes using Redux easier, and how RTK Query simplifies data fetching:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://fsjam.org/40&#34;&gt;&lt;strong&gt;FSJam 40: Redux Toolkit with Mark Erikson&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;march-state-of-the-react-ecosystem&#34;&gt;March: State of the React Ecosystem&lt;/h3&gt;

&lt;p&gt;A panel vidcast where several React ecosystem library maintainers were able to talk about what&#39;s new and upcoming with their tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=n8N6mQD-jrA&#34;&gt;&lt;strong&gt;State of the React Ecosystem (March 2021)&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;may-podrocket&#34;&gt;May: PodRocket&lt;/h3&gt;

&lt;p&gt;I appeared on LogRocket&#39;s podcast, and we had some great discussions about the value of Redux, what&#39;s in RTK, tradeoffs of using Immer, when it makes sense to use Redux, why people keep claiming Redux is dead, and how people should approach writing new tutorials for tools that already have docs and an ecosystem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://podrocket.logrocket.com/redux&#34;&gt;&lt;strong&gt;PodRocket: Redux is alive and well, with Mark Erikson&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;december-scrimba-podcast&#34;&gt;December: Scrimba Podcast&lt;/h3&gt;

&lt;p&gt;A nice discussion about career-related topics, including thoughts on searching for info online and dealing with programming errors.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://scrimba.com/podcast/career-advice-from-the-creator-of-redux-mark-erikareer/&#34;&gt;&lt;strong&gt;Career advice from the maintainer of Redux, Mark Erikson&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    
    <item>
      <title>Blogged Answers: Why React Context is Not a &#34;State Management&#34; Tool (and Why It Doesn&#39;t Replace Redux)</title>
      <link>https://blog.isquaredsoftware.com/2021/01/context-redux-differences/</link>
      <pubDate>Mon, 18 Jan 2021 10:00:00 -0500</pubDate>
      
      <guid>https://blog.isquaredsoftware.com/2021/01/context-redux-differences/</guid>
      <description>&lt;p&gt;&lt;/p&gt;

&lt;h2 id=&#34;introduction&#34;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;&amp;quot;Context vs Redux&amp;quot; has been one of the most widely debated topics within the React community ever since the current React Context API was released. Sadly, &lt;strong&gt;most of this &amp;quot;debate&amp;quot; stems from confusion over the purpose and use cases for these two tools&lt;/strong&gt;. I&#39;ve answered various questions about Context and Redux hundreds of times across the internet (including my posts &lt;a href=&#34;https://blog.isquaredsoftware.com/2018/03/redux-not-dead-yet/&#34;&gt;Redux - Not Dead Yet!&lt;/a&gt;, &lt;a href=&#34;https://blog.isquaredsoftware.com/2020/01/blogged-answers-react-redux-and-context-behavior/&#34;&gt;React, Redux, and Context Behavior&lt;/a&gt;, &lt;a href=&#34;https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-mostly-complete-guide-to-react-rendering-behavior/&#34;&gt;A (Mostly) Complete Guide to React Rendering Behavior&lt;/a&gt;, and &lt;a href=&#34;https://changelog.com/posts/when-and-when-not-to-reach-for-redux&#34;&gt;When (and when not) to Reach for Redux&lt;/a&gt;), yet the confusion continues to get worse.&lt;/p&gt;

&lt;p&gt;Given the prevalence of questions on this topic, I&#39;m putting together this post as a definitive answer to those questions.  I&#39;ll try to clarify &lt;strong&gt;what Context and Redux actually are, how they&#39;re meant to be used, how they&#39;re different, and when you should use them&lt;/strong&gt;.&lt;/p&gt;

&lt;h3 id=&#34;tl-dr&#34;&gt;TL;DR&lt;/h3&gt;

&lt;!-- omit in toc --&gt;

&lt;h4 id=&#34;are-context-and-redux-the-same-thing&#34;&gt;Are Context and Redux the same thing?&lt;/h4&gt;

&lt;p&gt;No.  They are different tools that do different things, and you use them for different purposes.&lt;/p&gt;

&lt;!-- omit in toc --&gt;

&lt;h4 id=&#34;is-context-a-state-management-tool&#34;&gt;Is Context a &amp;quot;state management&amp;quot; tool?&lt;/h4&gt;

&lt;p&gt;No. Context is a form of Dependency Injection.  It is a &lt;em&gt;transport&lt;/em&gt; mechanism - it doesn&#39;t &amp;quot;manage&amp;quot; anything.  Any &amp;quot;state management&amp;quot; is done by you and your own code, typically via &lt;code&gt;useState/useReducer&lt;/code&gt;.&lt;/p&gt;

&lt;!-- omit in toc --&gt;

&lt;h4 id=&#34;are-context-and-usereducer-a-replacement-for-redux&#34;&gt;Are Context and &lt;code&gt;useReducer&lt;/code&gt; a replacement for Redux?&lt;/h4&gt;

&lt;p&gt;No. They have some similarities and overlap, but there are major differences in their capabilities.&lt;/p&gt;

&lt;!-- omit in toc --&gt;

&lt;h4 id=&#34;when-should-i-use-context&#34;&gt;When should I use Context?&lt;/h4&gt;

&lt;p&gt;Any time you have some value that you want to make accessible to a portion of your React component tree, without passing that value down as props through each level of components.&lt;/p&gt;

&lt;!-- omit in toc --&gt;

&lt;h4 id=&#34;when-should-i-use-context-and-usereducer&#34;&gt;When should I use Context and &lt;code&gt;useReducer&lt;/code&gt;?&lt;/h4&gt;

&lt;p&gt;When you have moderately complex React component state management needs within a specific section of your application.&lt;/p&gt;

&lt;!-- omit in toc --&gt;

&lt;h4 id=&#34;when-should-i-use-redux-instead&#34;&gt;When should I use Redux instead?&lt;/h4&gt;

&lt;p&gt;Redux is most useful in cases when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have larger amounts of application state that are needed in many places in the app&lt;/li&gt;
&lt;li&gt;The app state is updated frequently over time&lt;/li&gt;
&lt;li&gt;The logic to update that state may be complex&lt;/li&gt;
&lt;li&gt;The app has a medium or large-sized codebase, and might be worked on by many people&lt;/li&gt;
&lt;li&gt;You want to be able to understand when, why, and how the state in your application has updated, and visualize the changes to your state over time&lt;/li&gt;
&lt;li&gt;You need more powerful capabilities for managing side effects, persistence, and data serialization&lt;/li&gt;
&lt;/ul&gt;

&lt;!-- omit in toc --&gt;

&lt;h3 id=&#34;table-of-contents&#34;&gt;Table of Contents&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#introduction&#34;&gt;Introduction&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#tldr&#34;&gt;TL;DR&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#understanding-context-and-redux&#34;&gt;Understanding Context and Redux&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#what-is-react-context&#34;&gt;What is React Context?&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#using-context&#34;&gt;Using Context&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#purpose-and-use-cases-for-context&#34;&gt;Purpose and Use Cases for Context&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#what-is-redux&#34;&gt;What is Redux?&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#redux-and-react&#34;&gt;Redux and React&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#purposes-and-use-cases-for-react-redux&#34;&gt;Purposes and Use Cases for (React-)Redux&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#why-context-is-not-state-management&#34;&gt;Why Context is Not &amp;quot;State Management&amp;quot;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#comparing-context-and-redux&#34;&gt;Comparing Context and Redux&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#context-and-usereducer&#34;&gt;Context and &lt;code&gt;useReducer&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#choosing-the-right-tool&#34;&gt;Choosing the Right Tool&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#use-case-summary&#34;&gt;Use Case Summary&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#recommendations&#34;&gt;Recommendations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#final-thoughts&#34;&gt;Final Thoughts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#further-information&#34;&gt;Further Information&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&#34;understanding-context-and-redux&#34;&gt;Understanding Context and Redux&lt;/h2&gt;

&lt;p&gt;In order to use &lt;em&gt;any&lt;/em&gt; tool correctly, it&#39;s critical to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What its purpose is&lt;/li&gt;
&lt;li&gt;What problems it&#39;s trying to solve&lt;/li&gt;
&lt;li&gt;When and why it was originally created&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It&#39;s also critical to understand what problems you are trying to solve in your own application right now, and pick the tools that solve your problem the best - not because someone else said you should use them, not because they’re popular, but because this is what works best for you in this particular situation.&lt;/p&gt;

&lt;p&gt;Most of the confusion over &amp;quot;Context vs Redux&amp;quot; stems from a lack of understanding about what these tools actually &lt;em&gt;do&lt;/em&gt;, and what problems they solve.  So, in order to actually know when to use them, we need to first clearly define what they do and what problems they solve.&lt;/p&gt;

&lt;h3 id=&#34;what-is-react-context&#34;&gt;What is React Context?&lt;/h3&gt;

&lt;p&gt;Let&#39;s start by looking at &lt;a href=&#34;https://reactjs.org/docs/context.html&#34;&gt;the actual description of Context from the React docs&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Context provides a way to pass data through the component tree without having to pass props down manually at every level&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In a typical React application, data is passed top-down (parent to child) via props, but this can be cumbersome for certain types of props (e.g. locale preference, UI theme) that are required by many components within an application. &lt;strong&gt;Context provides a way to share values like these between components without having to explicitly pass a prop through every level of the tree&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Notice that &lt;strong&gt;it does not say anything about &amp;quot;managing&amp;quot; values - it only refers to &amp;quot;passing&amp;quot; and &amp;quot;sharing&amp;quot; values&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://reactjs.org/blog/2018/03/29/react-v-16-3.html&#34;&gt;The current React Context API (&lt;code&gt;React.createContext()&lt;/code&gt;) was first released in React 16.3&lt;/a&gt;.  It replaced &lt;a href=&#34;https://reactjs.org/docs/legacy-context.html&#34;&gt;the legacy context API&lt;/a&gt;, which had been available since early versions of React, but had major design flaws.  The primary problem with legacy context was that updates to values passed down via context could be &amp;quot;blocked&amp;quot; if a component skipped rendering via &lt;code&gt;shouldComponentUpdate&lt;/code&gt;.  Since many components relied on &lt;code&gt;shouldComponentUpdate&lt;/code&gt; for performance optimizations, that made legacy context useless for passing down plain data.  &lt;code&gt;createContext()&lt;/code&gt; was designed to solve that problem, so that any update to a value &lt;em&gt;will&lt;/em&gt; be seen in child components even if a component in the middle skips rendering.&lt;/p&gt;

&lt;h4 id=&#34;using-context&#34;&gt;Using Context&lt;/h4&gt;

&lt;p&gt;Using React Context in an app requires a few steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, call &lt;code&gt;const MyContext = React.createContext()&lt;/code&gt; to create a context object instance&lt;/li&gt;
&lt;li&gt;In a parent component, render &lt;code&gt;&amp;lt;MyContext.Provider value={someValue}&amp;gt;&lt;/code&gt;.  This puts some single piece of data into the context.  That value could be anything - a string, a number, an object, an array, a class instance, an event emitter, and so on.&lt;/li&gt;
&lt;li&gt;Then, in any component nested inside that provider, call &lt;code&gt;const theContextValue = useContext(MyContext)&lt;/code&gt;.&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whenever the parent component re-renders and passes in a new reference to the context provider as the &lt;code&gt;value&lt;/code&gt;, &lt;a href=&#34;https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-mostly-complete-guide-to-react-rendering-behavior/#context-and-rendering-behavior&#34;&gt;any component that reads from that context will be forced to re-render&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Most commonly, the &lt;code&gt;value&lt;/code&gt; for a context is something that comes from React component state, along these lines:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-jsx&#34;&gt;function ParentComponent() {
  const [counter, setCounter] = useState(0);

  // Create an object containing both the value and the setter
  const contextValue = {counter, setCounter};

  return (
    &amp;lt;MyContext.Provider value={contextValue}&amp;gt;
      &amp;lt;SomeChildComponent /&amp;gt;
    &amp;lt;/MyContext.Provider&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A child component then can call &lt;code&gt;useContext&lt;/code&gt; and read the value:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-jsx&#34;&gt;function NestedChildComponent() {
  const { counter, setCounter } = useContext(MyContext);

  // do something with the counter value and setter
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&#34;purpose-and-use-cases-for-context&#34;&gt;Purpose and Use Cases for Context&lt;/h4&gt;

&lt;p&gt;Based on that, we can see that &lt;strong&gt;Context doesn&#39;t actually &amp;quot;manage&amp;quot; anything at all. Instead, it&#39;s like a pipe or a wormhole&lt;/strong&gt;.  You put something in the top end of the pipe using the &lt;code&gt;&amp;lt;MyContext.Provider&amp;gt;&lt;/code&gt;, and that one thing (whatever it is) goes down through the pipe until it pops out the other end where another component asks for it with &lt;code&gt;useContext(MyProvider)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So, &lt;strong&gt;the primary purpose for using Context is to avoid &amp;quot;prop-drilling&amp;quot;&lt;/strong&gt;.  Rather than pass this value down as a prop, explicitly, through &lt;em&gt;every&lt;/em&gt; level of the component tree that needs it, any component that&#39;s nested inside the &lt;code&gt;&amp;lt;MyContext.Provider&amp;gt;&lt;/code&gt; can just say &lt;code&gt;useContext(MyContext)&lt;/code&gt; to grab the value as needed.  This does simplify the code, because we don&#39;t have to write all the extra prop-passing logic.&lt;/p&gt;

&lt;p&gt;Conceptually, &lt;strong&gt;this is a form of &lt;a href=&#34;https://en.wikipedia.org/wiki/Dependency_injection&#34;&gt;&amp;quot;Dependency Injection&amp;quot;&lt;/a&gt;&lt;/strong&gt;.  We know that the child component needs a value of a certain type, but it doesn&#39;t try to create or set up that value itself.  Instead, it assumes that some parent component will pass down that value, at runtime.&lt;/p&gt;

&lt;h3 id=&#34;what-is-redux&#34;&gt;What is Redux?&lt;/h3&gt;

&lt;p&gt;For comparison, let&#39;s look at the description from &lt;a href=&#34;https://redux.js.org/tutorials/essentials/part-1-overview-concepts&#34;&gt;the &amp;quot;Redux Essentials&amp;quot; tutorial in the Redux docs&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Redux is a pattern and library for managing and updating application state, using events called &amp;quot;actions&amp;quot;.&lt;/strong&gt; It serves as a centralized store for state that needs to be used across your entire application, with rules ensuring that the state can only be updated in a predictable fashion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Redux helps you manage &amp;quot;global&amp;quot; state&lt;/strong&gt; - state that is needed across many parts of your application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The patterns and tools provided by Redux make it easier to understand when, where, why, and how the state in your application is being updated, and how your application logic will behave when those changes occur.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Note that this description:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;specifically refers to &amp;quot;managing state&amp;quot;&lt;/li&gt;
&lt;li&gt;says that the purpose of Redux is to help you understand how state changes over time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Historically, &lt;a href=&#34;https://blog.isquaredsoftware.com/2017/05/idiomatic-redux-tao-of-redux-part-1/#redux-was-built-as-a-flux-architecture-implementation&#34;&gt;Redux was originally created as an implementation of the &amp;quot;Flux Architecture&amp;quot;&lt;/a&gt;, which was a pattern first suggested by Facebook in 2014, a year after React came out.  Following that announcement, the community created dozens of Flux-inspired libraries with varying approaches to the Flux concepts.  Redux came out in 2015, and quickly won the &amp;quot;Flux Wars&amp;quot; because it had the best design, matched the problems people were trying to solve, and worked great with React.&lt;/p&gt;

&lt;p&gt;Architecturally, Redux emphasizes using functional programming principles to help you write as much of your code as possible as predictable &amp;quot;reducer&amp;quot; functions, and separating the idea of &amp;quot;what event happened&amp;quot; from the logic that determines &amp;quot;how the state updates when that event happens&amp;quot;.  Redux also uses &lt;a href=&#34;https://redux.js.org/tutorials/fundamentals/part-6-async-logic#redux-middleware-and-side-effects&#34;&gt;middleware as a way to extend the capabilities of the Redux store, including handling side effects&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Redux also has &lt;a href=&#34;https://redux.js.org/tutorials/fundamentals/part-4-store#redux-devtools&#34;&gt;the Redux Devtools, which allow you to see the history of actions and state changes in your app over time&lt;/a&gt;.&lt;/p&gt;

&lt;h4 id=&#34;redux-and-react&#34;&gt;Redux and React&lt;/h4&gt;

&lt;p&gt;Redux itself is UI-agnostic - you can use it with any UI layer (React, Vue, Angular, vanilla JS, etc), or without any UI at all.&lt;/p&gt;

&lt;p&gt;That said, Redux is most commonly used with React.  &lt;a href=&#34;https://react-redux.js.org&#34;&gt;The React-Redux library&lt;/a&gt; is the official UI binding layer that lets React components interact with a Redux store by reading values from Redux state and dispatching actions.  So, when most people refer to &amp;quot;Redux&amp;quot;, they actually mean &amp;quot;using a Redux store and the React-Redux library together&amp;quot;.&lt;/p&gt;

&lt;p&gt;React-Redux allows any React component in the application to talk to the Redux store.  This is only possible because &lt;strong&gt;React-Redux uses Context internally&lt;/strong&gt;. However, it&#39;s critical to note that &lt;a href=&#34;https://blog.isquaredsoftware.com/2020/01/blogged-answers-react-redux-and-context-behavior/&#34;&gt;&lt;strong&gt;React-Redux only passes down the &lt;em&gt;Redux store instance&lt;/em&gt; via context, not the current &lt;em&gt;state value&lt;/em&gt;!&lt;/strong&gt;&lt;/a&gt;.  This is actually an example of using Context for dependency injection, as mentioned above.  We know that our Redux-connected React components need to talk to &lt;em&gt;a&lt;/em&gt; Redux store, but we don&#39;t know or care &lt;em&gt;which&lt;/em&gt; Redux store that is when we define the component.  The actual Redux store is injected into the tree at runtime using the React-Redux &lt;code&gt;&amp;lt;Provider&amp;gt;&lt;/code&gt; component.&lt;/p&gt;

&lt;p&gt;Because of this, React-Redux can &lt;em&gt;also&lt;/em&gt; be used to avoid prop-drilling, specifically because React-Redux uses Context internally.  Instead of explicitly putting a new value into a &lt;code&gt;&amp;lt;MyContext.Provider&amp;gt;&lt;/code&gt; yourself, you can put that data into the Redux store and then access it anywhere.&lt;/p&gt;

&lt;h4 id=&#34;purposes-and-use-cases-for-react-redux&#34;&gt;Purposes and Use Cases for (React-)Redux&lt;/h4&gt;

&lt;p&gt;The primary reason to use Redux is captured in the description from the Redux docs:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The patterns and tools provided by Redux make it easier to understand when, where, why, and how the state in your application is being updated, and how your application logic will behave when those changes occur.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are additional reasons why you might want to use Redux.  &amp;quot;Avoiding prop-drilling&amp;quot; &lt;em&gt;is&lt;/em&gt; one of those other reasons.  Many people chose Redux early on specifically to let them avoid prop-drilling, because  React&#39;s legacy context was broken and React-Redux worked correctly.&lt;/p&gt;

&lt;p&gt;Other valid reasons to use Redux include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wanting to write your state management logic completely separate from the UI layer&lt;/li&gt;
&lt;li&gt;Sharing state management logic between different UI layers (such as an application that is being migrated from AngularJS to React)&lt;/li&gt;
&lt;li&gt;Using the power of Redux middleware to add additional logic when actions are dispatched&lt;/li&gt;
&lt;li&gt;Being able to persist portions of the Redux state&lt;/li&gt;
&lt;li&gt;Enabling bug reports that can be replayed by developers&lt;/li&gt;
&lt;li&gt;Faster debugging of logic and UI while in development&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dan Abramov listed a number of these use cases when he wrote his post &lt;a href=&#34;https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367&#34;&gt;You Might Not Need Redux&lt;/a&gt;, all the way back in 2016.&lt;/p&gt;

&lt;h2 id=&#34;why-context-is-not-state-management&#34;&gt;Why Context is Not &amp;quot;State Management&amp;quot;&lt;/h2&gt;

&lt;p&gt;&lt;a href=&#34;https://daveceddia.com/visual-guide-to-state-in-react/&#34;&gt;&amp;quot;State&amp;quot; is any data that describes the behavior of an application&lt;/a&gt;.  We could divide that into &lt;a href=&#34;http://jamesknelson.com/5-types-react-application-state/&#34;&gt;categories like &amp;quot;server state&amp;quot;, &amp;quot;communications state&amp;quot;, and &amp;quot;location state&amp;quot;&lt;/a&gt; if we want to, but the key point is that there is data being stored, read, updated, and used.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://twitter.com/DavidKPiano/status/1347723896800346112&#34;&gt;David Khourshid, author of the XState library and an expert on state machines, said&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&amp;quot;State management is how state changes over time.&amp;quot;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Based on that, we can say that &amp;quot;state management&amp;quot; means having ways to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;store&lt;/em&gt; an initial value&lt;/li&gt;
&lt;li&gt;&lt;em&gt;read&lt;/em&gt; the current value&lt;/li&gt;
&lt;li&gt;&lt;em&gt;update&lt;/em&gt; a value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There&#39;s also typically a way to be notified when the current value has changed.&lt;/p&gt;

&lt;p&gt;React&#39;s &lt;code&gt;useState&lt;/code&gt; and &lt;code&gt;useReducer&lt;/code&gt; hooks are good example of state management.  With both of those hooks, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;store an initial value by calling the hook&lt;/li&gt;
&lt;li&gt;read the current value, also by calling the hook&lt;/li&gt;
&lt;li&gt;update the value by calling the supplied &lt;code&gt;setState&lt;/code&gt; or &lt;code&gt;dispatch&lt;/code&gt; function&lt;/li&gt;
&lt;li&gt;Know that the value has been updated because the component re-rendered&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Similarly, Redux and MobX are clearly state management as well:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Redux stores an initial value by calling the root reducer, lets you read the current value with &lt;code&gt;store.getState()&lt;/code&gt;, updates the value with &lt;code&gt;store.dispatch(action)&lt;/code&gt;, and notifies listeners that the store updated via &lt;code&gt;store.subscribe(listener)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;MobX stores an initial value by assigning field values in a store class, lets you read the current value by accessing the store&#39;s fields, updates values by assigning to those fields, and notifies that changes happened via &lt;code&gt;autorun()&lt;/code&gt; and &lt;code&gt;computed()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can even say that server caching tools like React-Query, SWR, Apollo, and Urql fit the definition of &amp;quot;state management&amp;quot; - they store initial values based on the fetched data, return the current value via their hooks, allow updates via &amp;quot;server mutations&amp;quot;, and notify of changes via re-rendering the component.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;React Context does not meet those criteria. Therefore, Context is &lt;em&gt;not&lt;/em&gt; a &amp;quot;state management&amp;quot; tool!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As we established earlier, Context does not &amp;quot;store&amp;quot; anything itself.  The parent component that renders a &lt;code&gt;&amp;lt;MyContext.Provider&amp;gt;&lt;/code&gt; is responsible for deciding what value is passed into the context, and that value typically is based on React component state.  The actual &amp;quot;state management&amp;quot; is happening with the &lt;code&gt;useState/useReducer&lt;/code&gt; hook.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://twitter.com/DavidKPiano/status/1347723896800346112&#34;&gt;As David Khourshid also said&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Context is how state (that exists somewhere already) is shared with other components.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Context has little to do with state &lt;em&gt;management&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or, &lt;a href=&#34;https://twitter.com/CONNERtheBUSH/status/1347736489875140608&#34;&gt;as a recent tweet put it&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I guess Context is more like hidden props than abstracted state.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Think of it this way. We could have written the exact same &lt;code&gt;useState/useReducer&lt;/code&gt; code, but prop-drilled the data and the update function down through the component tree.  The actual behavior of the app would have been the same overall.  All Context does for us is let us skip the prop-drilling.&lt;/p&gt;

&lt;h2 id=&#34;comparing-context-and-redux&#34;&gt;Comparing Context and Redux&lt;/h2&gt;

&lt;p&gt;Let&#39;s review what capabilities Context and React+Redux actually have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Context&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Does not store or &amp;quot;manage&amp;quot; anything&lt;/li&gt;
&lt;li&gt;Only works in React components&lt;/li&gt;
&lt;li&gt;Passes down a single value, which could be anything (primitive, objects, classes, etc)&lt;/li&gt;
&lt;li&gt;Allows reading that single value&lt;/li&gt;
&lt;li&gt;Can be used to avoid prop-drilling&lt;/li&gt;
&lt;li&gt;Does show the current context value for both &lt;code&gt;Provider&lt;/code&gt; and &lt;code&gt;Consumer&lt;/code&gt; components in the React DevTools, but does not show any history of how that value changed over time&lt;/li&gt;
&lt;li&gt;Updates consuming components when the context value changes, but with no way to skip updates&lt;/li&gt;
&lt;li&gt;Does not include any mechanism for side effects - it&#39;s purely for rendering components&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;React+Redux&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Stores and manages a single value (which is typically an object)&lt;/li&gt;
&lt;li&gt;Works with any UI, including outside of React components&lt;/li&gt;
&lt;li&gt;Allows reading that single value&lt;/li&gt;
&lt;li&gt;Can be used to avoid prop-drilling&lt;/li&gt;
&lt;li&gt;Can update the value via dispatching an action and running reducers&lt;/li&gt;
&lt;li&gt;Has DevTools that show the history of all dispatched actions and state changes over time&lt;/li&gt;
&lt;li&gt;Uses middleware to allow app code to trigger side effects&lt;/li&gt;
&lt;li&gt;Allows components to subscribe to store updates, extract specific pieces of the store state, and only re-render when those values change&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, clearly these are very different tools with different capabilities.  The only overlap between them, really, is &amp;quot;can be used to avoid prop-drilling&amp;quot;.&lt;/p&gt;

&lt;h3 id=&#34;context-and-usereducer&#34;&gt;Context and &lt;code&gt;useReducer&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;One problem with the &amp;quot;Context vs Redux&amp;quot; discussions is that people often actually mean &amp;quot;I&#39;m using &lt;code&gt;useReducer&lt;/code&gt; to manage my state, and Context to pass down that value&amp;quot;.  But, they never state that explicitly - they just say &amp;quot;I&#39;m using Context&amp;quot;.  That&#39;s a common cause of the confusion I see, and it&#39;s really unfortunate because it helps perpetuate the idea that Context &amp;quot;manages state&amp;quot;.&lt;/p&gt;

&lt;p&gt;So, let&#39;s talk about the Context + &lt;code&gt;useReducer&lt;/code&gt; combination specifically.  Yes, Context + &lt;code&gt;useReducer&lt;/code&gt; &lt;em&gt;does&lt;/em&gt; look an awful lot like Redux + React-Redux.  They both have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A stored value&lt;/li&gt;
&lt;li&gt;A reducer function&lt;/li&gt;
&lt;li&gt;dispatching of actions&lt;/li&gt;
&lt;li&gt;a way to pass down that value and read it in nested components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, there&#39;s still a number of very significant differences in the capabilities and behaviors of Context + &lt;code&gt;useReducer&lt;/code&gt; vs those of Redux + React-Redux.  I covered the key points in my posts &lt;a href=&#34;https://blog.isquaredsoftware.com/2020/01/blogged-answers-react-redux-and-context-behavior/&#34;&gt;React, Redux, and Context Behavior&lt;/a&gt; and &lt;a href=&#34;https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-mostly-complete-guide-to-react-rendering-behavior/&#34;&gt;A (Mostly) Complete Guide to React Rendering Behavior&lt;/a&gt;.  Summarizing here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Context + &lt;code&gt;useReducer&lt;/code&gt; relies on passing &lt;em&gt;the current state value&lt;/em&gt; via Context.  React-Redux passes &lt;em&gt;the current Redux store instance&lt;/em&gt; via Context.&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;That means that when &lt;code&gt;useReducer&lt;/code&gt; produces a new state value, &lt;em&gt;all&lt;/em&gt; components that are subscribed to that context will be forced to re-render, even if they only care about &lt;em&gt;part&lt;/em&gt; of the data.  This &lt;em&gt;may&lt;/em&gt; lead to performances issues, depending on the size of the state value, how many components are subscribed to that data, and how often they re-render.  With React-Redux, components can subscribe to specific pieces of the store state, and only re-render when those values change.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In addition, there&#39;s some other important differences as well:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Context + &lt;code&gt;useReducer&lt;/code&gt; are React features, and therefore cannot be used outside of React.  A Redux store is independent of any UI, and so it can be used separate from React.&lt;/li&gt;
&lt;li&gt;The React DevTools allow viewing the &lt;em&gt;current&lt;/em&gt; context value, but not any of the historical values or changes over time.  The Redux DevTools allow seeing all actions that were dispatched, the contents of each action, the state as it existed after each action was processed, and the diffs between each state over time.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;useReducer&lt;/code&gt; does not have middleware.  You can do some side-effect-y things with &lt;code&gt;useEffect&lt;/code&gt; in combination with &lt;code&gt;useReducer&lt;/code&gt;, and I&#39;ve even seen some attempts to wrap &lt;code&gt;useReducer&lt;/code&gt; with &lt;em&gt;something&lt;/em&gt; that resembles a middleware, but both of those are severely limited in comparison to the functionality and capabilities of Redux middleware.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It&#39;s worth repeating &lt;a href=&#34;https://github.com/facebook/react/issues/14110#issuecomment-448074060&#34;&gt;what Sebastian Markbage (React core team architect) said about the uses for Context&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;My personal summary is that new context is ready to be used for low frequency unlikely updates (like locale/theme). It&#39;s also good to use it in the same way as old context was used. I.e. for static values and then propagate updates through subscriptions. It&#39;s not ready to be used as a replacement for all Flux-like state propagation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There&#39;s a lot of posts out there that recommend setting up multiple separate contexts for different chunks of state, both to cut down on unnecessary re-renders and to scope concerns.  Some of those also suggest &lt;a href=&#34;https://saul-mirone.github.io/performance-optimization-in-react-context/&#34;&gt;adding your own &amp;quot;context selector components&amp;quot;&lt;/a&gt;, which require a mixture of &lt;code&gt;React.memo()&lt;/code&gt;, &lt;code&gt;useMemo()&lt;/code&gt;, and &lt;a href=&#34;https://kentcdodds.com/blog/how-to-use-react-context-effectively&#34;&gt;carefully splitting things up so there&#39;s two separate contexts for each segment of state&lt;/a&gt; (one for the data, and one for the updater functions).  Sure, it&#39;s possible to write code that way, but &lt;strong&gt;at that point you&#39;re just reinventing React-Redux, poorly&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So, &lt;strong&gt;even though Context + &lt;code&gt;useReducer&lt;/code&gt; sorta-resemble Redux + React-Redux at a quick glance... they are &lt;em&gt;not&lt;/em&gt; fully equivalent and &lt;em&gt;cannot&lt;/em&gt; truly replace Redux!&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&#34;choosing-the-right-tool&#34;&gt;Choosing the Right Tool&lt;/h2&gt;

&lt;p&gt;As I said earlier, it&#39;s critical to understand what problems a tool solves, and know what problems you &lt;em&gt;have&lt;/em&gt;, in order to correctly choose the right tool to solve your problems.&lt;/p&gt;

&lt;h3 id=&#34;use-case-summary&#34;&gt;Use Case Summary&lt;/h3&gt;

&lt;p&gt;Let&#39;s recap the use cases for each of these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Context&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Passing down a value to nested components without prop-drilling&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;useReducer&lt;/code&gt;&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Moderately complex React component state management using a reducer function&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Context + &lt;code&gt;useReducer&lt;/code&gt;&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Moderately complex React component state management using a reducer function, &lt;em&gt;and&lt;/em&gt; passing that state value down to nested components without prop-drilling&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Redux&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Moderate to highly complex state management using reducer functions&lt;/li&gt;
&lt;li&gt;Traceability for when, why, and how state changed over time&lt;/li&gt;
&lt;li&gt;Wanting to write your state management logic completely separate from the UI layer&lt;/li&gt;
&lt;li&gt;Sharing state management logic between different UI layers&lt;/li&gt;
&lt;li&gt;Using the power of Redux middleware to add additional logic when actions are dispatched&lt;/li&gt;
&lt;li&gt;Being able to persist portions of the Redux state&lt;/li&gt;
&lt;li&gt;Enabling bug reports that can be replayed by developers&lt;/li&gt;
&lt;li&gt;Faster debugging of logic and UI while in development&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Redux + React-Redux&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;All of the use cases for Redux, plus interacting with the Redux store in your React components&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Again, &lt;strong&gt;these are different tools that solve different problems!&lt;/strong&gt;&lt;/p&gt;

&lt;h3 id=&#34;recommendations&#34;&gt;Recommendations&lt;/h3&gt;

&lt;p&gt;So, how do you decide whether to use Context, Context + &lt;code&gt;useReducer&lt;/code&gt;, or Redux + React-Redux?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You need to determine which of these tools best matches the set of problems that you&#39;re trying to solve!&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the only thing you need to do is avoid prop-drilling, then use Context&lt;/li&gt;
&lt;li&gt;If you&#39;ve got some moderately complex React component state, or just really don&#39;t want to use an external library, go with Context + &lt;code&gt;useReducer&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;If you want better traceability of the changes to your state over time, need to ensure that only specific components re-render when the state changes, need more powerful capabilities for managing side effects, or have other similar problems, use Redux + React-Redux&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My personal opinion is that &lt;strong&gt;if you get past 2-3 state-related contexts in an application, you&#39;re re-inventing a weaker version of React-Redux and should just switch to using Redux&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Another common concern is that &amp;quot;using Redux means too much &#39;boilerplate&#39;&amp;quot;.  Those complaints are very outdated, as &amp;quot;modern Redux&amp;quot; is significantly easier to learn and use than what you may have seen before.  &lt;a href=&#34;https://redux-toolkit.js.org&#34;&gt;Our official Redux Toolkit package eliminates those &amp;quot;boilerplate&amp;quot; concerns&lt;/a&gt;, and &lt;a href=&#34;https://redux.js.org/tutorials/fundamentals/part-5-ui-react#reading-state-from-the-store-with-useselector&#34;&gt;the React-Redux hooks API simplifies using Redux in your React components&lt;/a&gt;.  As &lt;a href=&#34;https://www.reddit.com/r/javascript/comments/kr0ce6/future_of_state_management_in_react_with_xstate/gi9af94/&#34;&gt;one user recently told me&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We just switched from context and hooks over to RTK on one of our production application&#39;s frontends. That thing processes a little over $1B/year. Fantastic stuff in the toolkit. The RTK is the polish that helped me convince the rest of the teams to buy into the refactor. I also did a boilerplate analysis for that refactor and &lt;strong&gt;it&#39;s actually LESS boilerplate to use the RTK than it is to use the recommended dispatch pattern in contexts&lt;/strong&gt;. Not to mention how much easier it is to process data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yes, adding RTK and React-Redux as dependencies does add additional byte size to your application bundle over just Context + &lt;code&gt;useReducer&lt;/code&gt;, because those are built in to React.  But, the tradeoffs are worth it - better state traceability, simpler and more predictable logic, and improved component rendering performance.&lt;/p&gt;

&lt;p&gt;It&#39;s also important to point out that &lt;strong&gt;these are not mutually exclusive options - you can use Redux, Context, and &lt;code&gt;useReducer&lt;/code&gt; together at the same time!&lt;/strong&gt;  We specifically encourage &lt;a href=&#34;https://redux.js.org/tutorials/essentials/part-2-app-structure#component-state-and-forms&#34;&gt;putting &amp;quot;global state&amp;quot; in Redux and &amp;quot;local state&amp;quot; in React components&lt;/a&gt;, and &lt;a href=&#34;https://redux.js.org/style-guide/style-guide#evaluate-where-each-piece-of-state-should-live&#34;&gt;carefully deciding whether each piece of state should live in Redux or component state&lt;/a&gt;.  So, you can use Redux for some state that&#39;s global, and &lt;code&gt;useReducer&lt;/code&gt; + Context for some state that&#39;s more local, and Context by itself for some semi-static values, all at the same time in the same application.&lt;/p&gt;

&lt;p&gt;To be clear, &lt;strong&gt;I&#39;m &lt;em&gt;not&lt;/em&gt; saying that all apps should use Redux, or that Redux is always a better choice!&lt;/strong&gt;  There&#39;s many nuances to this discussion.  I &lt;em&gt;am&lt;/em&gt; saying that &lt;strong&gt;Redux &lt;em&gt;is&lt;/em&gt; a valid choice, there are many reasons to choose Redux, and the tradeoffs for choosing Redux are a net win more often than many people think&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And finally, Context and Redux are not the only tools to think about.  There&#39;s many other tools out there that solve other aspects of state management in different ways. MobX is another widely used option that uses OOP and observables to automatically update data dependencies. Jotai, Recoil, and Zustand offer lighter-weight state update approaches.  Data fetching libraries like React Query, SWR, Apollo, and Urql all provide abstractions that simplify common patterns for working with cached server state (and &lt;a href=&#34;https://rtk-query-docs.netlify.app&#34;&gt;the upcoming &amp;quot;RTK Query&amp;quot;&lt;/a&gt; library will do the same for Redux Toolkit).  Again, these are different tools, with different purposes and use cases, and are worth evaluating based on your use case.&lt;/p&gt;

&lt;h2 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h2&gt;

&lt;p&gt;I realize that this post won&#39;t stop the seemingly never-ending debate over &amp;quot;Context vs Redux?!?!?!?!?&amp;quot;.  There&#39;s too many people out there, too many conflicting ideas, and too much miscommunication and misinformation.&lt;/p&gt;

&lt;p&gt;Having said that, I hope that this post has clarified what these tools actually &lt;em&gt;do&lt;/em&gt;, how they&#39;re different, and when you should actually consider using them.  (And maybe, just &lt;em&gt;maybe&lt;/em&gt;, some folks will read this article and not feel the need to post the same question that&#39;s been asked a million times already...)&lt;/p&gt;

&lt;h2 id=&#34;further-information&#34;&gt;Further Information&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Context Purpose and Design&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://reactjs.org/docs/context.html&#34;&gt;React docs: Context&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://reactjs.org/blog/2018/03/29/react-v-16-3.html&#34;&gt;React blog: v16.3 - new Context API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/facebook/react/issues/14110#issuecomment-448074060&#34;&gt;Sebastian Markbage: use cases for Context&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://twitter.com/DavidKPiano/status/1347723896800346112&#34;&gt;David Khourshid: Context is not &amp;quot;state management&amp;quot;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://spin.atomicobject.com/2020/04/08/react-contexts-dynamic-scope/&#34;&gt;React Contexts are Dynamic Scope&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Redux Purpose and Design&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&#34;https://blog.isquaredsoftware.com/2017/05/idiomatic-redux-tao-of-redux-part-1/&#34;&gt;The Tao of Redux, Part 1 - Implementation and Intent&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://redux.js.org/understanding/thinking-in-redux/motivation&#34;&gt;Redux docs: Understanding Redux - Motivation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://redux.js.org/tutorials/fundamentals/part-8-modern-redux&#34;&gt;Redux Fundamentals tutorial: Modern Redux with Redux Toolkit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Differences between Redux and Context&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&#34;https://blog.isquaredsoftware.com/2020/01/blogged-answers-react-redux-and-context-behavior/&#34;&gt;React, Redux, and Context Behavior&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&#34;https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-mostly-complete-guide-to-react-rendering-behavior/&#34;&gt;A (Mostly) Complete Guide to React Rendering Behavior&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;When should I use Redux?&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&#34;https://blog.isquaredsoftware.com/2018/03/redux-not-dead-yet/&#34;&gt;Redux - Not Dead Yet!&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&#34;https://changelog.com/posts/when-and-when-not-to-reach-for-redux&#34;&gt;When (and when not) to reach for Redux&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&#34;https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367&#34;&gt;You Might Not Need Redux&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://redux.js.org/faq/general#when-should-i-use-redux&#34;&gt;Redux FAQ: When should I use Redux?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Other Redux and Context Comparison Discussions&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.valentinog.com/blog/context/&#34;&gt;Valentino Gagliardi: React Context API is not a state management tool&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://daveceddia.com/context-api-vs-redux/&#34;&gt;Dave Ceddia: React Context API vs Redux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.simplethread.com/cant-replace-redux-with-hooks/&#34;&gt;Mike Green: You Might Not Need Redux (But You Can’t Replace It With Hooks)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://staleclosures.dev/from-redux-to-hooks-case-study/&#34;&gt;Sergey Ryzhov: From Redux to Hooks: A Case Study&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://medium.com/javascript-scene/do-react-hooks-replace-redux-210bab340672&#34;&gt;Eric Elliott: Do React Hooks Replace Redux?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://dev.to/chrisachard/can-you-replace-redux-with-react-hooks-20gm&#34;&gt;Chris Achard: Can You Replace Redux with React Hooks?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://medium.com/better-programming/redux-vs-context-vs-state-4202be6d3e54&#34;&gt;Denny Scott: Redux vs Context vs State - an in-depth look at state management in React&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://academind.com/tutorials/reactjs-redux-vs-context-api/&#34;&gt;Maximilian Schwarzmüller: Redux vs React&#39;s Context API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://blog.jakoblind.no/redux-context-props/&#34;&gt;Jakob Lind: When to use Redux, Context, and props&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
    </item>
    
  </channel>
</rss>