This week Jamon and Mazen are joined again by Mark Erikson to discuss RTK vs MST and some of the benefits and downsides they each have.
This week Jamon and Mazen are joined again by Mark Erikson to discuss RTK vs MST and some of the benefits and downsides they each have.
This episode brought to you by Infinite Red! Infinite Red is a premier React Native design and development agency located in the USA. With five years of React Native experience and deep roots in the React Native community (hosts of Chain React and the React Native Newsletter), Infinite Red is the best choice for your next React Native app.
Helpful Links:
Connect With Us!
Todd Werth:
Welcome back to React Native Radio Podcast, brought to you by Pumpkin Spice Latte, which is an odd way to spell vanilla. Episode 249, A Redux Maintainer's Thoughts on RTK vs MST.
Jamon Holmgren:
Mazen, have you ever been to an IndyCar race?
Mazen Chami:
Is that the one where it's like track and field where they're running around in a circle, but in cars?
Jamon Holmgren:
I think you understand the basic concepts of motor racing, at least. I know that you're a Formula 1 fan.
Mazen Chami:
Yes.
Jamon Holmgren:
You are? Who's your favorite driver?
Mazen Chami:
Lewis Hamilton mainly, but I do like George Russell, just because he's young and coming up.
Jamon Holmgren:
Yeah, up and coming. Lewis Hamilton, I mean, you can't go wrong with Lewis Hamilton. He's amazing. So I went to this IndyCar races in Portland International Raceway, which I've only been to with my friends 20 years ago when they would drag race there. So I didn't even really realize they did big Grand Prix and stuff there.
And the funny thing was it felt like, I don't know. I've watched Formula 1 races, it looked a lot like it. IndyCar looked a lot like it. I know that might be sacrilege to people who are Formula 1, but there was a Formula 1 former driver there, Romain Grosjean? Is that how you say his last name?
Mazen Chami:
I think I know who you're talking about.
Jamon Holmgren:
He raced for 10 years in Formula 1 and he was there, didn't do very well. He actually got into the most wrecks of everybody. It was pretty cool. But we were in a really good spot where there was a long straightaway and then there was a quick little turn right in front of us. So that's where all the accidents happened.
Mazen Chami:
Gotcha. That's interesting. I never really, I've heard of it. Just never watched it.
Jamon Holmgren:
I think they do a little more oval track racing like IndyCar does, where Formula 1, that's never a thing. But in this case it was very much like a Formula 1 track.
Mazen Chami:
Gotcha. It's interesting that Grosjean isn't doing too well. He was on, if I remember, he had a little stint with Mercedes, so he'd driven high performance cars. He was with Haas. That's who he was for a while. So interesting. I guess it's just different sport kind of thing.
Jamon Holmgren:
I don't know if Grosjean must be ... I mean he might be okay, doing fine. But in this particular race, he was definitely running off the track a little bit and through the barriers. It was pretty exciting. I've never been to a real race. That was the very first time and it was fun.
I don't really have a great pivot into our bios from there, but I'm just going to go ahead and roll right into it. I'm Jamon Holmgren, your host, friendly CTO of Infinite Red. I live in the Pacific Northwest with my wife and four kids. I play recreational hockey. I just added that to my bio, rec hockey. Why not? And I drive my tractor whenever I get an excuse to.
I'm joined today by my notable co-host, Mazen, and Mark Erickson, who I will be introducing in just a second here. Mazen Chami lives in Durham, North Carolina with his wife and baby boy. He's a former pro soccer player and coach and is a senior React Native Engineer, also here at Infinite Red.
Mark Erikson is ... I'm really excited about this by the way. Really happy to have you on, Mark. Mark is one of the primary maintainers of Redux and Redux Toolkit. He's known for his expansive and extensive Discord replies that are according to some, better than many projects documentation. He lives in Cincinnati. Is that right, Mark? Cincinnati?
Mark Erikson:
Dayton, Ohio. Close enough.
Jamon Holmgren:
He lives in Dayton and is an avid golfer, reader, PC gamer, and a fan of the Bengals, the Reds and the Ohio State Buckeyes.
Mark Erikson:
Indeed.
Jamon Holmgren:
Yes, the Ohio State Buckeye. You have to say it that way, otherwise they get offended.
Mark Erikson:
Yeah, that whole "the" thing is kind of silly, but yeah, whatever.
Mazen Chami:
I have some Ohio State fans and if you don't put in "the" before it, they get pretty mad.
Jamon Holmgren:
They correct you. You'll hear about it. Before we get started, this episode is sponsored by Infinite Red. Infinite Red is a premier React Native design and development agency located fully remote in the US and Canada. You're looking for React Native expertise for your next project? Hit us up at infinite.red/reactnative. And don't forget to mention that you heard about us through the React Native Radio Podcast, specifically the one that Mark Erikson was on, because we like to hear that.
All right, let's get into our topic for today. So I'm really excited about this. We had a chat with Mark after our previous episode, which was-
Mazen Chami:
241 Redux Toolkit versus MST.
Jamon Holmgren:
241. And we kind of did this breakdown of Redux Toolkit versus MobX-State-Tree. And what was kind of interesting about that is both Robin and Mazen were using RTK on their current project. They had been for actually quite a while. And of course, I'm primary maintainer of MobX-State-Tree.
So we kind of came at it from that angle, but this is even better. This is where we actually get a first person view from Mark. So Mark obviously is the primary maintainer of Redux Toolkit, and I'm going to be asking you a bunch of questions, Mark, more about that. But that's really where we want to get your perspective on all of these.
Mark Erikson:
I actually want to say I appreciated the fact that that podcast episode was an actual nuanced discussion of tradeoffs, and pros and cons. Far too often on the internet, any discussion of different technologies turns into a religious war or just, "This tool is awful. I hate it. Don't use it."
Different tools have different strengths and weaknesses and different purposes, and we all win when we make reasoned intelligent comparisons rather than just yelling at each other about which one's better.
Mazen Chami:
Yeah, I agree. I think we even said it in the episode. We weren't trying to pick one over the other or attack one versus the other. And like you mentioned, that happens all the time unfortunately. We had firsthand experience of both. And for a Redux Toolkit, Rob and I had been for about a year and a half I think at that point. So we had quite a bit of working experience with it and felt like we could come to the table and just give a good comparison of both.
And then also give the listeners the idea that this is what's out there. You don't have to stick with one over the other. Feel free to make your decision based on what sounds and what works best for your app. Sometimes MST might be the best option. Sometimes one of Redux, Redux Toolkit or RTK Query might be the best option for your setup.
Jamon Holmgren:
And I want to reiterate that we use Redux and RTK a lot at Infinite Red. In fact, most of our listeners probably use it. That's definitely, it's the winner in the space, and we have for years. I mean, I think at the very beginning, we used Redux and we will continue to.
It's a great state management system. It is well understood. There's a lot of really cool things that it brings to the table that it does really well. And at the end of the day, the community is just massive, massive, massive community, which is awesome. So again, these are trade-off decisions and sometimes personal preferences come into play here. But at the end of the day, that's what we're talking about.
So we did that episode. Mark had a reaction to this episode and you said that you appreciated the nuance, but obviously, it was our perspective. There's more to it than what we brought to the table.
So let's just launch into it, because I think we have a lot to cover. I want to make sure we get enough time in here. I want to talk first about the Flux pattern, and I was kind of critical of the Flux pattern in the episode. I want to hear your perspective, Mark. I want to hear where you're coming from on that.
Mark Erikson:
Sure. So I'm a very big fan of the idea that understanding a tool and what it's supposed to do and when and why to use it really benefits from understanding the historical context. When was this tool created? What problems was it originally designed to solve? What were the other influences at the time? And what was even the state of the art at the time?
You described the Flux pattern some before, but let me see if I can give a brief recap again since it's a new episode. So React came out publicly in 2013 for Facebook, and a year later, they had another developer conference and they announced this concept called the Flux Architecture. And the original intent of this was they've been building complex client side apps, Facebook, and they were seeing cases where data was getting out of sync in different parts of the app.
I think the classic example was you would read your messages in the chat panel, but you'd still have the notifications counter up top showing some unread messages. And at that time, common libraries were things like AngularJS version 1, which let you do things like directly mutate the contents of the controller data at any point in the template or backbone where you had these little models and collections that were smart wrappers around JavaScript objects, but they had event triggers.
And so you change the first name field in a person object. It triggers a change first name event. A few can listen to it, but other code can listen to it and run more code and trigger more events. And next thing you know, things could kind of like ricochet around the app. So you had cases where either any part of the app could change the data, or code could be listening to certain events and it would ricochet around and you just really didn't have a good consistent idea of what is going to happen when some piece of data is updated.
It was this idea of being able to really have predictable state updates. So what Facebook came up with was really more a concept in a pattern than a specific library. They said, "We're going to define some reusable chunks of code called stores," and they would typically define one for each piece of domain data. So like a user store, a post store, a comment store, and they would kind of collect these together and they would put them behind a central point called a dispatcher.
And then anytime any piece of code wanted to trigger a state update, you would write a plain JavaScript object called an action, usually with a string type field that describes some event. Send it over to the dispatcher, the dispatcher would iterate through all the different store objects and give them a chance to update if appropriate. And then each of the store objects have their own subscribe method and different pieces of UI could listen to those.
It really focused on this idea of a one-way data flow where it's very predictable that the sequence of events and when you know that a thing has actually changed.
Mazen Chami:
I really want to highlight your word of ricocheting around the app because-
Jamon Holmgren:
Yeah, I was laughing at that one.
Mazen Chami:
You just describing it ... This is a perfect example where I want to come in with a fresh set of eyes and hear your perspective on. And you saying that just makes me go back to the days of using it from the beginning. And it literally just felt like, okay, good luck. Come back with the right information and then ping, ping, ping, rerender, rerender." And then it's back. So that really rings true to me.
Mark Erikson:
So Flux Architecture was announced in 2014 and Facebook did put out a small utility library that tried to help with it, but it wasn't like they released a complete library that did all the pieces. So over the next year, the community took this idea and ran with it. They put out dozens of different Flux libraries, usually with names that were bad back to the future jokes like McFly and Fluxer and all this other stuff.
A year later, Dan Abramov had this idea for a conference talk and he wanted to show off this idea of time travel debugging, except he didn't actually have any code that would do that yet. So he was already very familiar with Flux and Redux really started as two or three ideas mashed together. Let's build yet another Flux library. Let's make this time travel debugging thing possible, and let's also apply some functional programming principles to it.
And there are some reasons for this. Dan wanted to be able to do hot module reloading and have just be able to swap the UI layer and stuff like that. He also wanted to make server side rendering more feasible. And those were really the influences that went into Redux to begin with, Flux architecture, functional programming. And as part of that, if you look at some of the original discussions between Dan Abramov and Andrew Clark, they originally had this multistore concept and they realized, "Wait a minute, we don't have to have these stores hold the data. They can just be functions that accept the data as an argument. We're going to call them stateless stores."
They kind of went with that for a while. Halfway through Andrew is like, "You know what? I don't like that name. You should call them something else. Maybe Reducers." And Dan is like, "Okay, yeah, fine, we can go with that." I've actually got a whole gist where I went through a lot of the early issues and I tried to pull out some of the interesting spots. Where was the first place the word reducers was used? Where do they decide on the name thunks, and other things like that?
The amazing thing is a lot of this happened in a two-month period. It was a very fast development cycle. So that is the context with which Redux was created. It inherited a lot of the terms and concepts and data flow from this Flux Architecture and inspiration from the other Flux libraries at the time. It applied functional programming principles. It swiped a couple other terms and ideas. The word thunk actually is a longstanding computer science term. It's the idea of a delayed computation. And my first job was doing some C++ plus virtual machine type stuff in like 2008. They were using the term thunk back then.
Jamon Holmgren:
Yeah, I didn't know that. That's very interesting.
Mark Erikson:
So there's a lot of interesting historical reasons why these things even exist.
Mazen Chami:
That's really good to know. First time I just learned something new today. Great. Can you, Mark, I guess what we're going to do, I want to ask you to kind of go back a little bit. How did you get into maintaining Redux? And if you don't mind just telling us how that evolved into where you're at now.
Mark Erikson:
Sure. It was entirely by accident. So my first web app was using Google Web Toolkit back in 2011, 2012. It was a Java to JavaScript compiler and at the time I was very familiar with your classic object-oriented desktop style frameworks, Qte, MSE, Swing, C#, WinForms, et cetera. I really didn't start doing a JavaScript until 2013. There was a whole long rant about the project that I was on. The short version is the first six months it was being built in just jQuery and we ended up with brand new code base unmaintainable spaghetti.
And I had been reading about these newfangled JavaScript MVC frameworks that were coming out. Ended up doing some prototyping, figured out that backbone was a good option for our project. I rewrote a lot of that jQuery spaghetti into backbone over the next couple years, taught other people on my team how to use it, cobbled together a dozen different backbone plugins for event handling and data binding and other stuff.
By 2015 I was thinking about the idea of trying to rebuild that GWT apps client in something JavaScript because I was the only person on my team who'd worked with GWT. Mid-2015, I had heard about React and decided I would finally give it a shot. And purely coincidentally, that was exactly the same time that Redux was being developed. So my first looks at some of these Flux libraries where things like ALT was a popular Flux slip at that time.
I discovered the Reactiflux chat channels back when they were still on Slack and was just working, reading, trying to pick up info from other people, reading blog posts and tutorials. Started seeing questions that I knew the answer to and being a friendly, helpful person would try and reply. Kept seeing questions, it was like, "Oh hey, I saw a blog post about this the other day. Let me go back in my browser history and pull up the link and paste it in." Kept answering the same questions.
So I started collecting a list of the blog posts I was referring to most often. Goofy thing was by the end of the year I was answering so many questions, they asked me to be a moderator in the channels and I still hadn't written any real React code yet. My first real React and Redux code wasn't until December 2015, January '16 as I started trying to rewrite a piece of that GWT app.
So early 2016, I was seeing the same Redux questions everywhere, Stack Overflow, the issues, Reddit, Reactiflux, and I semi-volunteered to write an FAQ page for the Redux docs. Dan said, "Go for it." And I had some free time, so I spent the next couple months assembling a list of questions and writing up a long list of answers with my usual links to 15 different resources for each one. That got merged. Dan gave me commit rights and for the next few months, I really felt like I only had permission in my own head to help triage issues and answer questions and things like that.
Dan had gotten hired in, I think, November '15 to work on React. And so by mid-'16 he was pretty busy with that, and he handed over the maintainer keys to myself and a guy named Tim Dorr. It still took me a few more months until I really felt like I was a maintainer and actually had the right to say, "This is how the library works and it should be used." There were a couple of very specific issues. One where someone proposed adding a for loop to the guts of Redux so we could accept multiple actions at once when you dispatch, and another where someone basically popped up and said, "Yeah, I've rewritten React Redux from scratch. And I think it's faster. Would you like this implementation?"
And at first I thought it was kind of a joke and I kept talking to him and he kept addressing the edge cases I brought up. And so by the end of those couple issues, I felt like I actually knew how things worked and had the right to say, this is what the library should do.
Jamon Holmgren:
As a fellow state management library maintainer who inherited someone else's system, I understand what you go through because there's a lot of context there when you're building something that you don't have once you take it over as a maintainer. And building that knowledge, it's a big effort. It's a lot of work. So huge kudos to you.
We've talked about Redux's amazing documentation in the previous episode and I've talked about it in blog posts and tweets and everything. It kind of sounds like it's a lot of you. You're doing a lot of that. Now, I know it's a big community effort that I'm not in any way minimizing that, but it sounds like you've put a lot of effort into it a lot of times.
Mark Erikson:
I've written, I'd have to go check actual numbers. I would say it's fair to say I've written a majority of it and certainly the majority of the current docs. Dan wrote all the original docs in the original tutorials.
Jamon Holmgren:
And he's a great writer in his own right, for sure.
Mark Erikson:
I wrote the Redux FAQ 2016. I also wrote a structuring reducer's section with different patterns you can apply. I wrote the Style Guide Best Practices page in 2019, and then I rewrote the tutorials entirely from scratch in 2020. So we now have the essentials tutorial, which teaches Redux Toolkit as the default, the fundamentals tutorial, which walks from the basics on up. I did all the original Redux Toolkit docs and a lot of the React Redux docs as well.
Mazen Chami:
So I just mentioned that I worked on the Redux Toolkit project for a while and that documentation is very good. Coming into a project cold, we had the Redux Experience, not Redux Toolkit. We were a little bit concerned with that. So we went in like, "All right, let's start with the documentation, see some sample apps and figure it out from there." They were great.
The examples were very helpful. We talk a lot about how documentation sometimes can be just as important as what your app or what your library does because without your ability to give your message across the table, users aren't going to be able to use your app well. So that's very good to have, a really good documentation to help drive the app forward.
Jamon Holmgren:
So one of the things I heard early on when we were first integrating Redux into our first apps, we had two. So 2015, kind of same timeline as you, we were starting ... We actually backed into React. We were more of a native mobile app shop and then we decided to move to React Native because we thought, "Okay, cross platform, this looks really good." We tried it out, it was good.
Started doing that, and we were doing Ember on the website. So we're like, "Okay, we can't be doing Ember and React Native. That doesn't make sense." So we switched to React on the website of things. Now we don't do any web, it's all React Native, but we kind of backed into it. So it was kind of that late 2015 era as well. And we needed a state management library and Redux is what we settled on.
When we were learning it, we had a couple of principal level developers kind of leading the way on that. And what one of them described to me, the Redux thing was that Redux, there's this steep learning curve at the very beginning, the very beginning, but then it really levels off. You don't have this curve that keeps going. You don't have this super deep thing. You kind of learn all the things up front and then you're done. You know how to build an app that's tiny or an app that's huge. And it's the same concepts that scale in a very kind of modular way from then on.
But I would say that that has led to this reputation that you hear all the time, and you're probably sick of hearing this word-
Mark Erikson:
I was waiting for that. I was waiting for that.
Jamon Holmgren:
You're waiting for the first ... This is a drinking game now. How many? But that's the reputation. And I've heard it, I've said it myself. It's a thing that is kind of just out there. And now we're going to be talking about Redux Toolkit because that addresses a lot of this as well. But some people may think that this is a problem for scalability. I actually think the opposite, but what are your thoughts?
Mark Erikson:
I have a whole bunch of thoughts here, so I'm going to try to not go through all of them because we'd be here for a while.
Jamon Holmgren:
Feel free. Yeah, feel free.
Mark Erikson:
The original reputation for boilerplate was entirely deserved on multiple levels. Dan himself said in an early tweet like Flux and Redux were never designed to be the shortest way to write code. It was designed to be repeatable and understandable and being able to ... The focus is on being able to trace data flow and make things predictable rather than I can code golf everything into just a couple of lines.
Now having said that, the early Redux patterns were very verbose and there's a lot of reasons for this. Things like JavaScript itself is immutable by default. So you have to do work to write code in an immutable way. The docs showed patterns like splitting code across multiple folders by type. So you've got actions/todos.js, reducers/todos.js. You want to use your string constants in both files. So you've got constant/todos.js.
And there's valid reasons why you'd want to do that. We split up files, you code into different files conceptually by usage. And we don't want to define, you have inline strings in multiple places. There's very valid reasons for this. But it led to the impression that you have to write the code that way. There's the idea of action creator functions, which abstract the process of defining these action objects again, so you're not like in-lining things and maybe typo in a string somewhere.
And these patterns exist and there's valid reasons for them. Writing all that code by hand over and over and over is a pain. So I had a blog post that I wrote when Redux Toolkit 1.0 came out. And in that, I talked about this idea of inherent complexity versus non-essential. This wasn't the word I used, like non-essential complexity. The idea of dispatching an action and separating that from your reducer logic that updates the state, that's inherent in the idea of Redux. If you change that, it's not-
Jamon Holmgren:
I think I read that article.
Mark Erikson:
It's not Redux. But having to write object spreads for immutable updates or having to write action creator functions by hand or having to split code into reducers actions, constants, folders, none of that is required in the idea of Redux. Well unfortunately, it's the patterns we showed in the docs to begin with. So the idea of Redux Toolkit was, can we provide a way to write Redux code that eliminates this non-essential complexity?
Jamon Holmgren:
Yeah, I think you're right. And that is, you kind of see this in libraries and especially I would say state management libraries, but other libraries as well where the initial goals of the people who created it really inform the shape of it, the way that it feels to build it and things like that.
It wasn't a goal to be, like you said, the tightest, tiniest code. It was a goal though to have repeatable concepts that, like I said, you can build gigantic apps with it and you just don't have to learn anything new to do that. You just keep going. It's a journey of the same steps many, many times repeated. And there's a lot to say for that, especially if you're building something big or especially if for example, you have many projects going on across a broad spectrum of apps and you want to be able to move developers between projects and have the same concepts land over and over.
Mark Erikson:
And actually to that last point, one of the ... I spent a lot of time between 2016 and I want to say mid-2019, actually 2018-ish, keeping up a couple of links list repositories, one with useful articles about React and Redux, and the other was cataloging the Redux-related ecosystem. I would see all kinds of interesting looking Redux-related libraries on GitHub and I would add them to an appropriate catalog page in the repo. And I was seeing the same problems being solved over and over. Libraries to generate action types and reducers, libraries to manage data fetching, libraries to define, all kinds of stuff over and over and over.
So I was getting a very good sense of what kinds of problems people were running into and what kinds of tools they were writing to address those problems. And between that and looking at different people's projects, I could see the problems that they were running into. And one of the issues was every Redux project did end up looking a little bit different. There'd be a different folder structure or a different set of utilities for some of these things.
And so while the concepts may have been the same, the way each project tried to solve them was a little bit different, and that part could be a little disorienting. So another thing we were trying to address was that by providing an official set of tools that address these most common concerns, number one, you don't have to write that code yourself. And number two, it does help make each project more similar and more predictable.
Jamon Holmgren:
We had one of our own versions of those libraries, it was called Reduxsauce, and we used that ... Steve Kellock, when he worked at Infinite Red built that library. It's no longer under our ... We handed that off to someone else to maintain because we no longer see that as the best way to write Redux. But at the time, it was an attempt to kind of go that direction, like you said, Mark.
Mark Erikson:
When we built Redux Toolkit, I literally and explicitly took inspiration from a bunch of other libraries. Redux Actions was a popular way to write a lookup table that handled action types and provide the case reducers. Autodux was the inspiration for the createSlice API. When we built RTK Query, that took a lot of inspiration from Apollo and React Query and SWR for the hook style API design, and this is a win. The community builds on itself. People solve problems that inspires other people and we all benefit because we're coming up with better ways to do things.
Jamon Holmgren:
100%. And I think when you get, like you mentioned, the kind of religious wars at the very beginning, that's what you miss. That's what you lose out on is if you get religious, you get dug into your camp, then you fail to learn from the other sides. That's kind of a life lesson as well. But we'll apply it just to programming here.
So I want to talk, on Twitter, I've been kind of critical of selectors because I come from MobX-State-Tree, MobX specifically where it's observability. Essentially, you can just monitor the object, the store, the model, and you don't really have to think about what pieces of data you're pulling out of your store. You just use them and then the system under the hood works to figure out that sort of thing.
But I know that you have some feedback on this, some thoughts on this. What are your thoughts? I want to hear your side in the spirit of what I just said about learning from other perspectives on selectors and what they bring to the table and how they compare to MobX subscriptions and stuff.
Mark Erikson:
Sure. So the first thing I'll say, so during the last podcast you mentioned you were talking to some client company that had a 1,400 wine selector file. And it's like, well yeah, with MobX-State-Tree, that whole file just goes away. And that is actually a very fair and valid point. And that is probably the biggest difference between an observable-based state management library versus Redux is that you don't have to worry about when is this data going to be read or how many times is it going to update. You don't necessarily have to define these pieces and how you're pointing them out, maybe with the exception of those views and those derived fields.
That is a very fair point. But this also goes back to some of the trade-offs and the design intents. So part of the point of Redux as a whole is to try to make things very explicit. So this does sometime ... Well, probably more than sometimes, can lead to writing more code than you would with a more magic or implicit system. So MobX, a MobX-State-Tree, a Solid or something, or Svelte, something like that where there is a lot of magic going on internally to track these updates and figure out what pieces of the UI need to rerender.
On the other hand, with Redux, yes, you are having to define these. But it goes back to kind of that predictability pattern. So I know that if I'm looking at a React component that depending on how old the code is, I either look for the mapStateToProps definition, if you're looking for the old school, if you're using the old school ConnectAPI, or I look for the used selector calls at the top of the component and those right there tell me exactly what data is being pulled out of the Redux store and is being used by that component.
Another comment that you did make last time was that it is "easy" to write a Redux app that is poorly performing. And this is also a very valid critique. The React ecosystem in general primarily depends on immutability and being able to do reference equality checks to determine if something changed, which means you have to be careful about accidentally generating new references.
And the classic example of this would be, let's say I have a todos list component because we always have a todos app and I want to pull out just the IDs of the todo entries for using the parent. The obvious way to do that is to map over state.todos and pull out the individual IDs.
And the problem is that array map, array filter, whatever return new array references and both connect and use selector, figure out if something changed by default by doing a reference equality check, so bing double equals against whatever you return last time. And they rerun the selectors after every dispatched action. So if you do the obvious thing of return state.todos.map in t.id, by default you are forcing this component to renderer after every action regardless of whether any of the todos data changed or not.
So yes, it is easy to end up in a situation where you accidentally cause components to render more often than you need to, and this is where memoized selectors come into play. But then you do have to do the work to rate those carefully, et cetera, et cetera. So I will freely say up front that this is the biggest case where an observable-based library like a MobX or a solid or whatever does simplify this usage pattern a lot compared to Redux.
I will make the additional observation, even though Reselect is the standard memoized selector library, it's not the only library you can use. Daishi Kato who works on Zustand and Jotai and five million other useful libraries has done a lot of work with proxy-based libraries. He's written one-
Jamon Holmgren:
Yeah, I was going to mention react-tracked, which is one, I don't know if that's his, but it's in that realm anyway.
Mark Erikson:
That's his, yep. And he also has one called proxy-memoize, which uses the exact same approach to generate memoize selector functions, but using proxies internally. And in this particular use case, you could have a selector that just does return todos.map t.id and it would track that only the ID fields are getting accessed. And if some other part of the state were to be updated or even if you were to update todo3.completed, it would still reuse the same result. And I actually added that as a reasonably recommended library when they added a docs page on selector's awhile back.
So Reselect is the most common and it also relies on a lot of these reference checks, but there are other ways to write these as well.
Jamon Holmgren:
And that is actually something I have never used, something like react-track or this proxy based one, which I think is awesome because the newest version of MobX React is actually using a proxy as well. And in some ways when I'm listening to you, Mark, I'm thinking Redux actually works more how React wants to work, I would say. I think that it matches ...
MobX React kind of just hijacks the whole ... Under the hood, it's doing a set state and it literally uses that property that you said about like, it does a set state on just a rerender and it just passes an empty array, because it's like I know it's going to always rerender if I pass it an empty array. It's always going to rerender. So this is how I trigger a rerender, and I'll take care of that. React, you don't worry about it. I'm going to hook in and do this hacky thing to make you rerender this component. But I'll tell you when to do that.
So it's sort like it's mapping on top of React. It's putting a layer for the state management side of things. And that is one thing where I'm kind of like, "Well, why are we using React then?" Solid makes way more sense, because it's sort of like going further. It's like, hey, let's take the MobX the observability concept and let's build a framework around that rather than mapping this on top of something that's not really designed for. I would say Redux is more closely aligned to that.
Mark Erikson:
It's funny because it is and it isn't and it is. So both the original Redux core and Redux Toolkit are 100% UI agnostic. Now yes, the vast majority of our users use it with React, but you can literally use it with any UI framework or no UI at all. At the same time, we assume that our users are using it with React. And Redux was designed from the beginning with React integration.
The funny thing is like you mentioned that forced set state with a dummy value, that is actually literally how React Redux has forced updates all the way up to until version 8 when we switched over to the new useSyncExternalStore hook. But if you look at version 5, 6, 7, every one of those had some form of a dummy set state call just to force the rerender.
Jamon Holmgren:
We're not alone then. That sounds, yeah, that's funny. Let's talk mutability versus immutability, because here's another sort of, I don't know, could be a holy war in certain places. And the way I look at it is a lot of times it depends on what pain you felt in your career. If you felt pain from mutable objects, it's going to feel very weird to go back to that because you're going to be like, "I've had pain here. I don't want to go back to that." But yeah, I want to get your perspective on that.
Mark Erikson:
Sure. So this goes back to both the original inspirations and design goals question as well as the conceptual influences question. So part of a Redux's original design goal was the whole time travel debugging thing. And the way that works is the dev tools wrap the store, they capture each action that was dispatched. And in order to create the multiple different copies or versions of the state over time, they actually replay the actions in sequence with the real reducer.
And so if you go in and you click the jump backwards or the skip this action and pretend it wasn't dispatched, it's actually rerunning all those actions again minus the one that you skipped. And in order for this to work right, you need those reducers to be pure with no side effects and to produce a new result value every time. It also goes to the conceptual idea that Redux is about making your code predictable.
Reducers aren't just a neat party trick. It's the idea that the more of your app that you can write as these pure predictable functions that are just input in, output out, I know what will happen every time, the less of your code that you're wondering about like how does this stuff even work. Now real apps have tons of async logic and side effects, we know that. But if you can put more of your app code as these pure predictable reducers, it's less of the code that you have to spend time wondering about.
So immutability is ... And then the other factor is the UI layer. So we just talked about how React Redux depends on reference checks in order to determine quickly if the data has changed. And so that requires creating new references if you did actually update the data. So there's the UI implementation, there's the time travel debugging aspect and there's the, I want to know what will happen when I change this code, aspect.
So it's not just like mutability is evil, there are specific design goals that we're trained to implement and immutability is the best way to achieve those goals. And then yes, there is also in the cases of, well, if any part of the code can just mutate this data at any time, weird things happen. We've actually sort of been dealing with this at my day job.
So quick sidebar, Replay is a time-traveling debugger for JavaScript. It's really cool, you should try it out. The code base for Replay started as a literal copy-paste of the code base for the Firefox browser dev tools two and a half years ago. The founders were Firefox dev tool engineers at the time. And a lot of that code dates back seven, eight years. Most of it is React Redux. A lot of it was written in 2015, 2016, but there's also a lot of other design patterns in using that code.
And part of it is having these smart classes that wrap, say, like a value in your object that you're inspecting or a DOM node in your UI that you are inspecting. And as the logic and as our app has changed over time, those patterns have actually started causing problems for us. We received data from our back end that says, "Okay, your app is paused on this line of code, here's the variables that are in scope, here's the DOM nodes at that point in time." And we get it as playing JSON over a web socket.
But our client code then proceeds to wrap a lot of these raw objects in these classes that have methods you can call to ask for more information about the DOM node and they mutate themselves. And unfortunately, those classes end up in the middle of our Redux store. So our app is breaking the two number one rules of Redux, you don't mutate the data and you don't put non-serializable values in the state. And in all honesty, that has caused a lot of pain difficulty because some of these things change out from under us and we've been on a multi-month mission to try to eradicate that pattern from our code base. We're actually really close maybe about another month right now.
There's a whole bunch of reasons why Redux goes for immutability. That's not to say that mutability is always a thing that should be avoided and serving with an observable-based approach that is what you want, but that's why Redux goes that direction.
Jamon Holmgren:
Yeah, that totally makes sense. I appreciate you kind of going through that for sure. And it sounds like it's a lot more nuanced than people might think. There's more that goes into this discussion, more that goes into this decision than people might think.
We're long on time so I want to make sure that we get in some discussion about RTK Query, because that was a piece that we purposely left out because it was sort of like we wanted to limit the scope as it was. It was already a long episode that we were talking there. But that's actually a pretty big deal because even though there is like MST Query now, which is really cool and there's React Query, there's other options out there, fetching data is such an integral part of state management, and caching it and all those other things.
Where did RTK Query come from? Why did you decide to build it as another part of RTK? And then just tell us more about it.
Mark Erikson:
Sure. So going back to the original design principles thing and we said Redux was designed in 2015 when the problem people were trying to solve was, my notifications are getting out of sync on the client side and I want to make this data flow predictable. And immediately, the other thing people began to do with Redux on day one was I'm going to fetch data from the server and put it in the store. And even the old Redux tutorials before I rewrote them showed an example of fetching data for subreddits or something. So this has always been a thing that you could do to Redux.
What I started to see was starting around 2018, there was this ecosystem shift from people wanting to manage state to the idea of data fetching and caching. So you had Apollo and GraphQL. You had React Query, SWR, urql. And even within the last couple years, one of the big sales points for Remix is that it allows you to bypass worrying about how you fetch data on the client side.
So that's really been a big deal, and part of the discussion about people saying, "Well, you don't need Redux anymore," and this is the thing I've said in other discussions is if the only thing you were using Redux for was to store in cache server state and you choose to use a different tool to solve that problem, well then congratulations. Yes, you don't need Redux anymore. But that's not the only thing that Redux does. It's a thing that you could do with Redux.
So, late 2019 ... Late 2020? Late 2020 ... one of my co-maintainers, Lenz Weber, who has been utterly critical in all the Redux Toolkit work over the last couple years.
Jamon Holmgren:
Yeah, he's an incredible engineer.
Mark Erikson:
Honestly, I think he was bored and just sort of started playing around with this idea. And he and another maintainer, Matt Sutkowski, kind of started pitching in and trying to flesh this out some. I actually had no involvement with RTK Query at all during the development process other than going in and making a couple suggestions about like naming and some bits of API design and then helping to write some of the initial docs. It was all their work up through the initial release.
In fact, I actually did not feel comfortable touching any of that code until just within the last couple months as we've been doing some work for RTK 1.9. I finally had to go in there and make some changes and work on a couple new features, and I finally have a decent sense of how that code works. But even then some of the design decisions behind it, I have to run them all by Lenz first to see what he thinks.
Jamon Holmgren:
We might need to have him on to talk more RTK. I feel like RTK Query is like it's a whole another topic that we could deep dive into. It'd be a lot of fun.
Mark Erikson:
Yeah. I'll try to sum up some of the key points. So, it is a complete data fetching and caching solution. It is equivalent in use cases and capabilities to React Query, Apollo, SWR, urql. It does have some unique selling points and capabilities. So it's built on all the basic Redux Toolkit things, funcs, actions, reducers, middleware.
You can see all the actions in the dev tools. In fact, we actually have a special RTK Query inspector in the Redux dev tools that shows the query information in a better format. We've got some things like cache query life cycles where you can say, "Oh, we just fetched the first entry for this endpoint. Now maybe I want to do something like set up a WebSocket connection and stream more updates from the back end.
The approach of defining the endpoints upfront lends itself very well to code generation and we've got code gen utils for both OpenAPI specs and GraphQL schemes. And we also pre-generate React hooks. So the core behavior is, again, UI agnostic and there's people using it with View, Angular, Svelte. But again, we provide that React integration out of the box. And for every endpoint you define, you get your usegetPokemon query hook automatically generated for you with the right types.
Jamon Holmgren:
That's really cool. That's fantastic. And the tooling around Redux is amazing like you briefly mentioned with the dev tools. So there's so much there. I unfortunately have to cut us off because we're so far into this, but there's obviously a lot here. I would love to talk ... I nerd out about state management. It's one of my favorite topics. The reality is I'm a backend dev at heart and I'm just in a frontend world, so I find the most backend looking like thing, which is pretty much state management.
Thanks so much, Mark. I really appreciate you coming on. This has been fantastic. Really, really great insight from you on the stuff that's happening there. If people want to learn more about Redux or have questions or whatever, what's the best place to find you?
Mark Erikson:
So the best place to start learning is our official docs at redux.js.org. So we've got those two different tutorial paths for people who either want to jump straight into Redux Toolkit as a default, which is what we recommend or learn how it all works the fundamental concepts from scratch. In terms of asking questions, we have a Redux channel in the Reactiflux Discord. Lenz and I and a bunch of other folks hang out there on a daily basis and spend a lot of time answering questions. We also are pingable on Twitter, and we'll occasionally check in on Stack Overflow and stuff like that.
Jamon Holmgren:
Perfect. Thanks so much. You can find Mark, @acemarke. You can find me, @jamonholmgren, on Twitter. You can find Mazen, @mazenchami. Sorry, he wasn't as involved. We had some audio issues during this episode. Appreciate, Mark, in helping carry us forward on through all of our issues. You can find React Native Radio, this podcast, at React Native RDIO.
Again, thanks to Mark, really appreciate and not only for joining us but also for all of your work on Redux. We use it here at Infinite Red all the time, so your work, your documentation, your help has definitely helped and improved our lives here at Infinite Red. As always, thanks to our producer and editor, Todd Werth, our assistant editor and episode release coordinator, Jed Bartausky, our designer, Justin Huskey, and our guest coordinator, Derek Greenberg.
Thanks to our sponsor, Infinite Red. Check us out at infinite.red/reactnative and make sure you subscribe and also go give us a review. We don't ask for reviews enough and so I'm trying to do that a little bit here, maybe have a few more than we do.
Unfortunately, Robin is not here. She had some construction at home, so she skipped this recording. So we don't get a Robin's mom joke this time. We'll, survive somehow. We'll see you all next time.