I want to provide an in-depth overview of the important concepts behind reconciliation. We'll then explore how React uses the algorithm and go through a few magic words we hear a lot, like coroutines, continuations, fibers, generators, algebraic effects and see how they all relate to React.js.
Inside Fiber: the in-depth overview you wanted a TLDR for
AI Generated Video Summary
This Talk explores the internals of React Fiber and its implications. It covers topics such as fibres and units of work, inspecting elements and parent matching, pattern matching and coroutines, and the influence of coroutines on concurrent React. The Talk also discusses effect handlers in React, handling side effects in components, and the history of effect handlers in React. It concludes by emphasizing the importance of understanding React internals and provides learning resources for further exploration.
1. Introduction to Inside FIBR
Hi, everyone. It's great to be here. Hello, Amsterdam. Hello, everyone who's online. Yeah, I'm Mateusz and this session is Inside FIBR, the TRDR you wanted for. This is me. You can find me everywhere as Ytacombinator and I'm a senior front-end engineer at Medaglia and I also mentor front-end people at Tech Labs Berlin.
By the way, we are hiding. Before we start, I have a few disclaimers. The first one is that, as you probably can imagine, React source code is really complex and some of the thoughts I have here are a bit speculative. And the second thing is that maybe it's going to be not 100% what you call a TRDR, but whenever you find the huge payload of content, you'll see this icon, which means we can discuss that in the afterparty or in the Q&A sessions and etc. So don't worry.
2. Overview of Fibres and Units of Work
If we think about a React component, we can get to something similar. So we can think of a fibre in this way where we have our components instead of a function. Our props instead of our parameters and then our component state being our local variables. So to start, we can think of the fibre architecture as this React specific call stack model that basically gives full control over scheduling of what should be done. And, a fibre itself is basically a stack frame for a given React component. Okay, we got to this definition, we got to this parallel with regular stack frames, now we can start seeing fibres as units of work.
So let's think what happens with our components. So once our template goes through the JSX compiler, we end up with a bunch of elements that's what you note. What happens next is during reconciliation, the data from all of these elements is merged into three alpha fibre nodes, then we'll talk a bit more about them. And then, of course, depending on the type of, what is it, for example, it can be a function component, a class component, a suspense component, or whatever, so depending on the type of the thing, React itself needs to perform different types of work. So it's going to tag these. And then, each element is converted into this fibre node that we're talking about, that's going to describe for React what kind of work that needs to be done. So this leads us to this unit of work thing. And because fibres are units of work, it makes that convenient to track, schedule, pause, and abort specific types of work.
Now that we can abstract those in terms of units of work, we can also think about visualizing those units. And for this, I'd like to propose this first experiment that is inspecting elements. So let's take this simple app. I have just a few components with some local state and I have this button. I am incrementing and decrementing this local state and that's about it. If we start logging our fibres in the console, we are going to see this, a bunch of metadata about our components. And we'll see metadata about props, about state, about etc. And I know it was like really tiny but don't bother trying to read that. But I just wanted to see that there's a bunch of information there. And we can use, for example, this kind of information. So here's a piece of code where I'm using only five of these properties to iterate on those fibres.
3. Inspecting Elements and Parent Matching in React
Now I can inspect elements and get metadata about components. React has 25 different types of tags that can be used to tag what needs to be done. The more React adds features, the more these tags grow. React elements are just data, allowing us to manipulate them. Another experiment is parent matching in React, where I match a response of an API and render components based on the match.
For example, I can figure out that Twitter uses a lot of class components. So what I would recommend you to explore those is this part of the reconciler, it's called React internal types. So there you can see literally any all the properties of the fiber and what kind of data they store. And another really interesting part is this word tags. Because you will see that, for example, React has 25 different types of tags that can be used to tag what needs to be done. For example, an error boundary, a suspense component, a profile component, a regular class or function component. And you will see this keeps growing. Last time, a few years ago when I checked it was like five. So the more React adds features, the more you see those growing. Now that we can visualize those, we can also talk about manipulating them.
So I'd like to be a little bit of raise your hand here. Who here has ever heard about homo iconicity? So, this nice word is you'll find like in Wikipedia or some other sites that basically this is a property of programming languages in which the code used to spread the program is written using the data structures of that language. This is really easier to visualize in languages like Clojure, for example, or Lisp. But for now, let's take this, your code is data and your data within the problem is the code. What we just saw that React elements are just data as well. So, we can start thinking of React and home-iconicity. And just like Lisp and those languages that have this property, we can manipulate an element children or their properties in the way we want. And this allows us to do interesting things.
The second experiment is parent matching in React. I have this example of code where basically I have a match component and I have with and otherwise. And basically... And this is typed. But what this code is doing is I'm trying to match a response of an API. And if it's an error, I'm going to render my error component. If it's an image...
4. Pattern Matching and Coroutines in React
If it's okay but it's an image, then I'm going to render a component and then if it's a text, I'm going to render a different component. That's pattern matching. Coroutines are generators or producers that can consume values. They can also resolve async values and be used with a stack continuation. In React, fibers provide control paths to the scheduler, determining what runs next.
If it's okay but it's an image, then I'm going to render a component and then if it's a text, I'm going to render a different component. That's pattern matching. And this is built by checking elements internal metadata. By the way, the code for that is on GitHub.
Now, we can move to the next thing. Coroutines. I started reading about coroutines after I saw this tweet by Andrew Klock. He's from... He used to be in React. And basically he said that React suspends would trick people into learning about coroutines. And he was right. And then I started digging definitions out there for that. And I saw many of those.
So to sum up, we saw fibers, and in fibers, to summarize, we have control paths to scheduler. For example, React scheduler. And this scheduler is going to determine what runs next. It can be, for example, node.
5. Coroutines and Their Influence on Concurrent React
Coroutines in React give developers control paths to the caller, providing full control of pausing and resuming. However, coroutines no longer exist in React for visual experiences. Coroutines as an abstraction may have influenced concurrent React. An experiment is proposed to demonstrate the impact of a CPU-bound operation on a component's performance. By using a coroutines-based scheduler, the lag caused by the operation can be eliminated, resulting in a better user experience.
It will be something similar. Whereas in coroutines, basically you have control paths to the caller and handled by your code. So by developer code. So that's the key difference for these two.
Now we can talk about coroutines in React. So they first appeared when work on fiber was going as a specific component type. So remember we saw that tags where you have all the different component types? You have two others that were coroutine component and used component. And the idea there as opposed to fibers was to give you, the developer, the full control of pausing and resumbing that. And by the way, there's this really interesting pull request that determines the idea and how they manage implement that and the whole thing. But the thing is that coroutines, per se, in React no longer exist for the face to face visual experiences, for example, optimizing those and memorizing, et cetera, they no longer exist. But I imagine it's going to be really interesting to see in which form this was going to come up in the future.
For the next experiment, I wanted to recreate the same example as we had a coroutines-based scheduler. So, this is the code we have. I'm going to modify that a bit. And the key part you will see is that now I have a generator function. My resourceful operation is now a generator and I added this while true. I know it sounds hacky but I just wanted to use in the very beginning of the execution. And now I created this instance of a new scheduler and I'm passing my heavy operation. Let's see how this looks like, the actual code. So, I can start typing and you see, of course, it's suspensing because this scheduler is suspense ready. And you see it's suspense while it's performing but it doesn't lag at all.
6. Scheduler Code and Effect Handlers
You see no issues with user experience. The scheduler code is only four lines long, with three different states: idle, pending, and done. Promises are used to make it suspense ready. React's multitasking comparative model allows rendering to be interleaved with other tasks, including other rendering tasks. Updates can happen in the background without blocking the response to new input. The scheduler yields execution back to the main thread every five milliseconds, ensuring smooth animations. Rendering is effectively interruptible. Effect handlers allow running code in response to specific tasks.
You see no issues with user experience. And this is the whole source code of the scheduler I just shared. So, it's not even four lines. And I would highlight this part that is basically where I'm switching in three different states. So, it can be idle and then it can be pending and then it can be done. You will see I'm throwing some promises because I want it to be suspense ready so that you would see suspense execution. And what you can see is that... Wait, did we just did something that sounds like used transition?
So, let's go back to the original code. And let's now do this start transition that's out there in React 18, as you probably saw. Let's see how this looks. You will see that, yeah, it did like a bit in the beginning, but I have a really similar experience. So, yes, we did, using some coroutines magic. And when I start to think about, in React, there's no use of WebWorkers or WASM or anything for parallelism. So how does React feel like this? So, what we have is this multitasking comparative model, where you have, yes, you have one single rendering thread, but it's interruptible. And rendering can be interleaved with other tasks. And also, other tasks could be other rendering tasks. So, in our example, it's like this. We had this original rendered task, and then we had this user input that of course has high priority, so that the UI is responsive. And then you have the right priority to handle test, but then you can resume the original And the cool thing is that any update can happen in the background. So, it would block the response to the new input. And the scheduler isn't smart enough to switch to the more usable one, then after it finishes, resume the other one you had. And my favorite fact about that is that it yields the execution back to the main thread every five milliseconds. And the first time I figured this out, I was like, OK, but why five is this like, those magical CSS numbers we use sometimes? And it's not. Actually, the thing is that it's the smaller thing you can fit in a single frame, even on 120 FPS devices. So that's why it feels like it doesn't block animations. And to be honest, the second thing is that most of the individual components, they don't take longer than that. It's not like we, on a daily basis, have to iterate up to a million in our components or anything like this. So in practice, yes, rendering is effectively interruptible.
And one of my favorite parts, effect handlers, so an overview for that is I was Googling that when I first saw it, I saw that basically effects are this thing that asking the call environment to handle a particular task. And when an effect is used, the nearest effect handler is called and then it allows you to run some code in response to this.
8. History of Effect Handlers in React
Effect handlers in React have been used throughout its history, starting with the layouting algorithm. They were experimenting with effect handlers to redefine the way React handled layouting. The idea of effect handlers was also used when rebuilding the context API in React 16.3, but issues with memoization and optimizations prevented them from using effect handlers. However, it is still an interesting concept to explore.
So effect handlers in React, they appear a lot throughout the whole history. So it all started with the layouting algorithm. So years ago, yeah, you can check this out. It's the whole thing, but to summarize, they were experimenting with that with constructions based on effect handlers to redo the whole way React was layouting. Fast forward in time when they were rebuilding the context API, so if you think about when React 16.3 was out, we had a brand new context API. When they were experimenting to create that, they also used the idea of effect handlers to build those. But again, issues with memoization and other optimizations prevented them from shipping that with effect handlers. But still, it's really interesting to check this out.
9. Handling Side Effects in React Components
Again, handling side effects inside a component. A proposal by another core team member. Similar pattern, but instead of throw/catch, it catches effects. Components can suspend rendering by throwing a promise, caught and handled by the framework. This mimics effect handlers using the throw, handle, resume pattern in React components.
Again, with handling side effects inside a component. So this one is a proposal by another guy from the core team. A really similar example. And again, where you have the same pattern, but instead of throw rays, instead of catching, catching effect. Now, my favorite part about that. Who here has ever built a suspense ready API? So you're building an SDK, and you want people to use that with suspense. Oh. So I would recommend you to check this one, for example. That's React cache. But it doesn't have to be React cache. Just check any suspense ready API you use. For example, React fire or whatever. You will see the same pattern. That is a component is able to suspend the rendering by throwing a promise, which is caught and handled by the framework. And this sounds hecky, but actually this was the way they had to mimic the effect handlers. And how? Because you have the same throw, handle, resume pattern, but in the form of React components.
10. Conclusions on React Fiber and its Implications
React fiber was a rewrite of React, focused on giving more control for low-level execution. React addresses the lack of resources at the language level by implementing alternative solutions. Understanding these internals allows us to come up with our own abstractions. React is not reactive but feels more and more concurrent. React is a democratic agent of spreading knowledge. Don't always trust all speculations and future predictions. Thank you for being here.
A few conclusions on top of that. So I guess the first one is, yeah, React fiber was a rewrite of React, the whole React core, focused on giving more control for lowlevel execution. Where we have fibers as this cooperative way to handle execution in lowlevel, and we have algebraic effects as a way to handle effects where they and their behavior are independent.
For the next conclusions, I'd like to mention this talk by Rich Harris from years ago. It's called rethinking reactivity, and in this talk, he explains a whole thing of why React is not reactive, and I think he's right. It's not reactive, but it feels more and more concurrent, and that's amazing. For one of my last ones, I love this tweet. It's like years and years ago. It was by Gisela Mahosh, and he said that React is such a good idea that we would spend the next decade exploring its implications and applications. I couldn't agree more because the fact that we are here, a lot of people discussing all of those concepts because they all somehow relate to React, shows that React is this democratic agent of spreading that kind of knowledge that's not so popular in the front-end world. So I really love that. And for my last one, I have this picture. And people say that a picture is worth 1,000 words. So this is me eight years ago in an iOS meetup, telling iOS developers that Ionic or Angular would be the whole feature of development. So I guess the last conclusion is don't always trust all of my speculations and future predictions. These slides are going to be available on my Speaker Rack profile. And that's all. Thank you so much for being here. And it feels great to have those events back. Thank you. Thank you so much. Now, before people rush off to lunch, I have this massive cup filled with React coins. Remember, if you collect the most React coins, you can get a prize. Stick around. You'll find out how to get these off of me. But thank you so much for your talk. I really enjoyed it.
Questions on Throwing Promises and Try-Catching
Can you use React hooks that store data on the fiber in the same stack frames? Try-catching is not an expensive operation in React. The difficulty lies in abstracting the way you handle effects. This was a reason why the proposal for algebraic effects didn't progress much.
Really enjoyed it. Let's jump into some of those questions. So when throwing a promise for suspense, can you also use React hooks that store data on the fiber in the same stack frames? I'm not sure if I get this one. It says to rephrase, maybe. We'll come back to that one. But let's jump over to the next question about try-catching being an expensive operation. As far as I'm aware of, no. To be honest, I'd never check like benchmarking on that, to be honest, but as far as I'm aware, no. Probably this question was because of the things I mentioned. The difficulty of memorizing stuff and etc. But it relates more to the whole way you abstract those than to try and catch something itself. For example, this was the reason, for example, that the proposal for jabraic effects didn't move forward a lot because we got to the same thing that it was difficult to optimize that, but not because of the whole nature of trying catch. Nice, nice.
Learning Resources and Comments
To learn more about Fiber, you can start by engaging in discussions with the IRFCs and exploring the PRs and discussions, especially the older ones. Additionally, you can check out other repositories like React Basic and the IRFCs repos to gain a better understanding of the historical context. It's also beneficial to draw inspiration from other languages such as F, Clojure, or other Lisp languages that have worked on similar concepts. Lastly, don't forget to connect all the pieces together and explore the interesting work on caching and suspense.
Now, we don't have any more questions, but I just want to learn. I personally have not actually learned a lot about Fiber, so I was listening to your talk and it's so much that I would like to go and research and learn more about. Where should I start looking? One of my favorite ways of doing that is actually talking during discussions to all the IRFCs and what they've been doing since 18, a really good work with the RFCs, and etc., but go through the PRs, discussions, especially the old ones because you see how they evolved to the point it is now. You will understand the context, that's one thing, and go through other repos like React Basic and the IRFCs repos. There are repos outside of the main one but they have a lot of content that helps you understand the historical context and how they got to this point. That's one thing. The other thing is check inspiration from other languages. For example, there is a language called F and it implements out of the box this whole idea of effect handlers. It's called F. Check how those other languages abstract on top of that. For example, for the thing of homo-iconicity, Clojure or other Lisp languages. So, trying to see how other languages who did a lot of previous work on that abstract on top of this and then connecting to what you see of the previous work on the React team and try to piece them all together.
Nice. Now, this next one looks more like a comment than a question about Drupal throwing values instead of exceptions. I'm going to guess we don't need to feel too bad about React. Yeah. I didn't know to be honest. I mean, I've done PHP in the past but I didn't get to that point. But that's interesting. And by the way, I'm not sure if that was a post or only a comment in a PR discussion but then Abramoff was kind of mentioning that it's a pity that today we see only suspense as throwing promises because it's way much more than that. I know that there's a lot of interesting work on caching for example that pieces together with suspense. So I myself was one that was making jokes about throwing promises for a while. But yeah, it's interesting to see that. Nice. Thank you so much.
Now before you go, folks, I have literally maybe a couple hundred React coins. I'm going to give them over to Matheus. Wow. Because if you go over to Matheus, you can grab some of these React coins from him. However, you need to ask him a question. So Matheus, they can ask you a random question, any question, I'm throwing you under the bus here. It could be about fiber, it could be just about your time speaking. And go and talk to Matheus over at the Q&A booth. Sure. And see him before you go to lunch. Not only this, but I didn't get to put it on the slides, but I did trade stickers as a side. Trade stickers, yes. Me and him traded stickers at the booth. Definitely check him out for cool stickers.