
Redux Toolkit with Mark Erikson
Mark Erikson discusses Redux Toolkit, RTK Query, TypeScript integration, and how modern Redux simplifies state management patterns.
Episode Description
Mark Erikson discusses Redux Toolkit, RTK Query, TypeScript integration, and how modern Redux simplifies state management patterns.
Episode Summary
Mark Erikson, primary Redux maintainer, joins the show to discuss the evolution of Redux and the creation of Redux Toolkit. He begins by addressing why Redux has a reputation for being difficult, arguing that much of the struggle stems from JavaScript fundamentals rather than Redux itself, compounded by boot camp timelines that rush learners through concepts. He then explains how Redux Toolkit was born from a deliberate effort to reduce the incidental complexity of Redux—eliminating boilerplate like hand-written action types, switch statements, and nested object spreads—by leveraging tools like Immer for safe immutable updates. The conversation shifts to when Redux is actually needed, with Erikson emphasizing predictable state management over prop drilling avoidance, while acknowledging that newer libraries like React Query have reduced some of Redux's traditional use cases. A significant portion covers RTK Query, a data fetching and caching solution built on Redux Toolkit that draws inspiration from React Query and Apollo but offers UI-agnostic functionality and full TypeScript inference. Erikson also shares pragmatic advice on TypeScript adoption, recommending an 80% coverage approach for applications while noting that library-level types must be far more complex. The episode closes with reflections on the challenges of maintaining backward compatibility and managing build systems across diverse environments.
Chapters
00:00:00 - Meet Mark Erikson and the Redux Journey
Mark Erikson introduces himself as a full-time developer and primary Redux maintainer, known for appearing wherever Redux is discussed online. He describes how Dan Abramov handed him the project in 2016, and how he gradually grew into the maintainer role through community engagement rather than by seeking the position out.
Anthony sets the stage for the episode's central theme by recounting how boot camps struggle with teaching Redux, raising the question of whether the difficulty is inherent to Redux or a product of surrounding conventions. This frames the conversation that follows about Redux Toolkit and its mission to simplify common patterns.
00:03:09 - Why Redux Feels Hard and JavaScript Fundamentals
Mark argues that many struggles attributed to React and Redux actually trace back to gaps in core JavaScript knowledge—concepts like nested object access, arrow functions, and the event loop. He connects this to the boot camp model, where compressed timelines push learners into frameworks before they've solidified fundamentals, making both React and Redux feel unnecessarily difficult.
He then breaks down the difference between Redux's inherent complexity, which comes from its layer of indirection between actions and reducers, and its incidental complexity, which arose from documentation patterns like separate folders for constants, action creators, and reducers. This distinction motivates why Redux Toolkit was created: to strip away the incidental boilerplate while preserving the core architecture.
00:06:33 - Redux Toolkit and Immer Under the Hood
Mark explains how Redux Toolkit emerged from a community discussion about simplifying Redux, offering APIs like configureStore for quick setup with sensible defaults and createSlice for auto-generating action creators and types. Central to this simplification is Immer, Michel Weststrate's library that uses JavaScript proxies to let developers write mutating syntax that gets safely converted into immutable updates behind the scenes.
The conversation addresses the teaching challenge this creates: developers might assume mutation is always safe in reducers when it only works inside Immer-wrapped functions. Mark describes his approach of placing prominent warnings throughout the new Redux tutorials, and the hosts discuss real-world confusion around JavaScript's mutable array methods like sort and reverse, reinforcing why immutability is such a persistent pain point.
00:12:31 - Immutability, Tutorials, and Teaching Redux
The discussion continues with how immutability is emphasized more in the React and Redux ecosystem than in other frameworks like Vue, Ember, or Angular. Mark describes the two-track tutorial approach he built for the Redux docs: the Essentials tutorial teaches Redux Toolkit as the default from the start, while the Fundamentals tutorial walks through hand-written patterns before concluding with Redux Toolkit as the recommended simplification.
Mark shares his frustration that thousands of outdated tutorials across platforms like freeCodeCamp and Codecademy still teach legacy Redux patterns, including the connect API. He acknowledges he can't update external content and can only encourage learners to consult the official documentation for current best practices.
00:16:05 - When Do You Actually Need Redux?
Mark reframes the case for Redux, de-emphasizing prop drilling avoidance in favor of predictable state updates—knowing when, where, why, and how state changed. He acknowledges the growing landscape of alternatives including MobX, XState, React Query, and Apollo, noting that Redux makes the most sense when an application has complex client-side state shared across many components and benefits from logic living outside React components.
Christopher asks whether Redux should be added at project start or introduced later. Mark shares his own experience deliberately holding off on adding a Redux store to a migration project, instead using createSlice with React's useReducer hook until the application's complexity genuinely warranted a full store. This practical example illustrates his pragmatic approach to tooling decisions.
00:21:12 - TypeScript and the 80% Principle
Mark walks through how each major Redux package handles TypeScript differently: Redux core was converted but remains unpublished, React Redux relies on community-maintained DefinitelyTyped definitions, and Redux Toolkit is fully written in TypeScript. He shares his personal conversion story from 2019 and advocates for a pragmatic 80% approach—type your functions, props, and API boundaries, but don't agonize over perfect coverage everywhere.
He contrasts application-level TypeScript, where pragmatism pays off, with library-level TypeScript, where complex type inference is necessary so that end users get simple, automatically inferred types. The hosts echo this with insights from previous guests, reinforcing that investing in type inference at the library level saves enormous effort at the application level.
00:29:16 - RTK Query and the Data Fetching Revolution
Mark explains the shift in the React ecosystem from pure state management toward data fetching and caching, describing how the traditional Redux pattern of writing thunks with loading, success, and failure actions for every API call became tedious. RTK Query was built by core maintainers Lenz Weber and Matt Zukowski as a Redux-based alternative to React Query and Apollo, letting developers define endpoints once and auto-generating reducers, middleware, and typed React hooks.
Key differentiators include RTK Query's UI-agnostic core that works beyond React, its centralized endpoint definition approach, and a proof-of-concept for generating entire service files from OpenAPI schemas. Mark discusses the plan to merge RTK Query into the main Redux Toolkit package as version 1.6, making the data fetching capabilities available as a built-in feature with proper tree shaking support.
00:38:30 - Library Maintenance, Compatibility, and Build Systems
Mark discusses the philosophical relationship between Redux Toolkit and Redux core, declaring that Redux Toolkit is now the standard way to write Redux logic. He emphasizes his commitment to backward compatibility, noting that Redux code from 2016 still works with the latest versions, and references Hyrum's Law to explain how even undocumented behaviors become dependencies that maintainers must respect.
He illustrates the complexity of modern library maintenance through the build system, describing the need for CommonJS, ES module, UMD, and modern builds across development and production environments, all while navigating Node's evolving ES module handling. The chapter closes with a discussion of esbuild's role in simplifying this process, and the hosts wrap up by thanking Mark for his extensive contributions to both the Redux codebase and the broader developer community.
Transcript
00:00:00 - Anthony Campolo
It's always kind of a ghost in the machine somewhere. Mark Erikson, welcome to the show.
00:00:15 - Mark Erikson
Hi. Glad to be here.
00:00:16 - Anthony Campolo
Why don't you let our listeners know who you are and what you work on?
00:00:20 - Mark Erikson
I'm Mark Erikson. I work full time at a large contracting company. Most people probably know me for my work in and around Redux. I'm one of the primary Redux maintainers, and I generally spend a lot of time answering questions about React and Redux anywhere there's a text box on the internet. I will generally pop up any time the word Redux gets mentioned anywhere and supply links and references. I'm also typically known for being that guy with The Simpsons avatar.
00:00:48 - Anthony Campolo
Yeah, you've told your story of how you got into the project on quite a few podcasts that we'll link to. What I really enjoy about your story is you're someone who was so helpful you were almost forced to take over because people are like, this guy knows all the answers, and I don't know if anyone else knows all the answers. So I guess we got to put this guy in charge, is kind of how it seemed like it went.
00:01:12 - Mark Erikson
That's pretty close. Yeah. Dan handed the keys over to myself and Tim Dorr in the summer of 2016, after he'd gotten busy with working on the React team at Facebook. I really didn't feel like I had the right to actually have an opinion on how the library worked and how development ought to go for quite a while, and it was actually because people were asking certain questions and offering certain paths that I finally had to have an opinion. It wasn't until after that had gone on for a while that I really considered myself to actually be a maintainer.
00:01:49 - Anthony Campolo
Yeah, it's going to be cool to tease apart what exactly the difference between Redux and this new kind of Redux Toolkit is. This is how you first got onto my radar. As I say, you've done a handful of podcasts about this, and the first time I heard you talk about what Redux Toolkit was, it really resonated with me because I was someone who went through a boot camp. Our boot camp, the Redux portion of the boot camp, is legendary. It is legendary for being legendarily hard, and everyone kind of has an opinion on why that is and whether it should be that way or needs to be that way. Whether it's inherent to Redux itself, whether functional programming is just more challenging than object-oriented programming. A lot of different theories about why this is. What you were talking about is part of what makes Redux challenging is simply the way that we use it and the conventions around it, because of how you would split up your files and have your logic kind of all over the place.
[00:02:54] The point of Redux Toolkit, as I understood it, was to create conventions to make Redux a little bit easier and a little bit nicer to use. So do you think that's kind of accurate? And then how would you describe what those conventions are?
00:03:09 - Mark Erikson
That's a good series of questions, and I have a whole bunch of answers that I will combine into one. First off, with even just the boot camp thing you were mentioning, I was actually tweeting about this a day or two ago. I've noticed that a lot of people's struggles with React and Redux, in general, aren't even React and Redux questions at all. It's really about JavaScript fundamentals. Even something as simple as, how do I access a nested field in a JavaScript object? Well, it's obj.a.b.c, but I've seen people struggle with that, much less things like arrow function syntax and object spreads or the event loop. So many of these questions really boil down to how much of a grasp you have on the fundamentals of JavaScript itself. Both React and Redux layer a lot of framework-specific concepts and concerns on top of that, but it's kind of like the classic, how much trouble are you going to have with algebra if you can't even add and subtract properly?
[00:04:14] I don't want to go off into the whole gatekeeping discussion, where you must spend five years learning the fundamentals of JavaScript before you can ever write your first line of React code or something like that. I think that's kind of silly and absurd, and it's also a whole separate argument that I don't want to get into. But, well and truly, so many people's struggles would either be resolved or at least lessened if they had a better grasp on the fundamentals of JavaScript. And so, with that boot camp type environment, that's actually kind of the big problem, because I don't know the exact timelines, but I get the sense that in most cases it's like, here's a month of JavaScript, here is a month of React, here's a month of Redux, and you're being thrown into these other tools when you really don't even have the foundation.
Purely just from that perspective, I can see learning either of those to be incredibly difficult. And for Redux itself, we typically advise that most folks shouldn't really try to get into it until they're already at least comfortable with React, partly just because that way there's fewer concepts to learn at once, but also because by that time it's more clear what React does and how it works.
[00:05:23] Then it's more clear how Redux can fit into that, and what problems Redux might help you solve, especially in that boot camp type environment. Being thrown into just React and Redux right away is going to be difficult for a lot of people. You pointed to some of the common concerns people have with learning Redux in general. Historically, there has been a lot of complexity around the standard patterns that we even showed in our docs. Frankly, a lot of those patterns existed because we showed them in our docs. Things like having your separate folders for action creators and constants and reducers, writing out string action types like const addTodo equals quote ADD_TODO in upper screaming snake case, having switch statements and nested object spreads and whatnot.
On the one hand, not all of those were strictly required to use Redux. Redux itself doesn't care about your file structure, it's just some functions you can call. People have used different patterns for organizing files.
[00:06:33] You've never been required to declare your action types as separate variables and stuff, but those were the patterns we showed in the docs, and there were good reasons for those. I've discussed some of those in my Idiomatic Redux series on my blog. But a lot of that is not, like I've described it as, there's incidental complexity and inherent complexity. Redux has inherent complexity because it introduces a layer of indirection. You need to separate out the process of what happened by dispatching an action from the process of saying, here's how my state updates by putting that logic in a reducer. And that will always be a little more complicated than just saying obj.x = 1, 2, 3 in a click handler.
A lot of this additional complexity comes from things like JavaScript is a mutable language by default, and so having to do immutable updates gets a lot more complicated. And yeah, a lot of the patterns that we showed required writing a lot more code. And that was kind of painful to deal with. Several years ago I filed a discussion issue asking, how can we make Redux easier to use?
[00:07:39] How can we simplify some of these common patterns? Out of that, we created this library called Redux Toolkit, and as you said, its primary purpose is to simplify the common use cases and patterns that people see in Redux code. We were able to look at how people are using Redux, what are the common problems they run into, what are the pain points they're dealing with, and create a series of APIs that really simplify those standard use cases.
So it's things like being able to create a Redux store with just a couple lines of code, one function call that sets it up with a good series of defaults that catches common mistakes, like accidentally mutating the state, that automatically turns on Redux DevTools. It's APIs like createSlice, which automatically generates all those action creators and action types for you, so you never have to write them by hand ever again. It's using libraries like Immer, which let you write mutating syntax like state.push inside and actually have that turned into safe and correct immutable updates so that you can't accidentally mutate ever again.
[00:08:51] And the state update code you're writing is just vastly simpler. From there, we've gone on to add some additional APIs for things like making async calls and dispatching actions and managing normalized state in your reducers. All these are directly designed to simplify the standard things Redux users are trying to write today.
00:09:15 - Christopher Burns
I've had a little experience with Redux. It was three years ago. I actually started before I learned React. I learned React Native. I watched a few talks and they were like, oh, here's how you need to handle offline state, online state, all these states. Okay. Load up Redux, load up Redux Saga, load up 20 other things. Here you go. This is how to do state management. And it confused me as a beginner to React, not necessarily a beginner to any JavaScript. It confused the hell out of me. For ages I just ran away screaming and never came back.
I feel like if I went to look at it now, I would probably understand it better now that I'm more seasoned. And it's definitely what you go back to, of how much of the language do you understand before you can ask the right questions? For example, the other day I was doing an API request that required a bit of array manipulation with a multi-step process to sort the array, add variables, sort it again, and as I was going through it, I was like, oh, it's going forward, it's going backwards, going forward.
[00:10:18] But I'm declaring new variables each time with the array, with, say, array. So let a equals array1.sort blah blah blah. It was only when I actually realized that it was mutating it every single time that you think, wait, do all these things actually do this? And then you Google it and like, yep, all of these other things do. And you just never notice it until you really notice it, that it happens this way. It's a bit crazy sometimes.
00:10:45 - Mark Erikson
There's actually a site out there, I believe the URL is doesitmutate.xyz, which has a list of all the different methods that are built into arrays. It's like, mutates? Yes. Mutates? No. Sort and reverse are probably the two that bite people the most because they return what looks like the sorted or reversed array. But what you don't realize is it's the same array that just got updated in place.
00:11:15 - Christopher Burns
In all aspects, it doesn't matter if you put let a equals array.sort. What's actually happening is the array that you tried to sort has now been mutated with the sorted one. So it doesn't matter if you did let a because the original array mutated. And if you're trying to keep track of the progress through each step you're trying to do, that gets quite confusing quite fast.
I think as a JavaScript developer, mutating and immutable are two concepts that are hardly taught properly. I didn't realize it until I actually learned it, if that makes sense, because a problem came up and then it still catches you out sometimes.
00:11:56 - Mark Erikson
Immutability is not a concept that is strictly unique to either Redux or React, but from what I've seen, both React and Redux emphasize that concept far more than any other framework. Vue's got your proxy-wrapped variables. Ember is all about the get and set functions. Angular has all the chains and class variables. It really feels like it's primarily React and Redux that emphasize the concept of immutability as a first-class concern.
00:12:31 - Anthony Campolo
I frequently hear this talked about when I was learning in just the realm of JavaScript itself, as you talk about pass by value and pass by reference. And that's what they would try to get you to understand. Okay, sometimes it's going to pass the actual value itself, or sometimes it's going to copy it and then make a reference to it. And if you aren't aware of when you're passing by value or when you're passing by reference, you're getting yourself into trouble with these kinds of things.
00:12:59 - Mark Erikson
One of the tricky things for me when I was rewriting our docs last year: I wrote a brand new tutorial from scratch that I called the Redux Essentials tutorial, which now actually teaches Redux Toolkit as the standard way to write Redux logic. And then I rewrote the existing tutorial sequence, which starts off with writing all your actions and reducers by hand. I heavily reworked that and called it the Redux Fundamentals tutorial. And that one now finishes by saying, now that you've written all this code by hand, here's Redux Toolkit. Please never write that code by hand again.
But the real trick in both of those is that Immer lets you write mutating state syntax, and it correctly turns it into safe and correct immutable updates. My concern is that people will see that and think it is always safe to mutate in a reducer. And the answer is no, no, it's only safe if you're inside of our magic createReducer and createSlice functions that use Immer. How do you carefully teach that?
[00:14:05] And my only answer was slapping giant red warning banners multiple times in the tutorial, saying this only works because of Immer.
00:14:14 - Anthony Campolo
Can you actually give a brief overview of what Immer is?
00:14:18 - Mark Erikson
Immer is a library from Michel Weststrate, the author of the MobX state management tool. The idea is that it really exports a single function called produce by default, and you pass in your existing state, so some kind of an object or an array. The second argument to produce is a callback function. And the callback function receives what looks like your exact state that you just passed in, but it's actually been wrapped in a special kind of a JavaScript type called a proxy, which lets you intercept any attempts to read or write any fields in that object within that callback.
You can mutate this draft value. You can set fields, delete fields, add nested values, push into arrays, anything you want. And it looks like you really are mutating that draft value. But internally, Immer actually records every change that you make. And when the callback function finishes, it replays all those operations with their immutable update equivalents and returns a final, immutable, updated value, just as if you had written a whole series of nested object spreads and array.concat and all the complicated immutable update logic that you normally would have written by hand.
[00:15:44] It drastically simplifies the process of writing immutable update code. One of the very first things that Redux Toolkit was built around was using Immer by default in our createReducer function, and you don't even have to think about it. You just write some reducers that mutate the state and it just magically works.
00:16:05 - Christopher Burns
This is a very broad question, and you obviously answer it in your documentation to a certain extent. When or if do I need Redux? Like what is a really good example of a problem that's starting to balloon, that Redux would fix.
00:16:24 - Mark Erikson
There's been lots of reasons given to potentially want to use Redux over the years. One of the most popular reasons traditionally has been to avoid the prop drilling problem. I need some data in widely separated components, or even just in a parent in a deeply nested child. Redux lets me bypass handing it down as a prop through every level. That is a reason to use Redux, but it's really not the kind of reason I try to emphasize today.
Really, the main point of Redux is that it allows you to write as much of your code in a predictable way, so that you can understand when, where, why, and how your state changed over time. Now, there's also valuable properties of having a central store, being able to do things like see those state changes in the dev tools, have middleware that look at the flow of actions and kick off additional logic. Really the point that I try to emphasize these days as the initial reason to want to use it is about the predictable state updates aspect.
[00:17:28] Now, as to when it makes sense to use it: it's gone back and forth over time. React has added more capabilities, like having the built-in useReducer hook. Other state libraries have become more popular over time, like MobX and XState. Now you've got data fetching and caching libraries like React Query and Apollo, and all these kind of overlap with some of the reasons you might have used Redux over time. So certainly, in that sense, there are somewhat fewer times you would necessarily need to use Redux.
The broad, generic answer is it makes the most sense to use Redux when you have fairly complex state management in the client, where a lot of that state needs to be handled by several different portions of the application, and you want to try to manage portions of that logic outside of the React componentry. It's a generic answer, but it's also kind of a generic question, for that matter.
00:18:23 - Christopher Burns
And what would you say are some of the biggest projects you know that use Redux?
00:18:28 - Mark Erikson
I don't have a huge list off the top of my head. I know Twitter's web client uses Redux. I know that Spotify uses Redux pretty heavily. A lot of people think that Facebook uses Redux a lot. They don't. My understanding is that their new Facebook site is mostly built with Relay, which is a GraphQL client. A lot of their older legacy code is still built using the original Flux implementation. There are isolated teams in Facebook that use Redux, but as far as I know, they never really heavily adopted it.
00:18:56 - Anthony Campolo
Slack uses Redux. They have, like, the Redux stores, how they sync between your different devices.
00:19:02 - Mark Erikson
I think Slack, yep. There are a lot of very major, well-known companies and sites that use Redux. Yeah.
00:19:09 - Christopher Burns
Would you say that you would be foolish to implement Redux before you wrote any of your actual logic? So like, I've got a project I need to start building. Would you say start coding first and then when you need Redux, add Redux, or is it better to just go, I know I will need it one day, I'm going to add it from the start and go from there.
00:19:33 - Mark Erikson
There's a lot of different potential answers to this question, and some of it depends on your own development style. There are people who ask, should I start my app by making an HTML wireframe and then splitting it up and writing components? There are other people who would say, you should build out all your logic first, so you can kind of run your app even without a UI. There are other people who just want to dive straight into making some components and splitting it up as they go.
To some extent, it's a question of how you personally approach development. There are valid points to be made in favor of both approaches. There's a question of you ain't going to need it. Don't add a thing until you know you truly have a need for it. I'm kind of punting on the question, but it's because there are so many different ways to approach building an app.
I actually have been working on a project for the last year or so. It's a legacy AngularJS application. We've been migrating it over to React and TypeScript, and I actually deliberately held off and did not add Redux to the new version of the codebase for a long time.
[00:20:35] On the other hand, I actually did bring in Redux Toolkit, and I used the createSlice API specifically to generate a reducer just for use with the React useReducer hook. I wanted to have a well-typed reducer function. I just didn't see the need for a Redux store yet. And actually now we've hit the point where I think that the complexity of the application really does justify having a Redux store in there. So I finally went ahead and set it up, but I was kind of holding off on adding Redux because I didn't think the complexity or the use case justified it yet.
00:21:12 - Anthony Campolo
Let's start getting into some of the TypeScript stuff. You've been converting a lot of Redux, I think, in general to TypeScript. This is something that I've at least seen you tweet about a lot, so I'm curious what exactly that journey has been.
00:21:28 - Mark Erikson
Sure. It's kind of funny because all three of the major Redux repositories handle TypeScript in a different way. The current version of Redux core is still written in plain JavaScript, but we have our own typedefs file that is directly in the repository. React Redux is still written in JavaScript, but we have never kept our own types. The React Redux typedefs are kept over in the DefinitelyTyped repo and the community has maintained those, which is good because the React Redux typedefs are insanely complicated. I'm not sure I ever want to touch those.
On the other hand, Redux Toolkit itself, I started it in JavaScript, but it was converted to TypeScript some months later. That whole code base is written in TypeScript. Technically speaking, the Redux core was actually converted to TypeScript, and it's sitting there in the master branch in the repo, and it's been sitting there for like a year and a half now, and we still haven't published it. Someday it might hopefully become Redux version five, but there hasn't been a need to publish it yet, and there would probably be some breaking types changes.
[00:22:35] So we've actually just sort of been letting it sit there just because there's other, more important things to worry about. I didn't actually start using TypeScript myself until the start of 2019. I started converting one of my apps at work, and I actually wrote a whole blog post documenting my journey learning TypeScript as both a maintainer and an application developer, and I wrote that in, I don't know, maybe like November 2019.
My conclusion at the time was that, okay, I'm fully sold on using TypeScript for applications. I refuse to ever write another line of code again that isn't TypeScript. But the key is to use TypeScript pragmatically. I see a lot of people getting caught up in trying to get 100% absolute perfect type coverage for every line of code and panicking if there's a single use of any in the code base. Because now there's like this hole in your types and the whole application could apparently explode in flames at any second. I think that's way overdone. Granted, maybe some of my perspective is because I've been converting existing JS apps to TypeScript, so I have a much more real-world-ish approach.
[00:23:44] But I think the key to using TypeScript effectively is to go for the 80% bang for the buck. Type your functions, type your arguments, your API calls and results, your React component props, your Redux reducers and action creators. And you might have to throw in some uses of any or like some loose types that say, yep, it's an object. It's got some keys in there, we think, but it's not worth spending hours trying to write this huge multi-line typedef to satisfy the compiler.
If you know that when it goes in, it's a type A and it goes out, it's a type B, hand-wave it a little bit, slap in a type, fix-me-later equals any, and move on with your life. Go do something else more productive. The flip side of this is that if you look at Redux Toolkit especially, we have some incredibly complicated typedefs in there. createAction and createSlice in particular have insanely complicated types because, A, it's a library and we need to handle a lot of different potential ways people could use our code.
[00:24:50] And if you think about it, in a lot of ways, our typedefs are complicated so that application and user code types don't have to be. Redux Toolkit does a lot of work to try to infer many of your types, so that all you have to do is provide, say, the type of your action payload, and we now know what the action is. We can generate the action creators. We can infer types from the action creator later on.
Because we've spent all that time to make our types complicated and handle all those different scenarios, your types as a user can be a lot simpler. And I think that's kind of really the difference between defining types for a library versus defining types for an application.
00:25:33 - Christopher Burns
When we had Lindsay on, creator of React Query, he has been converting his stack over to TypeScript. And he said writing TypeScript for libraries is significantly harder than writing TypeScript in your application.
00:25:48 - Mark Erikson
Oh, absolutely.
00:25:49 - Christopher Burns
It does seem that way if we think about TypeScript as an end user. A lot of the time it's typing your props, typing your function that obviously does something, and that's about it. Not much else happens at the end of the day with libraries that do a lot more complex things. Sometimes headless, sometimes not. It can get very deep, very fast.
00:26:11 - Mark Erikson
Look at React Redux for an example. You know, connect is a function that takes four arguments, all of which are optional. The first two arguments could be a function or an object or null. mapState could be a function or a function that returns a function. And then you have to do all this passing through of the types of the props combined with the types returned. It's stupidly complicated, which is one of the reasons why we now recommend the React Redux hooks API as the default, because useSelector, it's one function that takes your state and returns a value that is really, really simple to type.
And it's a lot simpler for users to look at and understand what's going on, conceptually. There's still some complicated edge cases hidden in the actual implementation, but from a user perspective, it's very, very obvious what this API does and how you're supposed to use it.
00:27:07 - Christopher Burns
A lot of the time, I do totally agree with you about the 80% principle. I think the other way to look at it as well is how non-obvious is this function. If it's really a complex function and you think, well, I definitely need to put the time in to type it, then yeah. But some of the times you're like, even though it's a complex function, it is very simple. So you kind of ignore it, put any, and move on. I think that is super useful.
And I think the other thing to say at the end of the day is if you work in a team, a type for you may take two hours to write that complex type, but for someone else it could take five minutes. So do you say, I'm going to leave it to any for now for someone else to type later? It depends. Really?
00:27:50 - Mark Erikson
Yeah. I did write a few fairly complex types in that application that I've been migrating to React and TypeScript. There was some back-end code where we needed to derive some data, and we needed to do it in a bunch of different places based on certain inputs, and it was worth the time for me to set up a complex type that captured that transformation, so that every time we did that process, all the variables you were dealing with had their types inferred from the inputs in all the places where we actually use this.
You basically say it's a config type with inputs x, y, and z, and everything else in that structure got typed automatically and it just works. It took me a while to figure out how to do it, but in that case, the benefit more than outweighed the time that I sank into making it.
00:28:46 - Anthony Campolo
That was exactly what Kim recommended when she was on the show. We talked about TypeScript with her, and she said that getting the setup so that your types are inferred is always going to be the best way to go, because then you can get a lot of it handled for you, and it just will figure out a lot of your types for you. So that sounds like pretty good advice.
Why don't we get into the RTK Query stuff. So I'll be curious to get your definition of what RTK Query is and how it relates to Redux Toolkit.
00:29:16 - Mark Erikson
Over the last couple of years, there's really been a big change in what a lot of developers, especially in the React ecosystem, are looking for and what problems they're trying to solve. In 2014, it was about state management. You know, Flux was basically created to solve a lot of the problems people saw with Backbone models and events being triggered throughout your application. Flux and Redux were designed as a response to that problem.
What we've seen over the last few years is people are really switching over and they're focused more on data fetching and caching. You've always been able to fetch data from a server and put it into a Redux store. It's just that Redux didn't provide you anything for that out of the box. And so the typical pattern is you write a thunk that dispatches an action before you make the API call, and then you make the request and then you dispatch another action on success or a third action on failure. So you've got to define these three different action types. And then you've got to define a reducer that has some loading state and handles the before, the success, and the failure cases.
[00:30:20] And now you have to do that for every single API call you make. You can do it. Redux Toolkit even provides some newer APIs like createAsyncThunk that simplify part of that process, but it's still a lot of code to have to write. And what we've seen over the last couple of years is a lot of people don't want to have to worry about any of that. They just want to say, here's a URL, go get it for me, give me back my data, and is-loading values. And let me just render my UI based on that. I don't care how it happens. I just want to know, is it loading, and is it done?
So you see libraries like React Query and Apollo and SWR and Urql really taking off in this space because they were purpose-built to solve the problem of fetching data from a backend, abstracting away the entire process of managing the caching and the loading state. As a user, all I do is say use my query and I'm done.
[00:31:16] And there have been lots of third-party libraries that abstract some of that data fetching process for Redux, but we never had anything built in. I knew this was something we would want to try to tackle eventually for Redux Toolkit, and I'd been imagining kind of like a createAsyncSlice API, hypothetically, that would call createSlice internally and maybe generate some thunks for a REST API automatically or something.
There was a discussion thread where people were kind of pasting in their own versions that they'd made. And late last fall, my RTK co-maintainers, Lenz Weber and Matt Zukowski, just started making a brand new library that appeared out of nowhere. I almost wasn't even aware of what they were doing, and so they started hammering on it and throwing pull requests back and forth. What they've built is a Redux-based version of a data fetching library, inspired by the APIs and feature sets of these other data fetching tools, like Apollo and React Query. It solves the same kind of problem space where you basically just say, here's my server, here's my endpoints, and it generates all the data fetching code and the loading state management for you automatically.
[00:32:32] But it has several distinct differences from how React Query or Apollo work. Because it's built on Redux Toolkit, it's completely UI-agnostic, so the core functionality works even if you aren't using React. We've had people play around with building integrations for Vue and for Angular and for Svelte. You go about defining your API endpoints in a different way. You actually kind of define the endpoints in a single service file, and then it automatically generates a reducer and a middleware that you can add to your store setup, rather than React Query, where you're defining the URLs more as you're calling the useQuery hook.
And then on top of the UI-agnostic behavior, it actually has some React-specific functionality where it does generate React-specific hooks for each endpoint that you've defined. The canonical example in the RTK Query docs is a Pokemon example where it hits the publicly available Pokemon API and it has like a Pokemon-by-name query or something like that, and it automatically generates a React hook called useFetchPokemonByName or something like that.
[00:33:45] And on top of that, because this is all written in TypeScript, we're able to generate the hook with all the correct types in terms of what the arguments are and what the return value is. So they threw this together really over the course of just a couple months. And for purposes of experimentation, we've published it as a separate temporary standalone package that we've called RTK Query, or Redux Toolkit Query for short.
The idea was that it would allow us to iterate a little bit faster on this, rather than trying to put out a whole series of alphas of Redux Toolkit itself. That's allowed us to gather feedback, iterate on the API, make a bunch of improvements, make sure we're handling all the use cases correctly. So as of right now, today is mid-April. We are finalizing the work to put together a final alpha of RTK Query as a standalone package, and as soon as that's done, we're actually going to take all the code and all the documentation, and we're going to merge that back into the main Redux Toolkit repo.
[00:34:48] And we're actually going to put out a new Redux Toolkit release, probably version 1.6, that actually now has all that RTK Query functionality in the official Redux Toolkit package. So you just install Redux Toolkit as usual, and you get all the RTK Query functionality built in. We do intend to deliver this as separate entry points to help with the tree shaking and all that kind of stuff, so that you're only getting what you actually want to use in your application. But we're very, very excited about this.
00:35:19 - Anthony Campolo
I guess the first question most people would want to know then is why use that over bringing in React Query?
00:35:26 - Mark Erikson
First off, I want to say React Query is awesome. Tanner has done an amazing job building that library. He's done a lot of work to really help popularize this style of API design. RTK Query kind of shamelessly rips off, I'm sorry, I mean, takes inspiration from React Query and Apollo. Again, in terms of API design and feature set, I want to be very clear that we owe a lot of debt to them for kind of helping trailblaze this.
There are definitely people who have used React Query and Redux together. Tanner's standard comment is that if you switch all your server caching state into React Query, what's left over is probably so minimal that you can get by with just standard built-in React component state. I would say in a lot of cases that is probably true.
But some reasons you might want to consider using RTK Query and Redux Toolkit in this case: one is if you already have a Redux app and you want to simplify your data fetching logic, you want to drop a lot of the hand-written thunks and stuff that you already have. Another would be if you have an app that needs to deal with multiple different UI layers, maybe you're in the process of migrating from something to React and you happen to be using Redux. Or maybe you just really like to be able to use Redux DevTools and the rest of the Redux ecosystem, and you want to be able to integrate your data fetching capabilities with the rest of those tools.
One other interesting thing that's also still in a pre-alpha stage right now, but we've got some proof of concepts where we can actually take an OpenAPI schema definition and code generate an entire RTK Query service endpoint file with all the right types, with all the right endpoint definitions, and generate all the rest of your hooks based on that. It wouldn't surprise me if someone has done the same thing for React Query. I just haven't looked. But that's definitely something that we have a proof of concept for and we should be able to expand on down the road.
00:37:24 - Christopher Burns
It's a bit like if you were going to use something like Prisma. Prisma is obviously not for hooks. It's more like a function, but it's kind of like that, in that you would define a function or hook that would just give you all the information when you're required, and you've basically abstracted away the whole fetch, the whole loading, everything to do with that. It is all abstracted away with a simple hook to basically use the library. Pretty cool.
00:37:48 - Anthony Campolo
And you can use RTK Query with GraphQL. I know I've seen at least one GraphQL example in the docs.
00:37:55 - Mark Erikson
We actually do provide our own built-in fetch wrapper automatically, and so most of the examples use that fetchBaseQuery thing to do the actual API calls. But it's easy to swap that out if you want to use something like Axios or similar instead. And because it's ultimately just making basic HTTP requests, it's pretty easy to swap in some kind of layer that does GraphQL query string parsing, and does the actual GraphQL request and returns whatever the data is. And so, yeah, we have a GraphQL example in the docs.
00:38:30 - Christopher Burns
The goal of RTK is to never fully replace Redux. It's an addition to Redux.
00:38:37 - Mark Erikson
Well, that's where we get into some interesting philosophical questions. Redux Toolkit itself is a wrapper around the Redux core library. The Redux Toolkit package depends on Redux core. We've had discussions of what would happen if we just made all this literally be the Redux package, and we concluded that there's just too much inertia in the ecosystem. And Redux Toolkit is opinionated enough that not everybody likes those opinions. It's best to just leave it as a separate package the way it is now.
Having said that, if I could wave a magic wand and replace every single hand-written reducer, every single nested object spread, every single hand-written constant, every single hand-written thunk with the Redux Toolkit equivalents, I would do that in a heartbeat. Obviously, realistically, that isn't going to happen. But from my standpoint right now, Redux Toolkit is Redux. Redux Toolkit is the standard way to write Redux logic. If you really want to know how to do it by hand, we'll show you how those abstractions work under the hood. But from a teaching perspective and a usage perspective at this point, Redux Toolkit is the way to use Redux.
00:39:53 - Anthony Campolo
You heard it here, folks, from the man himself.
00:39:56 - Christopher Burns
I think the easiest answer would just be to put at the top of Redux, do not use this unless you 100% know what you're doing. Just go use RTK. Just call it a day. But we will not accept any questions unless you know what you're doing.
00:40:11 - Mark Erikson
Like I said, it's been an interesting challenge to balance how we teach this stuff in the docs. And so that is why we now have these two large tutorials. So the Essentials tutorial just says, okay, we're going to teach you Redux Toolkit as a standard approach. And we're actually going to jump in to kind of like building a real-world-ish type application with API calls and normalized state and all this other stuff. And we're not going to worry about how this works under the hood. We're just going to show you how to use it the right way so that you could dive in and start being productive.
But other people really need to know how it works first to feel comfortable using abstractions. And so that's why there's also the Fundamentals tutorial, which does still start from the bare basics. It shows hand-writing reducers and action type strings and everything else. And then it finishes by showing, and now you can simplify your store setup, simplify your reducers, simplify your data fetching. Here's how Redux Toolkit makes all that a whole lot simpler.
[00:41:10] Honestly, one of the biggest frustrations for me today is that there are thousands of existing Redux tutorials that are still showing legacy Redux patterns, videos that people are looking at. I even noticed literally just yesterday, like freeCodeCamp and Codecademy are still teaching older style patterns, including use of the connect API in their courses, and I can't do anything to magically update all those other tutorials out there. And the problem is most tutorials aren't updated to match the patterns we're showing in the docs. It frustrates me, but I can't do anything about it except gripe on Twitter. All I can do is try and tell people, read our docs and encourage people to look at the newer patterns.
00:41:55 - Christopher Burns
I think I've just come up with the ultimate solution: rename RTK Query to Redux Two: Cool Redux Redux, and be like, oh no, that's completely gone. We're now on Redux Two, even though SemVer is something completely different. Take the Prisma approach.
00:42:12 - Mark Erikson
That actually brings up a big point in regards to maintaining a library, and that's dealing with ecosystem compatibility. I take compatibility concerns extremely seriously. I'm fortunate in that both the Redux core and the React Redux library have basically had the same basic APIs since 2016. If you look at React Redux, we've actually rewritten the guts of it like three or four times, and I have a whole 10,000 word blog post about when and why we've done that.
But for all intents and purposes, you could take your Redux code from 2016, update to the latest versions of Redux and React Redux, and it would work just fine. I mean, we've got people who are using the Redux core as a script tag in a CodePen. We've got people who are writing hyper-sophisticated code split tools in Airbnb and Slack and whatever it's being used in, everything from IE11 to Node to the latest Chrome.
00:43:07 - Anthony Campolo
But has anyone rewritten Redux in Rust?
00:43:10 - Mark Erikson
I think I may have seen that at some point. I've certainly seen reimplementations in like C#, Java, and C. As a maintainer, I have to take all these different potential runtime environments and use cases and stuff into consideration. There's a saying out there called Hyrum's Law, which basically says every bit of behavior in a library, even if it's not formally documented, will become something that people expect and depend on.
As an example, in React Redux version six, we switched to using the context API for state updates, and one of the reasons I did that was that it would help ensure consistency of the data all throughout the React componentry. It turns out that a lot of the code splitting and SSR solutions for Redux relied on being able to inject a reducer as a component is loaded and immediately have the additional state available as soon as that component tries to render. And with this context-based approach, that didn't happen. You were still looking at the old state as it existed at the time the render started.
[00:44:15] I didn't realize people were relying on that aspect of our behavior until someone filed an issue and said, hi, this is broken, what can we do about it? The problem sort of got fixed because we dropped the context-based approach in React Redux version seven, but it's all these edge cases and scenarios that you have to take into account when maintaining Redux Toolkit.
Right now we just redid our build system. We switched from using the TSDX package for bundling the library. It's actually using a custom setup based on esbuild that someone contributed for us. As I was looking at that, I was having to say, okay, how many different output file formats do we need? We need CommonJS for development and production. We need an ES module with import/export syntax, but it's got to be backwards compiled to ES5 so it works in bundlers and IE11. We need UMD files for development and production so that people can use them as script tags. We also need a modern build that has import/export syntax, but ES2017 compiling syntax all the way through.
[00:45:15] But we also need versions that have been pre-compiled for development and production. And, oh, if I add this new exports keyword to our package.json, it's apparently going to break under Node because Node just switched how they handle ES modules. It's enough to make you pull your hair out. I would still love to have a guide that says how to do this stuff the right way, because I barely feel like I understand any of this.
00:45:39 - Anthony Campolo
I'm glad you got some esbuild in there right at the end for me. I appreciate that.
00:45:44 - Mark Erikson
Yeah, I was making a comment about how complicated our setup is, and I made some comment about, like, esbuild can't handle this, right? And some user was like, yeah, well, I thought it was just for applications, isn't it? And the user basically responded by filing a PR to replace our build system. I'm like, oh, okay, I was wrong about that. I'm happy to be wrong.
00:46:06 - Anthony Campolo
Yeah, esbuild is pretty powerful.
00:46:08 - Christopher Burns
It's pretty powerful. But I'm not the first one to be like, is that it? Like, installed? And it's like, was that it? It didn't feel much faster on some things.
00:46:21 - Mark Erikson
But there was a great video that dropped about a month ago with, I think, Jason Lengstorf and Sunil Pai talking about how esbuild is insanely fast. And, okay, if your build happens in like a few milliseconds, how does that change your approach to developing applications and libraries? And what other tools like Vite are being built on top of esbuild? It was a fascinating watch. Highly recommend that.
00:46:46 - Anthony Campolo
I liked that episode. I shared that with Aldo. He appreciated it also. All right. Well, we're about at the end of our time here, Mark. Really appreciate you being here with us. I really appreciate all the work you put into open source and not only the library, but communicating the library and thinking holistically about what the library needs and how it fits into the whole ecosystem.
I appreciate your kind of quote-unquote thought leadership as much as we all hate that word. It is kind of what some of us do to a certain extent. You got to take that seriously. So thank you for that, and let our listeners know where they can find you and where they can get in touch with you.
00:47:24 - Mark Erikson
I'm at acemarke on Twitter and Reddit and the Reactiflux Discord. I'm markerikson on GitHub and Stack Overflow. I blog at blog.isquaredsoftware.com. Like I said, if you say the word Redux in a comment thread, there's a good chance I'll pop up about five minutes later.
00:47:44 - Christopher Burns
Thank you for your time. It's been great to have you on. I was going to say the final question would be, but do I truly need Redux? Go read the documentation and it will tell you if you need it or not.
00:47:57 - Mark Erikson
Thanks for having me.
00:48:27 - Anthony Campolo
I got a lot of respect for people who just pour as much time into open source as you do, so we'll have a lot of things to talk about.