Dominik Dorfmeister - VIDEO EDIT === Paige: [00:00:00] Hi, and welcome to Po Rocket, a web development podcast brought to you by Log Rocket. I'm your host today, Paige Nehouse. And today we have a returning guest, Dominic Dorf Meister, a software engineer at sent and open source maintainer of the Tans Stack Libraries, and he has here to talk about the useless use callback. Welcome back to the show Dominic. Dominik: Hi. I'm glad to be back. Thanks for having me again. Paige: ~Yeah.~ Well you have written a very interesting and a little bit maybe controversial, uh, blog post recently about use callback, which has spurred a lot of controversy, I think, just since its inception as a hook in the React ecosystem. So let's talk about. Why you feel that it is useless. And also the used memo callback, both of those were described in your article as totally pointless. So can you talk a little bit more about why you feel that way and you know how it's maybe become overused in react, in modern react code [00:01:00] bases? Dominik: ~Yeah. So, um,~ I'm usually not that definitive when it comes to like, framing things. And obviously, you know, we also have to take this with a, with a grain of salt. I mean, totally useless or pointless is, um, is obviously not like in a nutshell what you can say about, uh, these hooks. But from the way I've seen. Those grow in larger code bases is often that, you know, things start out with a little bit of memorization that is needed in a specific situation. Um, mostly for referential stability to speed things up. You know, maybe you have a memorized component, um, that gets a callback call and then you need to wrap that in, use callback so that you don't break that referenceability between renders and. Usually when this gets introduced into code bases, you know, there's, there's no like, ill intent behind it. People try to make things better and faster, but the problem I have with that, so to say, is that I think it doesn't evolve [00:02:00] well over time. And that actually starts with React Memo, which is, uh, the other article, uh, the Uphill Battle of Memorization that, that I've written where kind of like you start having reactor memo and then you need to memorize all the props. What components usually do over time is they evolve and we add more props, and then it's very easy to just break that memorization. And once you are at that point where you have all props memorized except for one, um, none of this actually works as intended. And this is what I've seen in, in older code base, in larger code base a lot that whenever I see a used callback somewhere, I try to see why it's there and then I go to the component that is memorized and then I actually see that, you know, everything would work just the same without this used callback. Because in that instance it doesn't do anything. And that's what I've seen a lot and that's why I wrote the article. Paige: So really people are trying to improve the performance of their applications, but as like you said, as the code [00:03:00] base grows and evolves over time. It just adds another layer of complexity that isn't actually doing what they intended it to do originally. Dominik: ~Yeah. And that is the,~ that is the thing that I like to try to strive towards the most is actually to have the code readable and, you know, maintainable if you will. And, um, adding a lot of. Use callbacks and use memos that you only need for referential stability does the, the exact opposite. And apparently like these things don't get measured because if they were, or maybe they were measured when, um, this was introduced, right? So you see something is slow, you measure it, you find the cprit, you memorize it, it gets better, but then you don't actually measure this over time because otherwise you would see when it gets worse again. Um, and I found a lot of usages in, in, in century, for example, in the copays where we have like used callback and a bunch of times I just removed them because they didn't do anything and things were also working the same. So, um, there's this kind of situation where I'm wondering like, [00:04:00] why do we have them in the first place? But this is just how things evolve. Um, and that's kind of a problem I guess. Paige: So how do you weigh the trade off then between ensuring referential stability and keeping the code simple and maintainable? Dominik: Yeah. So one of the things that actually I think gonna really help here is the React compiler, because I don't think that. You know, the, the memorization itself is a, a bad idea if you want to make things faster. It's just very hard for humans to do consistently and everywhere throughout the code base, especially when props like children are involved when, when inline objects or arrays are involved and when functions are involved. Um, so the react compiler just changes the game by really memorizing everything. If memorizing everything were easy, you know, we, we would also do it. But then when we humans try to do it, we do almost everything. And almost everything is the worst you can do. And also it adds all this overhead to your code base that you have to read and passe and understand why it's there. And that's kind of like worse than, um, just, just [00:05:00] trying to get by without those. But with the React compiler, I think what, what, um, people have seen is that it does, um, create a very good performance benefit. And in, in that case, I think that's, that's the best, you know, situation we could be in. But if we're not using the compiler yet, I think the question is really like, why do I need to ensure referential stability in the first place? It is because I have a memorized component and I just, or I have a use effect that where I call this function. But yeah, use effect is a different like kind of beast. Um, we shouldn't also like, probably overuse that one. Um, but. I looked at this centric code base, um, just before, and I've only seen like this has, uh, a million lines of TypeScript code and I've only seen like 27 components that are actually wrapped in React memo. Um, so I think. There are situations where you might need it, and then it need to be very specific about it. It maybe do a proper naming on top of that, and [00:06:00] then maybe make sure that those components that you memorize don't take any props at all. Right? You could ensure that with a lin tool or only you, you, you don't allow functions or whatever. Um, but if you, if you apply this like surgically really only in situations where you know it's needed, um, then. You should be able to get away with not using it anywhere at all, because as soon as you try to, oh, this feels a bit slow, I'm gonna memorize this. Um, you are on, on, on a path to making your code base like probably a bit worse than, than without it. Paige: So you said that within the Century Code Base, there were only 27 instances that you could find. When, when do you think it is worth the effort or is it really ever worth the effort to use something like Use memo. Dominik: ~Um,~ so for React memo, I've seen a couple of usages around, around tables and virtualization. I think maybe that if you have like a library, like a virtualization library that expects the roles that it renders to be, uh, referentially stable, then obviously, you know, that is something you, you need to do. Apart from that, um, [00:07:00] I think there are very often ways to not reach for reactive memo. Paige: So. If a team, like in Century, for example, was using either use callback or use memo, how common do you think it would be? Or you know what? What can they do to avoid either breaking the referential stability and then completely invalidating the use memo or the use callback, or at least keeping track of it beyond. Performance timing, like do you have any examples or best practices to keep an eye on that sort of thing to make sure that it's still doing what you intended it to do? Dominik: Yeah. I think that's the hard part, and that's, that, that's also why I think it's, it, it's not worth doing it. I mean, I, I'm, I'm not saying like we need a Lin tool that forbids them completely, but every time, at least when I. See them in a pull request. I'm like questioning, why do we need this? Why is it? Why is it there? What does it do? Do we need the referential stability somewhere? [00:08:00] Of course, there are situations, especially for use Memo, which. Is, you know, when you have, when it's not about referential stability, but when it's about having a really expensive calculation that you might have, I mean, we have to define what, what expensive is and, and, and how often it, it, it, it runs or shouldn't run. But then of course use memo has like an a different use case in the prime use case about not to doing the work too often, but if we only use, use memo and use, and especially use callback, doesn't have that right. It's only for the reference stability because the function is always created anyways. Um, then it's kind of like, do we need that? What's the situation? It's probably gonna be a memoed component. Um, and then, um, or it's a use effect and I'm tracing down the steps. And in, in the one example of the blog post, I actually found that, you know, it was. Not working and the effect was running on every render anyways, even if, uh, we have all the memorization in place because there's always or very often something that kind of like bypasses this. So yeah, my, my, my rule of thumb is basically question [00:09:00] every usage of use callback that you see. Like, why is it there, why does it get added? Um, I know that's hard and you need to like be, be vigilant with those things. And the other thing that I try to do is if we, if we have them. Underneath them. I try to not make them dependent on props that come into a component or a custom hook, because once you have that, you basically, um, offload this kind of responsibility to the consumers. So you, you're saying I have a, I have a custom hook and that takes in an array of things, and then I'm using these as my dependency to my used callback or used memo. But the call side of this custom hook needs to know that they have to memorize. The, um, things that they pass in from the outside. And that's really hard because you need to, I don't know, maybe document it or name it in a way that it indicates it's memorized, because otherwise the call sites won't know. And maybe you are now like fixing all the call sites that you have at the moment, [00:10:00] but people will add new call sites. Maybe otherwise. Why is this like a shared custom hook? And then it's, it, it'll break again. So that's, that's kind of the, the situation where if you, if you need this, try to make it self-contained. Um, and try not to expect people to know that the arguments they pass to you have to be memoed because how could they know? Right. That's that, that's the hard part. Paige: Which comes back to your original point of just use React compiler, which will take care of it all for you, so you don't have to think about it. Dominik: Exactly. The hard part about React compiler is to actually be compatible with the React compiler. So you need to adhere to all the rules of react, like basically to the letter. Um, and there are some situations that are, that are hard, especially if you, if you haven't watched out for them like at all in the past, like reading from ref during render and so on. So there are kind of like nuances with a rec compiler. We just probably bail on, on doing things. But in that case, I can, I can recommend right now using the ES lint plugin, uh, for the react compiler that will tell [00:11:00] you right now where there are things that you're doing, um, things that the compiler will not be able to, to optimize, and then you can try to work on those. Paige: Oh, nice. Is that an ES LT plugin from the React team themselves, or is it a third party? Just helper function. Dominik: Now it, it's, it's from the React team themselves. It used to be a separate plugin called Lin Plugin React Compiler. But I think with the latest uh, uh, version, they just moved it into the Lin plugin React, and there is now a react compiler rule that you can turn on. And then, um, it's not, it's not the fastest Lin rule. Um, so it's, it's more on, on, on the, on the slower side of things. But it will, it will tell you when you're doing something that the compiler will not be able to optimize. And I think that's, um, that's a good step forward. Paige: ~Yeah, absolutely. That's worth it. ~Well, cool. So one of the things that you mentioned in your blog post was instead of using, uh, use, I think it was used callback, you used refs instead. Um, so you present kind of a latest ref pattern as an alternative Oh, to memos. Excuse me. [00:12:00] So are there any pitfalls or edge cases that developers need to be aware of if they kind of transition to trying to use this instead of memorization? Dominik: Yeah, so the, the latest rev pattern is something I actually read on Ken, see Dots blog, uh, the first time a, a couple of years ago. And basically what it does is. When if you have to depend on a prop that comes in and you don't want to create a callback that is reactive to this prop, so it needs to be recreated every time when this prop changes. But what you want to do is you want to have a function, and when this function is invoked, you want to actually just read the latest value no matter what, and imperative access, right? This is kind of like. Um, where react is usually declarative and everything gets recreated when something changes and it stays nicely in sync. You know, there are situations where we sometimes just want to call a function imperatively and then read something directly from it. Like when you have a, for example, a, a, a story you [00:13:00] can call, get state and just imperatively grab whatever is in there without subscribing to it. No. Obviously you wouldn't do that during render because then things get out of sync. But if you are in a callback function that gets invoked. Imperatively, you also want to just give me the latest value that I have right now. And if that's the case, then the latest ref pattern can be really helpful because what it basically does is it takes the, the prop and just writes it to a ref in its separate effect, and then, um, updates it all the time. And then in your, uh, callback, you are just going to read from the ref. And the ref doesn't need to be added as a dependency array. The ref is kind of like just a mutable object where you can write towards and you basically leave this reactive, declarative. Way of doing things into a more imperative way. And you can just then, uh, access the rev in the effect or in the callback. And that's been very helpful if you also apply it sparingly and internally. Like you wouldn't want to create that and then pass it around [00:14:00] to to, to child components, um, or something like that. And I think the, the React team has actually like also considered this. Um, in the past where they had this use event hook, I think that they wanted, and then specialized it towards something called Use effect event that is, uh, react Hook that is currently in the, uh, I think release candidate or like the, the, the, the preview build of the, of the, of the, of the next two release. So it's something that's still unstable. Um, so it's not an official hook that is in, in React 19, but it's something that, that you can try out. And that's basically comes from the fact that we sometimes want callbacks. That are, you know, referentially stable while still reading the latest value of, um, a, a prop or an another field. Um, and for this right now, you would need to work around it, but if this becomes like a built-in thing, then you wouldn't need to apply this manual pattern anymore. So that's, I think, uh, a really good step forward [00:15:00] too. Paige: Excellent. That's actually what I was gonna ask you about next was if you could talk a little bit about use effect event, but I think you've already covered it Dominik: ~Yeah, because I, I, I, I think doing,~ doing this manually with like the latest rev pattern is a a lot more work, right? You need to wire up all these things. Um, um, correctly, and since you, since you asked about pitfalls, I think there is some versions that actually use a layout effect instead of an user effect to update the rev because, uh, effect actually. Uh, run like bottom up. So children effects run first, and if you take this callback and then maybe pass it to a child component, um, and then use it in an effect there, you know, it might run before it's actually updated by the parent. So that's why they use a layout effect. But then you have the same problem if you have a layout effect in the child component. So it's kind of like trying to get the timing right with how this works internally in React. And as long as you have this rev and it just stays in the same component and you don't pass it around. Again, that's I think why I said, you know, [00:16:00] we shouldn't pass these things around. If we do these workarounds, it should kind of like be centralized in, in one place and not abstracted the ways that we can then do whatever we want with them. Um, but it's kind of like once you have like use effective events, the timing will always work because the React team just internally makes sure that like this is a specific use case that they're covering. Um, and we don't need them, those workarounds anymore. Paige: Got it. So it's helping to prevent some of those race conditions That can happen sometimes when, you know, data's being fetched from multiple places, but it's coming in in DI in different orders than originally intended. Dominik: I haven't, I haven't really, um, like seen those edge cases. It's just a theoretical thing that, uh, I think if you use, this would be good to be aware of. Paige: ~Nice. So one of the things that you suggest is outsourcing referential stability to a compiler. Uh, so what would a, a good compiler level solution look like for React?~ Dominik: ~Yeah, so I was basically talking about the React compiler itself, so that this is, uh, I think this is the, the, um, the thing that I think is. Most likely to succeed. I'm not sure if there are other compilers out there that are actually trying to achieve something similar. I'm not sure if, uh, million Js was also working in, in some direction there, but since React announced the React compiler and this is available, um, as an, as an opt-in solution right now, uh, via b plugin for everyone, um, I think this, um, you know, just takes away most of the problems for, for US humans.~ Paige: ~Got it. ~So you were saying that when you do PR reviews for Century, if you see a used memo or a used callback, you're immediately skeptical and take a really critical look at that. So do you have any team guidelines or tips that you would share that other developers can use to help do [00:17:00] their own PR reviews and be. And make sure that these things that are going into the code base are actually doing what, what they thought they were. Dominik: Yeah. You know, I think if everyone's aware of the, of the, like the whole situation, the problems, the upsides and the downsides, then what really helps is just not overuse. Those use memos and callbacks and if you add them, um, what I like to do is when I make a PR, is I leave a self review on that pr, so I go through my own prs and leave comments for the reviewers. Explaining maybe why I'm doing this. Um, and in those situations I would probably just outline why some memorization or use callback is actually needed here, so that we kind of like, don't have to go back and forth between why do you do this? Oh, I'm doing this because, oh, okay. Gotcha. Especially when the, when the team is like time zone differences, like I have with, with my team at Century, we will be wasting like two days discussing why are you, why are you doing this, um, back and forth. So I'm trying to be upfront about. Some situations where I think that people might have questions [00:18:00] upon. I'm just outright posting, um, a PR review, uh, for myself about, uh, the things that I did. Paige: Sure. That's a great way to do it. Um, my, my teammates and I do that as well for things that might get, that might be flagged as kind of strange or Why did you do this code? Dominik: Yeah. And I, I think that's, that's a, that's kind of like a, kinda like a cheat code sometimes because when I do this, I kind of have to think about. Why I did things and I kind of like, I realized, okay, maybe this isn't ideal. Sometimes when I try to write a comment about my own code, I figure out that, oh, actually I should, you know, maybe change this a bit. Or maybe I read through it and I find a better way to do it. Sometimes I find some code that I only left for, for tests and I want to kind of like for debugging and I want to remove that. So before I waste time of everybody else. I open the pull request in as a draft PR and then, um, go through it myself and only then when I'm, when I'm satisfied with that, that I haven't left anything and that this is as good as it can be. I'm, I'm kind of like giving it [00:19:00] to others for review. Paige: Yeah. I mean, self reviews and for whatever reason it. Different looking at your code in a GitHub PR versus in your VS code instance or wherever you prefer to actually write it. It's, yeah, it, and it just helps you, like you say, catch things that you didn't notice as you were putting it together. So yeah, thumbs up for the self review for sure. So one thing that I wanted to ask you, and this may be. Kind of why we've gotten into this weird state of use callback and use memo and people not really, or maybe overusing them is the React community docs. Uh, do you think that they might unintentionally encourage people to over memorize or try to over optimize before they really need to? Dominik: No, I don't. Um, I really don't think so. I think people don't read the React docs enough. Um, the very good, the, the new docs are really, really good. Um, there's this one article, there's one page that I, that are always linked towards. It's, it's called, you Might [00:20:00] Not Need An Effect, and it shows like all the usages. Um, it's a bit of different topic, but it shows like all the usages. Um, that you can do, uh, before you have to reach for a use effect. And that kind of like eliminates so many use effects from your code base if you just adhere to what the, what the React docs say. And I think for, for memorization, um, regarding use callbacks, they're also saying that, you know, the compiler can do this automatically. Uh, you only need to do this in specific situations when you really have the need and so on. So I think that. The React docs are really like on point in, in that regard. I just wish, you know, people would, would, would read it more. Paige: ~Good,~ good advice. Alright, uh, so is there anything that we haven't talked about in terms of use effect or sorry, use callback or use memo that you think people should be aware of or developers should keep in mind as they're using or potentially not using these things? Dominik: Well, I think, you know, just being aware of that, it's probably not necessary to do it or [00:21:00] just question like why things are there is already a very good first step. I've seen situations where people just do things because that's how it's done in the code base and we do it everywhere. And not really understanding why things are done. I know there's also a movement, um, of people saying, well, we memorize everything by hand. Because then we don't need to discuss why something is memorized and why it isn't. And then I'm seeing use memo calls on, I don't know, a, a, a variable that um, kind of like does a calculation of two numbers or whatever and then that gets, that gets memorized, um, and things like that. But if you really do memorize everything by hand, you also need to memorize things like children or other reactors that you do. And that usually doesn't happen because this is something that gets done very, very rarely. And. That's what the compiler does. So there's like, um, it really does it from top to bottom for everything, including react nodes, not just the props of it and so on. So that's where all of this, um, yeah. Advantage comes from. And [00:22:00] maybe one thing that I also, I think I, I, I purposefully didn't highlight in the, in the articles that I wrote, is that, um, some people say that you shouldn't use, use callback or use memo because it also has like an overhead of doing it. terms of, you know, use memo needs to actually store the old value, needs to do a comparison, use callback needs to create the function and then do a comparison of the props and so on. And while that's true, this is not something that I would like to focus on when thinking about do we need use callback or not, because. If they use callback is one that is necessary to really avoid something from happening. And you are aware of that, you know, this is self-contained and this, uh, works and you maybe also, uh, hopefully measure it and so on. Then it's a good use. But I, I would really rather focus on the readability of the code, um, and how much code we can actually delete. If we don't need to wrap everything and use callback, a used memo just to keep the referential stability, it's not that [00:23:00] much about, oh, this will create a little bit more memory, or it'll have to do a bunch of small comparisons. Yes, that's also true. But if that were a, a problem, you know, the rec compiler couldn't really work at all, and I think what they found out with the rec compiler is that there is a very small overhead in terms of memory on the first render run because it is memorizing everything, but it's not as much as they actually thought it would be. Then on the, on the, on the, on the upcoming reruns, um, it's actually a lot better than it was before performance wise. So all the talks about, oh, we shouldn't memorize this because this creates unnecessary overhead. If it doesn't work, that's not the problem. The problem is that we have to read this and we have to maintain like all the dependencies up to date all the time. Um, and that's, yeah, that, that, that, that's the, the topic I wanted to focus on with the, uh, with the article as well. Paige: ~Excellent. Okay, so let's go into the quick round, which is just a quick one-liners, which are really good for social media posts. So what is one react pattern? That you think should be retired altogether?~ Dominik: ~Um,~ ~react head on. Um. I would as not really related to this topic, uh, to like this, uh, use callback topic. I would say, um, calling use state set us directly in use effect. That's something you shouldn't do.~ Paige: ~Uh, what is one front end performance myth that you wish would die?~ Dominik: ~Performance myth. Um, lemme think for a bit.~ ~Huh,~ ~good question. Um, performance myth. I would say that, um,~ ~yeah, I would probably just generally say that applying used callback and used memo to. Well, yeah, let, let's, let's put it like that. Um, applying use callback and use memo everywhere by humans is a good idea.~ Paige: ~And in your opinion, what is the most overused react hook.~ Dominik: ~Oh, it's definitely use effect. Gotta be use effect. Not even close probably. Yeah.~ Paige: Excellent. Well, Dominic, it's been great having you on the show today. If people want to get in touch with you, learn more about what you're doing or talk more [00:24:00] about use callback and use memo, what are the best places to reach you online? Dominik: Uh, yeah, I'm very active on Blue Sky. You can find me there as, uh, uh, Tiki Dodo eu. I also have my blog where, um, I have a comment section on my blogs on Tiki Dodo eu. Uh, you can reach me there. I'm on the 10 Stack Discord a lot, obviously, for all. Of the, uh, maintenance of the libraries that I do. So yeah, reach out to me in any of those places. Paige: Great. Well, thank you again for joining us on the Log Rocket Podcast. It's been great having you back. Dominik: Yeah. Thank you for having me. It was awesome.