Joel and Tanner Linsley chatted about why state management is such a big issue when building UI's, the motivation for creating state management tools, how server State has inherently different problems from UI state and therefore needs to be managed differently. Additionally, Tanner gave a walkthrough of how React Query manages query caching and uses a serializable array or "query key" to do this.
Creating open-source software.
This blog expands more on the motivation of working on open-source software. ⤵️ https://tannerlinsley.com/blog/the-similarities-between-open-source-work-and-running-a-tech-startup
Finding the right tools! An example of this is building React Table.
1. Finite State Machines A finite state machine is a mathematical model of computation that describes the behavior of a system that can be in only one state at any given time.
2. Statecharts Computer scientist David Harel presented this formalism as an extension to state machines in his 1987 paper Statecharts: A Visual Formalism for Complex Systems.
3. Actor Model The actor model is another very old mathematical model of computation that goes well with state machines. It states that everything is an "actor" that can do three things:
There are various categories of state, but every type of state can fall into one of two buckets:
1. Server Cache - State's stored on the server, and we store in the client for quick-access (like user data).
2. UI State - State that's only useful in the UI for controlling the interactive parts of our app (like modal isOpen
state).
Custom Hooks in React is helpful for much more than just managing the local state and one-dimensional side-effects you see in almost every React Hooks example.
They can be used to build sophisticated memoization pipelines and chained effects that automatically manage local and network resources.
But most importantly, they provide a new layer of abstraction to accomplish new and amazing patterns that we couldn't have just over a year ago.
On the surface, this abstraction layer is simply a collection of custom hooks, but when unlocked, it can be the most powerful piece of your application architecture.
Query Keys are serialized deterministically.
This means that no matter the order of keys in objects, all of the following queries would result in the same final query key.
Query keys get passed to the getQueryArgs
function and then it determines the arguments by using the queryKey.
Utils.js
Next, it passes the queryKey
to the useBaseQuery
function to send it off to buildQuery
. useBaseQuery.js
Finally, it's passed to the buildQuery
. queryCache.js
If we take a look at config.js
we'll see that the simplest form of a key is not an array, but an individual string. config.js
When a query needs more information to describe its data uniquely, you can use an array with a string and any number of serializable objects to describe it.
This is where the stableStringify
is used.
This contains a JSON.stringify
function but with a stableStringifyReplacer
that will check if any values are objects. If they are Object, it will sort the keys and remap them back.
"Optimize for deletion when you write your code because if you don't it's easy to end up needing to grind out large rewrites, which contain massive risk."
From Chris Biscardi's Digital Garden: If you can't delete code then you're stuck with it
Implementation Suspense to React Query wasn't hard.
You can set either the global or query level config's suspense option to true.
Its handle by the handleSuspense
function. Github
People are building the same thing, they are not really worried about changes coming to Suspense.
Joel Hooks: [0:00] One of the things I'm curious about is why state management is one of the biggest struggles when we're building UIs. Maybe not small ones, but as they get complex, it always gets into state management. Why is that such a big problem?
Tanner Linsley: [0:22] Just generally?
Joel: [0:23] Yeah. Why is that an issue in UI development, or web development, specifically?
Tanner: [0:30] That's a tough question. It gets difficult...For some reason my mind is going to the way that the DOM works out of the gate with web development, and the way that the DOM works being like these mutable nodes and everything. Part of that, to me, is what drove React to come to be, is to make it all declarative and get rid of all the jQuery style of programming.
Joel: [1:02] You end up with big long JavaScript files that would go on forever and being madness.
Tanner: [1:09] State was a problem even before all of these frameworks.
Joel: [1:12] That's a problem with Flash, and J2EE development, I would assume, has a state issue.
Tanner: [1:21] A state is the expectations the user has for how the application should be working. The constant battle that anybody starts to fight as soon as they introduce any state into their app is like, "Is what the user's seeing exactly one to one what the state internally actually is?"
[1:47] Even before all these frameworks, I'm sure there was a ton of issues. If you updated some state, but didn't go and update some node, or fire some callback, or something, then you'd get out of sync with your state.
Joel: [2:04] Then you lose trust too. If your layers displaying isn't reality, then a user doesn't trust what they're seeing, and you can't have that.
Tanner: [2:13] Absolutely. You, as a developer, you start to distrust your application too. You're like, "Oh, my gosh. I can't even work on this reliably anymore because my state is getting whole out of whack."
Joel: [2:24] I was on Twitter yesterday, and there's DM notifications. I go on and open my DMs, the DM notification goes away. I close it out, there's a DM notification. I go in, there's no new DMs. Notification-like counts. It was discombobulating, and something was wrong, and you just don't even know what's wrong.
[2:45] That was a state issue. There is some cache somewhere. It was probably doing some business on him, but it was almost maddening to look at. I had to step away from the whole thing. That's probably a good thing to do anyway on Twitter, but it's just weird when it starts to go out of whack.
Tanner: [3:02] Also, as a developer, it's maddening, but as a regular user, it's just confusing and frustrating. We kind of know, "Oh, I might know what's happening right there. It's something with state." To a regular user, they're just, "This product sucks."
Joel: [3:18] If you're showing a brochure, if you have a Web page that's a brochure, like hyperlink things, and now we have Web applications, where you're doing banking, and you're doing real work. There's this critical need for consistency across the board, which seems important.
Tanner: [3:39] Yeah, and generally, the Internet has lagged behind a lot when it comes to managing state. You could get David going on this forever about how state machines have existed forever, but they haven't existed on the Web for a long time.
[3:58] For better or worse, all these Web frameworks, and a lot of the Web technology is being driven by people who don't have the experience that the rest of the programming world has had since the '60s and '70s. We're still learning, and that's part of the process.
[4:15] [crosstalk]
Joel: [4:17] I did Flash before this, and you've mentioned David, and he does XState. I wrote a bunch of unit tests for state machine when I was doing Flash development 11 years ago, and it's almost full cycle.
[4:33] Flash got killed, and then all that went away, and now the Web had to catch up. It almost feels it's come full circle back to that, which is neat to watch. I imagine developers that have been doing it longer than me, a decade longer than me, feel the same. I don't see it ending anytime soon, anyway.
Tanner: [4:53] I don't either.
Joel: [4:53] We don't have it figured out. I hope not. I hope this isn't like the pinnacle of developer experience.
Tanner: [4:59] Oh, no way, I hope not, because every day, even today, I just look at the tools I'm using, and a lot of them are the ones that I built. I'm like, "Oh, man, I hope this gets better."
Joel: [5:11] I've seen it, the TanStack. You are, it seems perennially dissatisfied with tooling and you keep making your own tools. What's the motivation for...I mean, I've seen your tools and it's all very pragmatic even and they're great. I'm wondering, what's the motivation not to just use something off the shelf and be satisfied with it?
Tanner: [5:35] Actually, before I answer that, I got to shout out to Shawn Wang, swyx. He's the one who was, in a DM, he's like, "Dude, you got to call it TanStack."
Joel: [5:43] Yeah, yeah. That's where I've seen it. It's exchanges between y'all two. I think it's great.
Tanner: [5:47] I was like, "Aw, Shawn, that's funny. Actually, I like that. I'm going to use that."
Joel: [5:51] It's up there with Jamstack for me.
Tanner: [5:58] I got into programming together with open source software, I guess. Part of what got me excited about programming was GitHub and open source. I thought it was really cool. I was like, "What? Free software?"
Joel: [6:11] Yeah, it's cool.
Tanner: [6:12] It's crazy. Part of that is maybe just this inner obsession with writing open source software. I just love it. The actual decisions that go into making these libraries is, most of the time, I will reach for a tool, and if it's the right tool, I will use it.
Joel: [6:35] Why not just use ng-grid instead of making React-Table?
Tanner: [6:39] Right.
Joel: [6:41] You could leave it there. That's fine.
Tanner: [6:42] What's crazy is I did use ng-grid.
Joel: [6:44] Imagine. It would be the first thing that I would think, like "Oh, I got data grid. We're going to go for ng-grid because..."
Tanner: [6:49] I used it for a long time, actually. I used ag-Grid too after they rebranded. It's not that I just, "Oh, I'm not going to use anybody's stuff." I use stuff until it starts to get in the way, I guess. A good story for me is...Actually, React-Table is a great story. I started with ng-grid, and it was fantastic.
[7:16] Then I moved away from Angular into React. I was like, "Oh, crap. What am I going to do now?" Then I'm like, "ag-Grid." I was like, "Oh, it's agnostic. Sweet." I used ag-Grid for a while and even put a couple of PRs. I'm sure none of them got accepted, but whatever. Until it got to the point where I was like, "You know what? I just want more of a native React experience."
[7:42] I was trying to do some things with React and state and JSX that just weren't meshing right. Maybe now those things are probably solved and everybody's happy, but at the time, I was like, "No, you know what? I'm going to build my own little in-house thing."
[7:57] I built what is now known as React-Table, but it was in-house at my startup for probably six or seven months before I even decided to open-source it.
Joel: [8:08] You were using it. It was battle-tested by the time it came to the world.
Tanner: [8:11] Yeah. A lot of what I do for open source is driven by what my startup, Nozzle, needs. I didn't wake up one day and be like, "I'm going to build a grid." It was because I needed it for Nozzle. I needed to show these data grids for our reporting dashboards.
Joel: [8:29] I've been on a project that's built a data grid from scratch. It's not for the faint of heart.
Tanner: [8:36] No. It's not.
Joel: [8:37] Especially when user needs start coming in and "Can we just have this cell do this?" Honestly, you want to circle it back to state management. There's a lot going on in a full-feature data grid.
Tanner: [8:54] There's a ton of state. There's a lot of derived state and computed state that is somewhere in the middle of "Well, yes, the user has provided state for this, but we're also augmenting it in a way."
[9:09] It gets really complex. The latest version of React-Table is basically just like a state manager, an event manager for tables, if you will. There's no markup in it.
Joel: [9:23] It doesn't give you an actual table. That's up to us.
Tanner: [9:25] No. It has prop-getters. It ships with "Oh, here's some default props that you're going to attach on to things." You can put those on whatever you want. Honestly, I saw somebody saying that they were using React-Table to power a chart, or like a D3 thing. I was like, "What?"
Joel: [9:42] That's what I was just thinking. I was like, "You could just take that," and you're just running data at that point. If you think of Excel and data in a grid, and Excel being the pinnacle of a data grid...
Tanner: [9:53] Yeah, rows, cells, headers.
Joel: [9:54] Then you can just parse it and do all sorts of weird...That's cool. When people do the unexpected with your libraries, that must be kind of fun.
Tanner: [10:04] It does come back to state though. People might be surprised in React-Table. I'm just using set state in a lot of places. I use reducer in others. Nothing fancy.
Joel: [10:22] When we talk about state management, I don't know if you've thought about this, but what are the big ideas around state management? Go across platforms and libraries and maybe the timeless structures in state management.
Tanner: [10:38] One that I very heavily agree with, at least in spirit for now, is what David preaches about having state machines be these intelligent things that can only be in a single state at any given time. I love reading all of David's literature. Just everything he talks about with state machines and XState. Not because I use XState. I don't even use it very much at all.
[11:17] The concepts that he's talking about are super sound and solid, that you never want to let your application be in multiple states at once for any reason. If there are multiple states, there's theoretical ways to track those individual states.
[11:45] All of David's XState stuff is built around that concept fully, but it's not difficult to use a lot of those same principles with whatever platform or whatever you're in. Instead of a bunch of Booleans, use an enum. If those enums have substates, then make those enums.
[12:06] It's this tree of possible states and making sure that you can only get to next states from the right state that you're in type of thing. That stuff is pretty universal wherever you go. The implementations are what change a lot.
Joel: [12:22] Did you see he posted a Tweet-sized state machine the other day? It fit in a Tweet. Like you said, this is a fully working state machine in a Tweet.
Tanner: [12:29] No, I didn't. I can't believe I missed that.
Joel: [12:32] It was pretty amazing. I'll shoot it to you.
Tanner: [12:35] I definitely want to see that.
Joel: [12:37] I just have to roll through it and find it again. It was like, "Wait, what?" It was quite cool.
Tanner: [12:41] That's awesome. That's one of the big ideas. Whether it's driving actual state machines you're using or just driving the way that you approach your state. That's one of the cross-cutting ideas of state management.
Joel: [13:02] That's interesting to me because it's the math. You can derive state from...It's probably all application...Like the universe is a finite state machine. Any application is going to be one. It's just a matter of...If you're in Angular and let entropy take over everything. I'm going to share my screen and show you this Tweet.
Tanner: [13:24] Yes, absolutely. I've seen something very similar to that in one of Kent's blog posts, too.
Joel: [13:36] Kent was in this somewhere. We were talking about no libraries, nothing. This is compatible with the XState visualizer. That was what was phenomenal. Kyle Shevlin was like, "You could do it even smaller." He's like, "It's not compatible anymore and I'm not interested in that." This is compatible with the visualizer. That's pretty amazing.
Tanner: [14:00] It's that optional chaining that makes it tweet size.
Joel: [14:02] Yeah, it is. I saw those question marks and I was like, "Wait, what's...That's right."
Tanner: [14:07] I'm happy they're doing that.
Joel: [14:09] That's something I can use now. I haven't used one yet. I have not used an optional...
Tanner: [14:13] Really? I use them everywhere.
Joel: [14:13] I use them in Ruby all the time. It's something I've had in Ruby since I've been doing...For whatever reason I ended up as a Rails programmer because of Egghead. It's really stuck there.
Tanner: [14:26] I'm using them everywhere and I agree.
Joel: [14:30] When somebody's out and it's like, "I got a new app. I need to build it." The Internet says Redux is dead. I can't do that. I talked to Mark yesterday. Redux Toolkit actually. That's pretty cool.
Tanner: [14:43] Mark is great.
Joel: [14:44] He is. I'm curious, when you're out there...We'll get to your ultimate answer. What should people be asking their state management libraries to do for them if React isn't enough? We know that. We've determined it. I don't know. How would you even know that? How do you know if React's enough and how do you know when it's time to bring in some heavy hitters to help you out?
Tanner: [15:15] It's a good time to talk about the other globally cross-cutting state management thing. That's big for me. Mostly just laying down the foundation with the last one. The one that I get into is the server state, server cache versus UI state. That's a big one for me.
[15:40] I just had a talk at React Summit just two or three weeks ago about this very thing. It's not global state. It's server state and UI state. They're very different things. Once you make that distinction, a lot of questions become clear when you're developing an application.
[16:05] For me, if it's just UI state...When I say UI state, I mean just local in-memory, the type of state that gets killed every time you refresh your browser. It's synchronous. It's just not remote. It's right in your app.
[16:22] If you're talking about UI state, I honestly haven't found a need to go outside of React. I can use Context and useReducer. I can make multiple versions of those. I've never had a problem. I have components that are re-rendering every 60 frames per second, and they're just using React Context.
[16:46] I'm a big believer that if it's just UI state, you don't need to go anywhere else unless you choose to. If you want to go with Redux because you like the DevTools or because you like one of the cool little middlewares that it has, then that's cool.
[17:07] I always tell people server state and UI state are very different. When you start introducing server state or server cache into your application, that's when I'm going to reach for a tool. I built a tool I'm going to reach for. It's called React Query. You can reach for other tools. There's Apollo. SWR is very similar to React Query. There's a couple others, but those are the big three for me.
[17:40] The reason I think that is because keeping track of local state is pretty easy, in my opinion. It's just set this mutation here, re-render the app. Oh, I have a slow re-render, make it faster.
[17:54] [crosstalk]
Joel: [17:54] You have complete control too, right? For the most part...
[17:58] [crosstalk]
Tanner: [17:59] It's predictable. As long as you have built it to be predictable, you can control everything inside of your app. It's there. It's synchronous. It's in memory. The server state and server cache stuff, it's like people don't realize it.
[18:13] When they start consuming data from outside of their app, they're no longer in control of that data. They are just reading a snapshot that an API endpoint gave them. A lot of people like to treat that data like they own it in their application, but they don't.
[18:31] There's a good reason that GraphQL went with a query and mutation nomenclature, because you're getting a snapshot of data from the server. Then you are triggering a mutation that you hope will do something. It's going to change something on some server somewhere.
[18:50] That's hard to keep track of. That talk that I did a week or two ago, I wish I could just dump that right into this segment.
Joel: [18:59] Is it online yet? I'll find it and check it out.
Tanner: [19:02] It's not public yet. I have an unlisted link to it that I can send you after this. It's great. Honestly, I don't even want to spoil anything. Just watch that video.
Joel: [19:19] I look forward to it, because we talked about that. I was at JSConf Hawaiʻi. It was at the beach probably when you're giving your talk, which sounded like a common theme based on the...
Tanner: [19:30] Yeah, it was a little more...
Joel: [19:32] Sorry, dude. It's Hawaii. What are you going to do?
Tanner: [19:32] It was a little more about React hooks itself. I was talking more about the UI state of things at the very beginning. Then I got to the server state.
Joel: [19:42] You closed with "and I write a library called React Query." It was like a to-be-continued kind of framework.
Tanner: [19:46] Yeah, it definitely was.
Joel: [19:49] It was entertaining and enjoyable discussion. I liked it a lot.
Tanner: [19:55] Full circle, can you ask yourself a question like, "Hey, this piece of state, who owns this state? Do I own this state in my browser, or is this state owned by something else? A server, another user, whatever."
[20:10] If you can answer that question correctly, then you'll probably know "Which tool should I be reaching for?" Also, "Can I do it myself? I probably shouldn't do it myself." That's a pretty good indicator of what you should do.
Joel: [20:29] You did do it yourself, right? You could've reached for a tool, and that kind of segue. You built a tool. React Query is exactly that, the idea that you need to...One, you didn't go GraphQL. I assume there's reasons for that, probably good ones.
Tanner: [20:46] Few people know that I actually did.
Joel: [20:48] OK.
Tanner: [20:50] In the very beginning, I was using Redux and trying to use Redux to be this server cache of data.
Joel: [20:58] A lot of people tried to do that.
Tanner: [21:00] Oh, man. I can't tell you how much code it took to do that. Then that turned into middlewares. Then I found other middlewares. If I knew that I was swimming in reducers just to handle a server state...A lot of it is repetitive. Like for every asset, something's different.
[21:17] Caching is just hard. When hooks came out, I just moved away from Redux completely and just built stuff out of context. I was like, "Oh, I need something to replace Redux. What am I going to do for server state?"
[21:34] I did keep my server cache in React State for a long time like I did on my other UI state until I started seeing these issues around it. I was like, "Ooh, this is really painful. What should I do?"
[21:46] For a few months, I tried out Apollo. I moved my entire API, everything. I moved it all over to Apollo. I set up everything. I had it working, even subscriptions. I was like, "Wow, this is neat." I don't like GraphQL right now. Even after I had it all set up and it was working, it was better, but it still wasn't what I wanted.
Joel: [22:12] I've spent seven years building a delightful REST API. We also have a GraphQL API. I still love it. I love a good REST API.
Tanner: [22:24] Also, I looked at Apollo and how much it latched on to my application. I don't know if they're going to see my video, but I like putting my fingers through the camera. I feel like Apollo just got into everything. In my application, there were links and hookups everywhere. There were special little things. Also, the bundle size was huge.
Joel: [22:49] They're a venture-backed startup.
Tanner: [22:52] It still is huge, from what I hear. It worked, so that was great. I was like, "I wonder if I could get this Apollo experience but just without Apollo." I started looking at what made Apollo tick.
[23:05] I found a couple of other libraries, just one that really inspired me. It was called Dracqula. That inspired the very first version of React Query internally, which looked almost exactly like Dracqula, but it just didn't have any GraphQL stuff in it.
[23:28] Then after using that internally at Nozzle for a month or two, I saw the patterns. I saw the problems that needed to be solved and just rewrote everything with this custom caching engine that would then feed into these hooks and this Cache API that eventually became React Query.
Joel: [23:55] You weren't trying to invent a Cache API from scratch. You were inspired by [inaudible] and had those. I mean, it's notoriously one of the hardest things. It's like, "I'll spend the full day and finally maybe I'll check the cache."
Tanner: [24:09] In reality, the external API shape is the only thing that I took inspiration from from Apollo and from Dracqula. Everything internal, I did write from scratch, mostly just because I knew it wasn't going to be anything related because it's all non-GraphQL. It's just REST.
[24:29] I had to make it agnostic. It didn't have a schema. I had to design this system for doing query keys, which is what you see today in React Query where you can have these complex arrays of primitives that will just magically give you this awesome caching key.
Joel: [24:47] Was that fun?
Tanner: [24:48] It was a lot of fun. I can show you the code.
Joel: [24:53] Yeah, it kind of sounds fun.
Tanner: [24:55] I can show you the code that does it. It is freakishly simple.
Joel: [24:58] I'd like to see it, actually.
Tanner: [25:00] I would love to show you. Let me pull that up.
Joel: [25:04] One of the things that is cool about your open-source libraries is the time, care, and attention you put in your READMEs. I just want to say that. It's really nice.
Tanner: [25:14] Oh, thanks.
Joel: [25:15] I love it when people take that extra effort to make something beautiful at the README level. I don't even have to leave the site to go to the official whatever URL. I can just read the README and be in my comfy zone.
Tanner: [25:26] I like that. I've had a couple of people. They're like, "Please do a doc site." I'm like, "No. I like this."
Joel: [25:32] You have. It's right here. It's called the GitHub repository.
Tanner: [25:34] This is the doc site.
Joel: [25:35] I like it.
Tanner: [25:36] If it doesn't suffice, then I'm going to tell GitHub that it needs more tools. All right. It's funny. I was just looking at this for something else. I was just stealing some of these functions to do something else.
Joel: [25:47] Oh, nice.
Tanner: [25:50] I'd love to do this. Let's follow the query key as it goes through React Query. Normally, the query key would be the first thing that comes through on these args.
Joel: [25:59] What does a query key look like? I don't even...
Tanner: [26:02] Query key looks like...You do like useQuery. Then it could be like this. It could be just a string. It could be a complex array. It could be like todo. Then you can have like an id. It would be like 5. You could do something even more complex. You could do todos. You could do an object and say status ending.
Joel: [26:31] Like a filter?
Tanner: [26:32] Yeah. Date like from or whatever. You could do a date. They can be these complex things.
Joel: [26:46] Similar to the query language that we're using with GraphQL, like in...
Tanner: [26:51] A little bit.
Joel: [26:52] You're making a query, right? You're able to structure it and...
Tanner: [26:55] Actually, this is just the key. What this does is it's saying, "These are the things that make the data that I'm asking for unique. The function right here is what's going to go get it. It's going to be like Axios endpoint. Then you're going to put these params in here like this.
Joel: [27:22] Oh, sweet.
Tanner: [27:25] Let's write it exactly how it would be. If these were extracted, this is what it would look like. Oops, function. Let's call this query funk. Let's call it todos.
Joel: [27:44] There you go.
Tanner: [27:46] Inside of here, every single one of these query keys get sent through here. You would have a key which would be this todos thing. We don't need that, so we're just going to underscore that. Then we would have this status and after. Then we would go and use those so we could do todos. Now we could do status after.
[28:20] The key is what tells React Query like, "This is what makes this data unique." If you use this query again somewhere in your app three times, it's going to be able to dedupe those and join those together. Just use a single cache to back off three of these usages, because the keys are the same.
[28:41] Then you get those keys in your query function. You can go and grab your data however you want as long as it returns a promise and throws an error. That's the only interface. Technically, you could even use GraphQL in here. You could do some GQL request or something like that.
Joel: [28:57] Yeah, it doesn't care. You can toss whatever in there.
Tanner: [29:00] Yeah, and you could just post in a query or something like that and be like, "Hey, go get me todos."
Joel: [29:10] React Query is absolutely totally agnostic in terms of your implementation, how you get data, where your data comes from.
Tanner: [29:19] As long as it resolves data, you're good. It would resolve these things, but under the hood, I need to know for query keys if somebody passes...If you're using a query four times on the page, I need to be able to match those up. What if you pass status first and then like that?
[29:47] Technically, you mean the same thing, but it's just not in the same order. I had to be a little smart about how to structure these query keys. This is what it does as query keys get sent through to this getQuery args function.
[30:07] If this goes through, it figures out, "OK, what are all the args?" At the end of the day, it needs to be a query key. Then it uses this base query function. Here's our query key. It sends it off to this buildQuery function right here.
[30:24] Let's go to the query cache, buildQuery. All right. Finally, we're going to do something with the query key. It uses the query key serializer function, which you can override in your config. I don't recommend doing this, but you can do it.
[30:43] The query key serializer function returns a hash and then the query key again, but a little normalized. If we go look at config, the default serializer looks like this. It's got this function support so you can pass a function that will return the key or throw if it's not ready.
[31:04] You can return the simple string. Remember how I said if you do use query todos or you can pass an array? It turns out that these two things are exactly the same. This will get forced into an array at the end the day, right here. It says if it's just a string, force it into an array.
[31:25] Then I use this stable-stringify function. All it is is a JSON.stringify, but it has a replacer in it. It will go through and check to see if any values are objects. If those are objects, then it will sort the keys and remap them back. It does that before it gets stringified.
Joel: [31:51] You're solving that problem you showed us earlier where you could switch the value or the keys around, and we want them to be consistent and guaranteed.
Tanner: [31:59] That's it. That's really all it is. It comes back stringified and then that stringified hash is used to key the entire cache. It's pretty simple.
Joel: [32:14] That's it.
Tanner: [32:14] Yup, that's it.
Joel: [32:21] I'm curious. I see handle Suspense in there. That's coming down the pipe. Suspense is here but Concurrent Mode and that sort of thing. How do you think about that when it's something that it feels like three people in the planet know where we're going to end up with that?
[32:41] Is it something that you can work around, because you're not relying on the cache? It feels like that was the thing, just like the big warning. The cache is going to be the thing's that going to change.
Tanner: [32:52] Because React Query is backed by some cache, it makes it Suspense resilient out of the box, to be honest.
Joel: [33:00] That's what I was thinking. The cacheing was what they were worried about. I hear Dan and them talk about Relay all the time. I'm like, "Does anybody use Relay?" I know Gatsby uses Relay under the hood, and there are people that use Relay. I don't know anybody that's doing apps outside of Facebook, personally.
Tanner: [33:22] To me, I look at Relay and I'm like, "Wow, this is a lot to take in." It feels like overkill.
Joel: [33:32] I'm not making Facebook. That is a complex app so it's like the official...
Tanner: [33:37] Again, it feels like something that's going to get into everything in my app. It's just going to lock me down. Everything I do in my app is going to have to be this world.
Joel: [33:48] It's a Relay app or an Apollo app or it's like the whole thing, now you're building...I'm not going to build a React Query app. I don't get that feel from what you put out here.
Tanner: [33:56] No.
Joel: [33:57] I'm going to have a React app and I'm going to use React Query.
Tanner: [34:00] That's another thing too that you have helped me help articulate here is that when I build libraries, I don't build them so that they feel like you're using that thing. I want it to feel like a natural extension of just React. I don't want it to feel like a thing. That's why all my libraries are React-thing. I don't want it to be this huge lock-in.
Joel: [34:26] Chris Biscardi gave me this concept. He didn't invent it. There's this idea that you plan for or you optimize for deletion. I want to optimize so I can I just remove this and replace it with something else entirely, and I'm not going to have to spend six months reconfiguring my entire app.
[34:46] Something new has happened and so we can just change and iterate. You can delete and not live in that fear. You build an app in a lock-in, it can be horrible. This idea of state management is something where, because it can be so pervasive, it's one of those things where it feels like you can get into that trap pretty quickly.
Tanner: [35:09] It's like Suspense. To enable suspense mode, it honestly wasn't that hard. The useQuery hook, if you saw -- I probably don't want to save that in there -- it uses this base query.
[35:28] Then I have this handleSuspense function that essentially...Let's see, where is this handleSuspense? Here it is. All it says is, "Hey, if you're using Suspense or you want to use an error boundary and there's an error, then throw the error during rendering."
[35:48] If you're using Suspense and we are in a hard loading state, then tell the query that going to be suspended, and then just throw the promise to re-fetch the query. Everything else is just cake. I don't have to do anything else other than that.
[36:13] When Suspense, when they come through and say, "Guess what, everything's changing. You don't throw a promise anymore. You throw this weird interface that we are exporting from the React library now. It's got all these cool things." I'll be like, "OK, I just have to come and change it right here, and I'm done."
[36:34] Unless they change, that's going to go a completely different direction, which they're probably not going to do. It works. It works today. It works great. There's a few, little edge cases that we're still figuring out. Who's perfect with Suspense yet anyway? Nobody's perfect with it.
Joel: [36:52] It's literally impossible to be. One, I don't worry about it too much. I deal with that. I mostly work on simple apps. I'd be a pedestrian user. Your edge cases wouldn't even come up generally for ordering or fetching data. I feel like most people are building...We all feel very special in our hearts, but our apps are all kind of similar.
[37:21] What are the problems you run into those edge cases? What are the big problems where you have to think about it for you in your startup? In your app, what makes it unique and drives some of this innovation and code that you're doing?
Tanner: [37:45] Nozzle, at least the part that I've been focusing on for the last few years, it's not very different from a lot of other apps in a sense that every single business has data. Unless that data is special in some way, then your business, why does your business exist, or the way that you handle that data?
[38:14] For me, it's just been about, "OK, we have this really special data, and there's certain characteristics about handling that data. We handle a lot of keyword data for SEO people, tracking a gajillion keywords. It's just this really niche space where there's a scaling problem of tracking that many keywords and managing all that. How do you do all that?"
[38:37] There's an entire API and CRUD engine backing all of that. Trying to navigate that engine that we've built with primitive tools was just a terrible experience, like trying to do that without great tools like React Query.
Joel: [38:58] At the end of the day you have to get up and want to go to work. You're trying to build a business too. In a way, you need to enjoy what you're doing so that you can proceed. I don't want to say make progress. I feel that way a lot.
[39:11] When you talk about developer, I've worked at jobs and I've worked on code bases where I'd literally wake up in the morning and dread walking to the computer because I knew what was in store for me throughout the day. Whether it's like your tooling or the code or your integrations, there's always a different layer that can make you sad.
Tanner: [39:37] Honestly, I'm glad that I don't suffer from that. I love going to work every morning. I love going, sitting down now to work every morning. For posterity, we're in the coronavirus times.
Joel: [39:56] I haven't left the house in seven weeks. That's why I don't have the mask on, and I can touch my face because I'm here.
Tanner: [40:03] People are going to be watching this in two years and be like, "What the freak are they talking about?"
Joel: [40:06] Exactly.
Tanner: [40:08] I'm so excited to wake up and just work on these difficult problems. Nozzle is nothing short of a very difficult problem to solve. I get my own special problems in the frontend world just because of handling those difficult challenges with Nozzle's data. I get to come up with some difficult problems. State management is just one of them.
Joel: [40:35] You layer that on top of being able to...Now you've open-sourced it and you have a library. I don't know how many people are using it but a lot because there's a distinct need for this solution right now. How many stars you got? What are you up to? Do you keep track on stuff? Do you care?
Tanner: [40:54] Sometimes.
Joel: [40:55] Metrics are silly.
Tanner: [40:59] It's up to 6.6, 7. Not bad. That's a lot. Clearly, I'm grateful for every star that the library gets.
Joel: [41:06] The point is you could've had this solution, solve your problems, and never shared it. One, you said that you've been motivated by open source since you started because it's cool. Free software is neat. Then you can.
[41:22] At the end of the day it's interesting to me because of the constraints and the design constraints it puts on top of what you're doing, because you can't just solve a specific problem. It needs to be general and broader I would think. That has to affect your thinking in some ways.
Tanner: [41:37] I built React Query to be able to handle a very generic problem of just solving server state. I didn't just build this in a vacuum. I tried not to. I involved a lot of other people, getting their feedback and making sure that what I was building was going to solve more than just Nozzle's problems.
[42:02] There's a lot of people who have been early adopters of React Query. One of them that I've been grateful for has been Kent C. Dodds. He and I talked almost from the very beginning about React Query and what it's going to be able to do or what it's going to look like. I'm happy to say that, at the end of the day, I made Kent happy.
Joel: [42:29] Nice.
Tanner: [42:29] That's not a difficult thing to do.
Joel: [42:33] He's pretty happy, he's a chipper guy.
Tanner: [42:36] He's very happy, but he has his opinions. He's very pragmatic and logical. I knew that if I could make Kent happy then I was going to have a library that was pragmatic and simple and just gets out of the way and lets you do your thing.
Joel: [42:53] Does SWR stand for stale while revalidate?
Tanner: [42:57] Yeah, it does. That's what it stands for.
Joel: [42:58] I thought it was like a Zeitism. Zeit or...They have a new name.
Tanner: [43:07] I need to update Zeit right here. I have Zeit still. It's Vercel.
Joel: [43:15] It feels likely that your libraries were, it was the twin false phenomenon where the two movies that seem like the same thing come out at the same time. Both of your libraries, I saw that and was excited for that.
[43:32] Then I heard about your library and whether that's...Probably, the coincidence of it is just the needs and then Suspense, and it's just a frustration with GraphQL maybe. I don't know exactly why they both exist.
Tanner: [43:47] It's funny you say that because I think of that analogy all the time. I love the movies, "The Illusionist" and "Prestige."
Joel: [43:57] They're both great. Prestige is a...They're good. I want to watch the Prestige again.
Tanner: [44:03] They're both really good, and they're different enough. I like them both for different reasons.
Joel: [44:05] Was it "Armageddon" and "Deep Impact," that was another example?
Tanner: [44:09] No, and that's a great example, to draw a connection here. We didn't talk about building SWR and React Query at the beginning like, "Hey, let's go both build our own thing. Then we'll come back in a couple of months and see what we have."
Joel: [44:23] Very inefficient.
Tanner: [44:24] We both heard about each other's libraries at the very tail end of when we were about done working on them. We were like, "Aw. You too? That's cool. Let's share notes and make sure that we're both not doing something terrible."
[44:40] Everything was like, "Yeah, I like those ideas. I have my opinions, but I like yours too." We both launched and everything went great. They're there, and they happened at the same time, because the timing was right. The foundation was laid.
[44:59] It would be hard to do what we're doing, and get it to be as easy to consume, unless hooks were there. Having hooks there was a big deal.
Joel: [45:10] Hooks lands, you get another, whatever span of time between that announcement and then these libraries appear because now they can exist.
Tanner: [45:19] Having enough time to explore the breadth and depth of what hooks can do and what they should do, and how far we can push the limits of hooks. Once everybody became comfortable with those, including myself...I realize some people still aren't comfortable with them, but I was using hooks as soon as they were announced.
[45:46] I wanted to become familiar with them so that I could build tools around them. That's what happened. The time was right, the experience was there, and the patterns were there. We recognized the patterns for what they were.
[46:03] Part of that is a little bit of the hype cycle of GraphQL as well. GraphQL is super-hot still. I feel like we're coming over the crest a little bit into the trough where we're going realize, "Oh, you know what, we still have a lot of work to do with tooling, and we still have a lot of work to do with getting this to be widely accepted and easy."
[46:34] Part of that, for me, is just realizing that, "Hey, I tried it. It's not there yet for me. I'm going to keep using REST, because 99 percent of the planet still uses REST. I wanted to build a tool that can help 99 percent of the planet."
Joel: [46:50] I figured.
Tanner: [46:51] OK, 99 percent of React users.
Joel: [46:56] I was telling somebody the other day, because it's like people at Egghead and they're worried about what to teach. I'm like, "Look, statistically speaking nobody even knows HTML." It's fine. This has already been done. Everybody already knows this. I'm like, "Nobody knows anything."
Tanner: [47:15] No. I barely know the DOM.
Joel: [47:20] I'm learning every day.
Tanner: [47:21] I'm just working with abstractions.
Member comments are a way for members to communicate, interact, and ask questions about a lesson.
The instructor or someone from the community might respond to your question Here are a few basic guidelines to commenting on egghead.io
Be on-Topic
Comments are for discussing a lesson. If you're having a general issue with the website functionality, please contact us at support@egghead.io.
Avoid meta-discussion
Code Problems?
Should be accompanied by code! Codesandbox or Stackblitz provide a way to share code and discuss it in context
Details and Context
Vague question? Vague answer. Any details and context you can provide will lure more interesting answers!