Tanner Linsley chats with Kent about creating open-source libraries and Hooks for fetching, caching, and updating asynchronous data in React with react-query.
Kent C. Dodds: 0:00 Hi, everyone. My name is Kent C. Dodds. I am joined by my friend, Tanner Linsley. We've been buddies for several years now. Actually, Tanner was on "Chats with Kent" podcast, so if you've listened to that, we have already talked about react-query on there.
0:15 I'm excited to chat with Tanner about react-query and the other stuff that he's involved with. We also are using his library, React Virtual, and the performance workshop. Just excited to have Tanner here. Tanner, I introduced a couple of the things that you work on, but can you tell us anything else about yourself that you want us to know?
Tanner Linsley: 0:35 Yeah. Happy to be here. I have a startup I'm constantly working on, called Nozzle. As Kent already described, I've got a really weird obsession with building open source software. I obviously love all things React. Yeah, not much else to know.
Kent: 0:57 Cool. We got to know each other back in the Angular JS days when we were all over Angular JS. I don't know which one of us moved first. You may have moved to React first. I can't remember. I moved at the end of 2015.
Tanner: 1:16 I moved in 2015 as well, early 2015. You were playing around with it before me, though. I remember a lightning talk at one of the meetups locally here, where you're like, "I tried out this React thing," and it was fast. I was like, "Kent, what are you doing?"
Kent: 1:42 Thanks to everything, man. It was great. You've been busy for years, actually. You've been building open source libraries, and recently, you've been doing lots of Hooks related stuff. You've got a great talk. What was that talk called? It was at the Hawaii JS, right?
Tanner: 2:03 Yeah. That one is, "React Hooks, the Ultimate Abstraction Layer." I think that's what it was called.
Kent: 2:11 Great talk. Everybody should go look that talk up. It's a great talk. You've been just hammering out some awesome libraries on that based on React Hooks. Do you want to tell us about some of those things?
Tanner: 2:25 Yeah. The first one that I built was called React Table, and that was before Hooks even came out. When Hooks came out, it was the first one that I was like, "I got to refactor everything to go to React Hooks."
2:41 There's React Table which used to be a component library, you drop in an actual table component. Now, it's just a hook and its tendless, so you render your own table markup using React Table as utility. It's cool.
Kent: 3:00 Yeah, it's wild. I haven't used React Table, but I'm assuming that you're using things like prop getters and things to get props across the elements that people are rendering?
Tanner: 3:10 Yeah, prop getters. Don't coin the term Kent, but that's exactly what they are. I call them prop getters in the docs and everything. Using prop getters and there's a lot of plugin ask reducers everywhere, that you can customize the props for everything.
Kent: 3:31 Very cool. The idea of headless UI components was predated React Hooks and you were using render props before, or you said it was an actual component that you used, or what was it?
Tanner: 3:45 Before you would render a component, and then to override pretty much anything, there were prop getters before Hooks in the previous table component library, but it was called get props.
4:03 It was more of a reducer instead of a getter that you'd put on something. You could just go in, say, you know what? Get the props for every cell, and it would give you the props coming in, you could alter them on their way out.
Kent: 4:18 I see.
Tanner: 4:19 Then similarly, you could go in and override the entire component with a render prop. There was a render prop for every single core component in the table that you could override. You know how that gets messy.
Kent: 4:36 Yeah. Hooks cleaned up those types of abstractions, for sure.
Tanner: 4:39 Yeah. In fact, you did a poll a long time ago, right around when React Hooks were coming out. You were like, "What's the library with the most public component API?" I was like, "React Table." I think it took the key, it won.
Kent: 4:56 There was one date picker from Airbnb that had 100 props, including a config prop that was an object with just more options, which is basically just more props.
Tanner: 5:10 I had close to that many. It was huge. Now, there's only a few options.
Kent: 5:15 It's amazing. It is so cool, because you were able. Most of those things are typically render related stuff. I want to render this; I don't want to render that or whatever. You say, "I'm just not going to do any rendering," then it becomes obvious, because it's not even part of the API.
Tanner: 5:36 With prop getters, it doesn't mean you can't have any opinion over props and things that get rendered eventually. You can supply people with good starting points with prop getters.
Kent: 5:49 That's one of the cool things about prop getters and just composition, in general. You could take all those prop getters, so you have a prop getter for a cell. You'd say people using React Table, it's common for them to render a cell, it would not make sense for them to render a cell.
6:12 With Downshift, we've got use cases. Downshift is an autocomplete enhanced input thing and so you have the menu, you have the input, you have the list items. It's possible to use Downshift to build a dropdown so you don't have an input. For people who don't care about that, they don't need to say don't render an input, they just don't render an input.
Tanner: 6:41 Yeah.
Kent: 6:41 If it were common enough, then what you can do because the composition is, you'd say, I've got my base hook and then I have a hook, on top of that that takes care of the prop getters. People don't care about those prop getters; they just use the basic. The composition capabilities of this stuff are just mind-blowing. We could do this with render props, but it was not as fun.
Tanner: 7:04 It was very messy.
Kent: 7:06 It's awesome. That's React Table. What else you've got for us?
Tanner: 7:13 After React Table, probably React Query is the next big player in my open source work, which is relatively new. React Table has been around for probably three, almost four years now, but React Query has only been around for six to eight months. It's very new.
Kent: 7:34 It's taking on fast in the community. It's grown really quick. It's because you just solved an enormous problem. When I had first had my aha moment with React Query, which it'd been out like maybe two weeks...I tried it pretty early on.
7:56 When I had that aha moment with React Query, I was like, "Oh my gosh, finally, why did we not have this earlier?" We should have had this since the beginning. This is the solution. Honestly, if React Query had been around before Redux, I don't think Redux would have been nearly as popular as it was.
Tanner: 8:16 Yeah. That's very kind of you to say, too. I totally agree. Just because of the amount of people I see now that are ditching...I hate to say ditching Redux. They're leaving a majority of their code behind in Redux and moving into React Query.
8:36 It's not just replacing all that code with a magnitude of the fraction of the code, but it's also doing a lot more than what the existing code in Redux was doing before as well.
Kent: 8:49 Absolutely. There have been other async libraries like React-async, for example, that I had used in the past, and I even contributed a little bit to it. That's not what was fundamentally awesome about React Query and it wasn't even just the hooks aspects, which was enabling for React Query.
Tanner: 9:15 You could have done the catalyst, you would say.
Kent: 9:17 Yeah. You could have done a lot of the same stuff without React Hooks. It just wouldn't have been quite as nice an API, but you could have done it.
9:24 What's critically awesome about React Query is the cache. You give me all of the right knobs to turn for controlling that cache, it is just so powerful and easy to do the right thing. There are definitely some things that you could shoot yourself in the foot over with that.
Tanner: 9:51 Yeah.
Kent: 9:51 Caching is a hard problem. For the most part, it's easy to do the right thing.
Tanner: 10:00 A lot of that came down to just researching the defaults that need to be in there. There are a lot of tools that have all these same knobs, but maybe they don't expose them in a good way or you have to configure everything all the time.
10:16 With React Query, everything is just set up to be relatively safe to use out-of-the-box, and then gradually, expose more API and stuff to you as you need it.
Kent: 10:31 Yeah. It's just awesome. I'm so glad I have React Query. I use it on pretty much anything that I need to make more than a couple requests. The other day, I was working on a little side project and I didn't even use create-react-app for it. It was literally an index HTML. That's, the entire app is in there in a script tag.
Tanner: 10:58 I saw this tweet.
Kent: 11:00 I used React Query and a couple other things. I started with my own useAsync hook, which I probably should open source. People who are going through Epic React will see it in a couple of places. We build it, actually.
Tanner: 11:14 I've seen it and it's awesome. Everybody has something similar to that in their application similar.
Kent: 11:21 I think so too. I copied it over to my little project and I was using it, then I was like, "Why am I doing this?" I mean, useAsync is great for async things that don't need to be cached.
Tanner: 11:40 Right. Or just one-off request and stuff.
Kent: 11:43 Right.
Tanner: 11:43 In fact, it probably works great as a mutation type application.
Kent: 11:48 Yeah, exactly. In fact, I use async in the bookshelf app in combination with React Query for status updates and stuff like that. It works great, really cool. People watching this, if they haven't seen that yet, though, they'll see it and they'll be like, "That makes sense."
12:06 I was just blown away by how easy it was to just add React Query. Now, I don't have to worry about that anymore. I can focus on this other stuff.
12:15 It was just a small side project, but even in that small side project, React Query made so much sense. It's amazing to me how well it scales from basically, the tiniest thing I can imagine to something legit that has customers and stuff.
Tanner: 12:33 I'm glad you said that, because I started from the other end. Nozzle isn't exactly a huge application, but it does have a lot of different kinds of assets that we're requesting from the server.
12:46 We've got at least 10 or 11 CRUD objects that we are requesting, and so is moderately scaled from the beginning. To see that it scaled down to small solutions and worked great, it's great to hear that.
Kent: 13:03 Dude, it's so awesome. Let me gush over it just a little bit more. This little side project, we just have one resource that I'm pulling from a JSON server API and that's it. Then I have some mutations and things, and not even mutations. I have just network request that go do a thing. Is that RCP, whatever?
Tanner: 13:30 RPCs.
Kent: 13:33 The technical term for that. I just use mutations for that, it works great. The stale-while-revalidate, I'd like you to talk about that a little bit. That makes it so nice because I use it every day.
13:58 Whenever there's a change in the back end, I just open up the window and boom, it gets updated automatically. I don't have to have a button to refresh or anything like that. It just happens because all the right default's in there.
14:15 That's the other thing. When I first started using it, one of the options is to revalidate everything when the user comes back to the window. I thought, "Man, I'm surprised that's built-in." I feel like that would be something you'd want outside.
14:32 Honestly, the whole philosophy of React Query is that you cannot be confident that the cache is valid, and so we just embrace the staleness of the cache and we revalidate stuff. If it's the same then it's like, "Great, we didn't have to update anything."
14:56 I'd love for you to talk about the philosophy behind it, not trusting the cache and the stale-while-revalidate idea.
Tanner: 15:04 The stale-while-revalidate term is something that I didn't even know was a term, a real thing, until after I came out with React Query. I learned about that term from the people over at Next and Vercel. They have a similar library called SWR, which literally is an acronym for stale-while-revalidate.
15:28 I didn't even know about a lot of this stuff until after I had built it. I was happy to know that it's called...
Kent: 15:34 It's very validating.
Tanner: 15:36 I'm like, "It has a name, that's good." Because I want to make sure I was doing it right. It definitely started out with that change of assumption. I knew that I needed caching inside of my applications to make them behave quickly and have a good user experience. It all came down to that, having a better user experience.
15:57 I always talk with my colleagues, I'm like, "Caching is terrible and it's so hard to do." I can't remember one of the discussions that we were having, but I said, "Well, shouldn't we just assume that everything in the cache is just always out of date?" They said, "Well, it depends on how expensive it is to revalidate it."
16:19 I was thinking about that a little more, and I was like, "Well, it's not that expensive." All of our APIs that we interact with, they're getting hit every single time we reload our pages anyway. If you hit reload on your browser, you're likely going to hit all of those same APIs again.
16:39 If your website and your API isn't built to withstand people loading it and reloading it all the time, then you've got bigger problems. As long as it is, then we could technically just revalidate all of our assets all the time and make that assumption that you just mentioned, that everything is always out of date or potentially out of date.
17:04 If you start at that point and work your way forward to, I know more when some assets are not going to be out of date and you opt into those experiences, rather than very dangerously assuming that your cache is perfect and it's always up to date, and you have to opt into revalidation, which is much harder.
17:31 That's what drove the philosophy of React Query is exactly what you mentioned. Just assume that everything is either always out of date or potentially out of date, and then build an entire API around that experience of how do we let people opt into keeping data around longer?
17:54 How do we make it easy to invalidate our data, because we know we're going to have to do that? What's the easiest path to invalidation? That's what drove the entire public API surface area, which, over the last three major versions, has actually survived pretty well. It's not that different from how it started out.
Kent: 18:18 It's awesome. If you configure your server cache headers and everything, even when I make this request, and if nothing changed, then the browser cache will take care of that. You just send it three or four hours, it's, "Nothing's changed." I just think that's a brilliant strategy.
18:41 I had a question about, just the other day I was thinking, what happens if it sends back the same data that it did before so there's nothing that was actually changed? Do you determine whether components need to be rendered or will they be rendered just with the same data?
Tanner: 19:03 It's nice that we make the assumption you're working with JSON data, which is serializable.
19:11 In the first versions of React Query, in fact, even up until just two days ago, everything in React Query was just compared with a deep comparison model and said, "If anything has changed in this new version, then we're going to replace the entire object," so you can do deep object equality on the entire result.
19:36 That worked well for a long time to avoid re-renders. If you revalidate a big old list of users and then the new users come back and they're in the same exact order and they have the same exact properties, then nothing changes and your app doesn't re-render.
19:56 Then I had an amazing contributor to React Query, who has been helping a lot in the last few days. He wrote something that actually uses structural object sharing. What that means, it's a fancy way to say that we can detect partial changes between the old and the new JSON.
20:18 A good example would be if you have a list of users that you're revalidating. The new result comes back, and only one of those users has changed. Then what's going to happen is the entire users' list reference is going to change, just as it would immutably. Old users does not equal new users.
20:42 However, if you were to go in and compare individual users, so old User 1 to new User 1, they would actually be the same reference still. Then the user that did change would be old User 2 and new User 2, would not be equal because it's been changed.
21:05 It lets you get really fine-grained if you're doing any memorization to say, "Only memorize something when the user object changes from this big list of users." It's really impressive and it's something that is just invisibly there. You don't even need to configure it now. We just upgraded it.
Kent: 21:26 That's awesome.
Tanner: 21:27 As of two days ago, that's working.
Kent: 21:30 That's great. Very good. One thing that I've had a couple people ask on the Bookshelf app is that they'll pull up the terminal and they'll do a profile and they'll see that...or I have a profiler installed and it actually sends profile data to the back end.
21:56 They'll notice that when you load a page that's using React Query, there are actually a lot more re-renders than you expect. What are those re-renders, and should we be concerned about that?
Tanner: 22:08 Some of those re-renders have gone away even in the last few days.
Kent: 22:13 Because of that new change?
Tanner: 22:15 Not only just that change but just optimizing the reducers that React Query is using under the hood. Some of those have to do with initial data. If you were to provide initial data to a query, you'd actually have two renders before, where it would render once without data, and then it would use a layout effect and add the data back in.
22:40 You're not necessarily wasting ping time, I guess kind of a little bit, but those are gone now and you should see far fewer renders going through React Query now.
Kent: 22:53 That's great.
Tanner: 22:53 We even added the ability to, if you really don't want to opt into re-rendering a component because of changes in loading state, or data state, or something like that, you can actually modify which notifications you want to receive in the component you're using the hook to get even more fine-grained. But a lot of that has been improved out of the gate.
23:21 The promise that I like to give people with React query, and now it's more true than it was yesterday, is that the component will never re-render unless something has changed in the state that you rely on in your component. Loading state is fetching in the background, data changing. Those are the only things that should be causing re-renders in React Query.
Kent: 23:46 That's awesome.
Tanner: 23:47 The reliability is changes, that's very true.
Kent: 23:51 I figured that things would improve. That's what's nice about a library that's dependent on and by a lot of people. Is lots of smart people come into this thing and just make it awesome. You made it work, make it right, make it fast, and we're still in that process of making it fast. It's awesome.
Tanner: 24:13 What's great is that even without those optimizations, it still was fast enough. I've spoken with a lot of big companies who are using it and they have never mentioned anything about performance.
24:29 In fact, I have someone who internally is looking -- they're already using it at Walmart quite a bit -- and he did speed test, performance testing, between Redux and React Query and all of the other options and they actually chose React Query because it had faster load times and invalidation times.
24:54 When I first learned about some of these performance optimizations to be made, I was like, "Wow, I'm surprised it's still even passed the test."
Kent: 25:02 That's fantastic. Do you know about the experimental useMutableSource hook that React...?
Tanner: 25:11 Yeah. I've looked into it a little bit. I was tempted to like, "I got to try this out right now," but that never really works out in my favorites. I've just been waiting for it to come out, but I am excited about that to where we're not going to have to use this pub/sub model anymore.
25:29 Even though you do the pub/sub model is not that bad. Let's be honest. It's not that bad. Zustand is another library that I actually adore. I've only been using it for a week, but for some reason, it just spoke to me. They're using the same type of pub/sub model. It'll be cool to see how useMutableSource gets rid of that and brings things back into the React the React way of doing things.
26:02 I'm still curious, whether it's going to make a huge performance impact. I feel like it's going to be more about being concurrent mode, safe, than actually being faster, in my opinion. But I guess that's the same thing, though.
Kent: 26:22 That makes sense. I wanted to also ask you about that React way. I know that people who are into Redux or MobX, they like the fact that you can put all of your business logic into this JavaScript-only stuff and then just this tiny little bridge to get you over to React land. There's just something that gets people excited about that.
26:50 Whereas with React Query, you're pretty much using hooks left, right, and center. You can't separate your stuff. You can make functions that go get your data and transform your data or whatever, but at the end of the day, you've got to hook this up to your own hooks, no pun intended. What are your thoughts about that, coupling your source code to React?
Tanner: 27:15 I don't know. From my perspective, how often are we changing frameworks? I used Angular for years and I built amazing projects with it.
Kent: 27:28 But you changed from Angular to React. Would you have even wanted to port over, just copy-paste?
Tanner: 27:39 No. We copy over concepts. If we had types at the time, maybe we would have brought over some types for some of the structures that we were still going to use. But I don't feel like we would have brought over much code just because between frameworks, it's not just logic that's changing. Usually, paradigms are changing.
28:03 Seeing how React is living even longer than my Angular bout was, I feel like the concern of changing frameworks and being framework-agnostic with your logic is slowly going away, at least for me. With that perspective, I say, why not just embrace whatever framework you're using?
28:28 As soon as you let go of all that, you gain a lot more productivity, and in a good way, the ability to abstract away a lot of this logic that you would have to do on your own. If you owned all the business logic here and you've got your framework here, I still feel like that's forcing a square peg into a round hole.
28:55 Just putting them together feels so much better. That's why I built React Query. I say, just go all in. If we're going to use hooks, let's take advantage of them to the fullest extent.
Kent: 29:08 That's my perspective as well. Even if we decided to switch over to Vue, how much of that stuff would we want to carry over? I am on the same page with you, that the paradigms change. Even when you come back around to it...
29:26 Maybe if you're migrating over time, which is what you'd probably want to do anyway, like having this logic be separate so it can communicate with both of them, I can see how that could be valuable.
29:38 I'm much of the opinion that if you're going to do React, just go all in and bet on it. It's been around long enough that you're not going to make a wrong bet. The computers will write your app for you before React is not useful anymore.
29:57 I wanted to talk really quickly about the React Virtual too because we do use that in the performance workshop. Where did the inspiration for React Virtual...? Because there's react-window, react-virtualized, both by Brian, who is a brilliant, smart engineer at Facebook. Why did you feel like React Virtual needed to exist?
Tanner: 30:25 I love Brian and I love those two libraries. I use them extensively still in some of my projects that I haven't migrated over. One of the reasons is, I wanted a hook-centric approach to virtualization, and I feel like the existing libraries in the ecosystem hadn't yet embraced Hooks fully. They still relied on rendering components or passing some type of rigid render prop or component, that usually ended up causing me problems with having to memorize components or use memoir component. I don't like doing that.
31:07 I would rather memorize the CPU and the work that goes behind all these things, and just be free to render it how I want. That was the challenge that opened it up for me. The inspiration absolutely came from the prior work in ecosystem. All of Brian's work was very valuable to go and study and learn exactly how the virtualization was working.
31:34 Once you wrap your head around it, it's still difficult to understand. I'll just say that. I still, "How does React Virtual work?" I would have to go back and look at my code to remember what I'm doing. I wanted to keep it small, easy to understand, just so that anybody could come in and contribute and make it better. I feel it does that without sacrificing too much.
32:05 Now, obviously, it doesn't have all the bells and whistles that you would need for building intense data grid, syncing, and all that stuff. Maybe it will someday. For now, it's doing a fantastic job at just, I need to virtualize this list and I want to have 100 percent control over my rendering. I'm going to use React Virtual.
Kent: 32:31 I can tell you that when I switched from React Window to React Virtual in the performance workshop, part of the reason that I did that was because I was also switching from the Downshift render prop component to the Downshift useCombobox hook, which I didn't write that one.
32:51 I did contribute a little bit to it, thanks to working on it, but on the upgrading this performance exercise.
32:59 When I was moving everything else, I was like, "Let's just go full sail onto Hooks." The before and after of that was like, render props and other components, and you have to pass this component as the children prop. It's weird and you have to memorize a bunch of stuff.
33:15 I switched over to Hooks and I was like, "Wow, this is amazing." Everything is so flat over onto the left and straightforward.
33:23 The one drawback was that I had to now think about applying styles myself, where React Window took care of that for me. That was nice.
33:36 I remember, I opened an issue asking you to give me a prop getter for that or something, and you push back. It was good. We ended up with something pretty good. That's a cool thing about these abstractions as a hook is that, if it mattered to me, I could just make my own hook around this and my own hook can do that. It's powerful stuff.
Tanner: 34:02 Yeah. That definitely gets difficult when it comes to styles. When it came to React Virtual, you're just translating a lot of these measurements and math into styles. Unfortunately, there's two or three different ways of doing virtualization when it comes to CSS.
34:25 You can do transforms, or you can do absolutely positioned elements, you can even do a CSS Grid where every single cell or row lives in the same grid item, which has great performance benefits, actually. They all have their different use cases. That was the primary driver for not making an assumption there and just saying, here's the math, and then here's some common patterns around it.
Kent: 34:52 Yeah. I can tell you I used React Virtual as well for this little side project, because I had a big list of things. My desktop was able to handle it fine, but I use my phone most of the time with this app, and I wanted to virtualize this listing.
35:09 This was the first time I tried to do any virtualization with dynamically sized elements. I bumped into a little problem, but you helped me out. I figured that out. My problem was not reading the docs, basically. I was surprised how easy it was to get that dynamic list going as well. It's amazing. Good job on that one.
Tanner: 35:34 The dynamic stuff is so confusing. It's hard. In fact, I feel like the hardest stuff I've done with some of my libraries has been around dynamic measuring for applications.
35:47 I'm going through the same rough patches with dynamic measuring with my charting library, React Charts, which has a lot of automatic dynamic calculation of placement of all these divs and SVGs to fit everything together. There, I've been working on for the last four days. I just want to give up, but I'm not giving up.
Kent: 36:12 Tanner, you've got GitHub sponsors, that's your main source of income from the open source side of things, in addition to the React Query Essentials. You want to talk about some of those things? How can people support you with this stuff?
Tanner: 36:28 Yeah. GitHub sponsors is a great way. Every single person who sponsors can get their name and a link. If you sponsor high enough, I even let you put a logo or whatever you want on my websites and stuff. It's advertising, but I don't like to call it that. People can do that.
36:50 I even have enterprise companies who sponsor me, that are not allowed to put their logo on my website for legal reasons but they just do it.
Kent: 36:59 That's good for them. That's great.
Tanner: 37:02 I make sure that all my sponsors are well taken care of. I'm not making promises on office hours or anything, but I make sure that everybody is happy.
37:12 React Query Essentials was something new for me. I know you've been doing courses for a while, at least, relatively from my perspective. I'm sure everybody watching this is going to love this course and everything.
37:26 I went out on a limb and decided to create my own first course called React Query Essentials, which goes in-depth just on React Query. Basically, goes over every single part of the API and took everything out of my brain and put it into a big video course, similar to what you're watching right now.
Kent: 37:49 That's great. I watched several of those videos, I haven't gotten through it all yet. People watching, it's the React Query stuff that I show you, which is quite a few videos, but it certainly doesn't cover all of the API. Just what we need for the app that we're building.
38:10 If that's not deep enough for you and the docs aren't doing it for you, then definitely give React Query Essentials a look, because it'll give you all the things. Tanner, you also have the TanStack Discord. Do you want to invite people to that?
Tanner: 38:26 Yeah, absolutely. You can just go to tanstack.com. There's a link there, and you can join the Discord. Just basically talking about all things TanStack. We have Query, Table, Charts, even React Virtual, they all have dedicated channels and people who are asking questions and helping each other solve problems around all those libraries.
Kent: 38:49 Great. Cool, man. Thanks so much for giving me some of your time, and looking forward to people watching this and tweeting at you. What is your Twitter handle?
Tanner: 39:00 Tanner Linsley, just my name.
Kent: 39:02 Awesome. Then tweet at Tanner, tell him how awesome he is, how much you love the TanStack. We'll see y'all later.
Tanner: 39:09 Thanks.