Build Your Own (Simple) React From Scratch!

Rate this content
Bookmark

Have you ever wondered how React works?

What would it feel like to create the magical lines that make up the tool we all grew to learn and love?

Come along on our journey to implement React from scratch, making a simple React project work with your own my-react.js library.


What's included

- Introduction

- Rendering our first component

- Update cycle and the VDOM

- Meet the hooks


Prerequisites

- Some knowledge of React.

112 min
28 Nov, 2022

Comments

Sign in or register to post your comment.

Video Summary and Transcription

This Workshop focuses on creating a React app from scratch and covers topics such as rendering components, handling state, and optimizing rendering. It provides step-by-step instructions and a GitHub repository with a test case and boilerplate code. The workshop also explores the use of hooks, such as useState and useEffect, for state management and handling side effects. The implementation includes a diff module to optimize rendering by identifying specific elements that require updates. Overall, the Workshop offers a comprehensive understanding of React development and provides practical examples for building a React app.

1. Introduction to React Workshop

Short description:

This workshop today will be creating our own React from scratch. We'll cover React basics, rendering components, handling state, diffing, and targeting updates. We have a GitHub repository with a to-do app as a test case and a boilerplate for your own version of React. The process is step by step with branches for each step and solutions available. Questions can be asked in the Discord channel. Clone the repository, navigate to the terminal, open the repo in your text editor, and follow the instructions in the read-me. React is a declarative, component-based JavaScript library that uses props to pass data through the application.

Gotcha! Yeah, so for those that are just joined, this workshop today will be creating our own React from scratch. So, how are we gonna do this? So, the agenda for today, it's the three-hour workshop, so I appreciate your time and also your patience. I'm gonna kick off with a small introduction and then look at some React basics quickly and then we're going to move on to, this workshop's kind of split into four chapters, where the first chapter's going to look at how do we render components in our intro to the virtual DOM, which is a key part of React, how we handle state. Is the second chapter, then chapter three will be about diffing and how do we target updates for a specific component rather than rendering the whole application and then our last chapter, we'll look at the user effect specifically.

We've just got some more latecomers. So this is kind of an important part about this workshop. We've created a GitHub repository for you. And this repository contains two things, a simple to do React application to add items to your to do list, you won't be touching that, but the repo also contains a boilerplate for your own version of React which is what we will be editing. And the goal at the end of the workshop is to have all the features of the to do app working with your own version of React. And the process is step by step. So, we've created branches for each step, which contains also then the solutions to the previous step. So, it's kind of an iterative approach and hopefully will be quite easy to follow. And if not, you've got the solutions available at the next step. And if you have questions, we'd ask you not to ask them here. But in the Discord channel, which you should have all access to. Maybe I can drop it in the link in case you haven't. I'll add it to the chat. I can find the chat. I'll drop it in the chat after. Or maybe, Zsányi, you can put in the chat.

So first things first, we need to clone our repository. That is also available in the Discord chat, I provided a link. So first thing is to clone that repository. So navigate to your terminal, clone it, then navigate into the directory and open the repo in your text editor of choice. And then all further instructions are in the read-me. That's your best friend and guide. And we also have quite extensive docs in the repository, too, that can help cover key concepts and give a bit more information in a detailed way about the concepts that we'll be covering in each step. And the slides from this point on are just kind of focusing on some overarching React concepts. All the fun will be happened in your own kind of code editor.

So, what is React? React is a JavaScript library. And key thing about it is it's declarative. Which means that your code is in a way that describes what you want to do and not how you want to do it. The great thing about React, it's component-based. So, you create lots of encapsulated components that are responsible for their own state. And they take in input data, which we call props. And it's written in JavaScript instead of a template language. So, data can easily be passed through your application by these props. And it helps keep state out of the dump. And I'm now going to pass over... Oh, no. So, let's meet the project first. So, as I said, we've got a to-do app, we won't be touching that. But we'll use it as kind of our test case. So, you can kind of test your progress. And, yeah, it uses all the concepts that we are trying to learn throughout this workshop. And again, our version of React. Let me just get more people in. So, if you've cloned the repository, just for those who have joined, you can access the GitHub repository in the Discord channel. And you need to clone that and then open it up. But I'm going to then navigate to what that repository looks like, quickly.

2. Setting Up the Environment and Updating Key Files

Short description:

To get started, change into the repository and run npm install to install the required dependencies. Once installed, start the local development server with npm start. Inside the packages folder, you'll find our own React version, which includes the key files we'll be updating. The index file acts as the entry point where we'll handle React's functions like state, effects, and rendering.

So, it should look like this. You want to first probably say change into the repository. And you want to do an npm install. So, that we install all of our dependencies that we require. And once those are installed, we want to run npm start. Which starts our local development server. And let's take a quick look at our kind of file structure. So, we have the todo app, which contains our React app. Don't really need to look into that. But what we do need to look into is inside this packages folder, which contains our own React version. And these are the key files that we will then be updating. And this index file kind of acts as our entry point. And it's where we'll be replacing React's functions that we want to handle. Such as the state, these effects, our render functions and things like that.

3. Rendering Components and DOM

Short description:

We're going to tackle the first chapter, which is about rendering components. We'll learn how to render JSX into our browser DOM, with the goal of having our app statically rendered. The document object model (DOM) is the tree structure that represents the HTML being rendered. React elements are similar to DOM elements but represented as objects. We'll start by killing the server and changing the webpack configuration. Then we'll check out Chapter 1, Step 1, and walk through the changes made to the Todo app. We'll use the DOMHangers as the equivalent of React DOM and our own React. We'll render our first element using create root and the render method. We'll then move on to the DOMHanders and follow the start here comments and don't forget comments. The current step and helpful information can be found in the snack box. The status light will indicate our progress. The hot model reloading is not part of this work.

So, now I'm gonna pass over to Jean, and we're gonna tackle the first chapter, which is about rendering components. Great. So, I will... I think, Jean, you have to stop sharing your screen for me to share mine. Fortunately. Thanks a lot. Cool. So, I'll share my screen as well. And we will look into the first chapter, which is all about rendering. And the idea is through this chapter, we'll learn how to render JSX into our browser DOM, and by the end of it, we are hoping that we will have our app statically rendered. And a few concepts that might be helpful as they will use their names quite a bit. So, DOM stands for document object model. And it's kind of the tree structure that your browser uses to represent the HTML that is being rendered at the moment. And we will use a similar concept later on with the visual DOM in memory. More about that later. And maybe one point that is a bit different between JSX and what the DOM looks like is React elements. So, they are... Most of them are kind of DOM elements but represented as object. But some of them are also a bit more complicated like components, which are functions. But we will walk through that as we go through this chapter. So here, we will start with killing the server in case you had it running because we will change the webpack configuration. So, we need to restart it. And then we can check out Chapter 1, Step 1, to get started. And after doing that, we can start our server again so it will pick up the new webpack configuration. And as a first step, maybe I will walk you through what is happening here. So, two small things that we did between the previous version and this version on the Todo app. One is within webpack, we addressed React to be my own React. So anytime you import something from React, it will actually import from my own React. And the second part is within the index, we changed a bit what is rendered here instead of our app. So we created this DOMHangers which will be the equivalent to React DOM and our own React. And we are doing pretty similar to what we would do with React, right? We are using create root from our DOMHanders with the root element at which we want to render our first element. And then we are calling the render method, that this exposes with whatever we want to render. And let's put a div here first. And so for this first step I will walk with you through it. And the next step of this is to go over to our DOMHanders. And when you walk through the different steps, you will always find within the code a start here comment like this. So you can search for them and that tells you where you work with Start. And there are some comments that are called, don't forget, like this. So you can search for those for like next step after you finish the start here, in case there are several places where you need to modify code. Now, for the first chapter, it's not very evident, but you have to do so we added some small guiding things that you can see here. So in this case, right, it's telling you that for now you don't seem to be rendering anything. And you should go over to my own React DOMHandlers to get started. And here you have a little snack box that tells you a bit what this current step is about and what is it that we are trying to do, and some helpful information. And for the very first chapter, you also have a small status light here. It starts red when you start because we haven't completed this step just yet. And on some step, it can become orange as you, like, start having something working. But maybe not everything is working and it should be green at the very end once everything's working. Two small extra points. The hot model reloading is broken. It won't be part of this work to fix it.

4. Rendering JSX and DOM Elements

Short description:

JSX is a small templating language that allows us to write things similarly to what we would write HTML, but also includes JavaScript within it. We can create an HTML element based on the type of the root child and insert it into the root element. After chapter one, our app will start working, making it easier to get feedback on completing the steps correctly. The next step is thinking about attributes and props.

So when you see such messages, you can ignore them. Same with this one. And I think this is just a missing manifest thing as well. We can ignore it. So those errors you will always see, but we can pretend that they are not here.

And so let's get back to our DOM handlers where we should start. And now that we are here, we are thinking about rendering our JSX element, which is here, which is a div. And so that tells us we should learn a bit more about JSX. So let's do that. And let's open this in the preview.

So JSX is a small templating language that allows us to write things similarly to what we would write HTML, but also includes JavaScript within it. And as it's simplest, it really looks like HTML. So this is almost valid HTML, and that would represent the div, except we can auto close it, which would be invalid in HTML. And this gets transformed by the Create React App web configuration into an object. So what we'll deal with within our code is an object of which for now, the only thing we are really interested in is the type property. And within it, it will be div as a string. And if we try a different type, say, span, then we would get span as a string. So that sounds good enough for the step that we are on. And I'll go back to our DOM handlers. And I will just confirm what I just told you. So if we console log our root channel and we save that, and there is no hot reloading, so always refresh after you make the modification. And we can see we log this object. If we ignore some of the more weird things for now, we can see our type div. So that doesn't sound too bad. And so what we could do then, we know that the tag name of this root child would be the type. And we can create an HTML element by doing document.create element and passing the tag name. So normally that should be our HTML element. And then we should be able to use our root element, which is the element at which we want to insert our react and append the child of our element. And if everything went fine, we should be creating an element based on the type of this root child. Then this HTML element we would insert into the root element. So, in theory, that should be good. Let's try to refresh. All right, so the weird big message in the middle disappeared. We have an orange light up here that tells us, cool, you can render a div. What if we try to render something else? In theory, we should be able as well to render a span. So let's try to put a span in here. And let's save that. Refresh. We are green. And if we look in our DOM, and we open our root element here, then we can see we have the span rendered here. So that's pretty much our first step that we did together. And the next step during the first chapter will look similar to this one in terms of what you can look at with the light status, and so on. And after chapter one, our app will start working. So it will be a bit easier to get proper feedback on whether you are completing the steps correctly or not. And so I will check out the next branch. So I can set you up for your first trial at doing this. And I'll pull just in case, as I'm not sure if I have the latest stuff. That's all good. Cool. So the next step we are taking is thinking about attributes and props.

5. Understanding Props and Handling Attributes

Short description:

Props are similar to HTML attributes and are used to make elements more useful by adding extra metadata. In JSX, props are represented as an object with key-value pairs. Sometimes, props don't match the attributes exactly, such as using class name instead of class. In some cases, the casing is different, like using camel case instead of kebab case. The DOM handlers handle props and populate the attributes correctly. We use a utility function to apply props to the HTML element. For event listeners, there is some extra code for handling them.

So what are props? Props are a bit similar to HTML attributes. So if you look at HTML here, we have an ID attribute that is a value test. And that helps us by making more the elements more useful and adds extra metadata sometimes to the elements. And it's quite an important concept with HTML. And it works pretty similar in JSX.

So we also can use an ID. We can also put it to test. And we would expect, in this case, for tags that the props would convert into the final HTML. And within our JSX, this following JSX would lend into an object looking like this. So we still have our type d, but we are now looking at an extra property of the JSX object, which is the props property. And this is an object that has every prop as a key value type. So the ID became the key of this prop, and the value test is up here. And if we have more props, then we just get a bigger object with each key value added. And there is an extra consideration that you might have to look into.

We recommend that you start with ID. That's probably one of the simplest. But in some cases, the props don't match exactly the attributes. Class name is a famous one, where we have to use class name, because class is a reserved keyword in JavaScript. So it wasn't possible to just call the prop class. And in some cases, the casing is a bit different. So acceptCharSet, for example, is a kebab case. But in JavaScript in general, we favor camel case, which led to a potential problem for the GSE developers. And they decided to actually use the camel casing here. So in some cases, you may have to also see what happens when the different casting happens. And that's pretty much it.

And when you go to your DOM handlers, we have here pretty much what we went through, but slightly more organized. So we have this function, renderTagElementToHTML. It takes our JSX root child, and this thing receives props and type. With the type, we create our element that will happen later on. And in here now, we need to handle the props so that the attributes are correctly populated. So what I will do is check out the next branch, chapter one, step three. And I will walk you a bit through the way we handled these changes. And set you up for the next step. So we were somewhere around here, right? And so far, we had our render tag element to HTML here. And we were mostly concerned with the props, right? We didn't have children yet, that's coming up. And the type, and we were creating our DOM element for the type and returning it here. And what you were tasked to do now is go through the props by object of entries and extract the key and value. And then for the sake of making code a bit easier to go through, we created this apply prop to HTML element utility function that takes our key value and a DOM element we want to apply them too. And if we look up here, the same right, we are getting key value which are attributes and our element. One thing we didn't disclose because it adds a bit more extra complexity for handling event listeners. We need a bit of extra code so you didn't have to do that but we have to do it for later on in the workshop. The part that you were asked to do is here and we did it in a bit of an interesting way and I will walk you through a bit what this means. I think probably you landed with either this one or this one, which essentially right, puts on our element for the attribute key specific value. When we started with the workshop, we had only set attributes and that works for most cases but here the key has to be the CSS key. So for example, you cannot put class name in here and it has an extra weird thing that if the, you're trying to set the value for an input and the key is the value of the input and the value is an empty string, somehow that actually doesn't work and that doesn't reset the input to be empty while this does. So then we replace with this but for example, for aria-hidden, that doesn't work because this key here is expected to be the JavaScript style and not the Ja6 style, right? So you cannot have the dash here. It should be aria-hidden with a capital H. So what we landed on is checking if this is undefined within the DOM and when this thing is not set but is a valid key for this element then this would be null, not undefined. So if this is undefined, we are pretty sure that this key is not a valid CSS key but should be, sorry, it's not a valid JavaScript CSS key but it should be the valid regular CSS key so we default to set attribute for those and for the rest of them, we can set them like this. And the last small thing is for event listeners.

6. Handling Event Listeners, Children, and Components

Short description:

We add event listeners, such as onClick for click events and onChange for input events. Children are the elements we render within a specific element. Handling children and primitive values involves mapping through them and applying specific rendering logic. The main render element switches between rendering primitives and stacks. Next, we move to chapter one, step four, where we handle children and append them to the DOM. Components are an exciting part of our journey with JSX, starting with functional components like the main title component.

So we have to add an event listener this way and what we did is we created a small map of the event listeners that we are interested in for the workshop and most of them, right? Just transfer quite easily to their actual events. So onClick is a click event. The only one oddball here is onChange which is technically defaulted to input by React so that when you are, like the difference is change has to have a blur event to be triggered whereas input is triggered on every single key input so that's a bit more efficient.

And cool, so our next step will be about children as you can see here but before we get into the code maybe let's continue a bit on our little JSX documentation we have here. So what are children? Children are just the elements, right? That we are trying to render within a specific element. So in this case, our d with ID root has two children. The first one is a span that says the first child and the second is another span to say a second child. And in JSX, it looks exactly the same, right? Like this is valid JSX which is also valid HTML in this case and is the same idea. First child is here, second child is here. Our structure though, gets a bit more complicated, right? When we start having this whole thing, then we can imagine that the top level JSX object that we'll have is a type div and this div has two children and children is an array in this case. And the first children is a JSX element as well, which is a span, and it has just one child. And it's a bit weird, but when JSX has children that is a single child, then instead of being an array, it's directly the thing that you want to render. So in this case, it's just a string because there's the final string within this component, this element, sorry. And the second children looks pretty much the same, it's again a span, it also has children, which is again only one child, which is directly the string, and it says a second child. If we go a bit into how we want to handle that, so as you can see within our render tag element now we are extracting children. And here we are saying if there are something in children, then we'll have to do something. But we also added a little bit of code to handle primitive values, which we haven't seen yet. So primitive values in our example here, that would be the two string that are here, which are not really valid HTML at all, and also they don't have a type like the other ones. So we decided to handle them separately. And what we do for them, we switch on their type, and for string and numbers we will render them as texnode with the value within it. For undefined we will render nothing, so undefined. And for Booleans, if it's true p, then it's true actually, right? It's a Boolean, so it has to be true or false. If it's true, we'll render it as a string, as it sounds like a bit like a mistake. But if it's false, it's probably conditional rendering that was intended. So putting undefined here is good because then we won't render anything. And so your task now is to think about here how to handle children, right? So you will have to think a little bit about, think a little bit about how this works. Oh, and the last thing, this main render element now is just switching. If it's a primitive element, it renders it as a primitive element. And if it's not, it renders it as a stack. So you should be able to use that as well within your solution to make everything work. Going to check out the next branch. All right, which will be chapter one, step four? Step four. And just in case we'll move. And I will walk you through the changes that we did as well as next steps that we are setting up for you. So we were here right? And we were looking at how to handle our children. And the small part that is not here, but I will walk you through it after. But in case we have children, what we do from here is mapping through them. And for each of them, we are going to call our render element to HTML function, which is that one that takes either primitives or tag elements and transform them into something that we can render to the DOM. And once we have them as DOM elements, we can loop through them again. And we verify whether we actually have something or not. In some cases, we are returning a define for conditional rendering or for undefined. So we don't want to try to open that, but for all of the rest, we can append the child to the DOM. And then in the end, right, this will all get appended to the root element. So if everything went fine, and I refresh here, wait a sec, I think I would need to put back what we were supposed to have here before, which would be something with children, right? So let's say simple test. And that would be rendering it here. So that sounds good. And now the next step we have in our journey with GSX, which might be the most exciting, is components. So first we can look a bit at what a component looks like. And in modern React, that's probably a functional component that looks something like this. So this is a main title component.

7. Rendering Components and VDom

Short description:

We create the main title component, which takes a title prop and renders an H1 element with the class name 'main title' and the title as its content. We handle components separately from DOM elements and use the GetRenderableVDom function to render them. The render function handles primitives and components differently, mapping through children and rendering them accordingly. The VDom represents the current tree in React, allowing us to store it in memory and track changes. We can identify elements based on their position in the tree using index within the children array. Components are important for the VDom but are not rendered in the final browser DOM. We can represent the structure of JSX in a tree-like structure and use it to identify elements within the VDom.

As props, it takes a title and it wants to render a H1 with the class name, main title. And within the H1 will be title. And we would use it this way in GSX, right? So we will create the main title component and give you the string as a title. And this once transformed into JavaScript will look something like this, where the type now is the main title function that we have defined here. And within the props, the same as before, right? We receive our props in the normal way. And so now we have to think a little bit right about what does it mean when we receive a function here and how can we handle that? And we have started getting a bit deeper into the architecture of how we do things.

So what we did already is create here this GetRenderableDidum function that comes from the index and within here, we are calling it. And what we get here will basically be, the idea is components are not really something that we know how to render within the DOM. So they're not very important for DOM handlers, but for our react function, right in the long run, they are quite important because we need to know how to render them. So we decided that within this file, we would only have things that we know how to handle within the DOM. And on the other side, we can handle things that are more react specific like components. And let's go to our other index, to our index, sorry, not other index, that's the only index. And so we have our GetRendableVDom function, which is here, and we are talking about VDom here, and I will introduce that in a second. But first, I think we can look at how our render works. So renderer works pretty much similar to the other side, right? We have a similar function here that handles primitives and other components differently. And for primitives, it doesn't do much. It pretty much, okay, I will walk you through what all of this does in a minute, but it pretty much just returns the primitives because we don't have to do anything on them. And for our component elements, it is extracting the children and props, and in case we do have children, there is the extra step here of checking whether the children are an array or not, and if they're not an array, for convenience, transforming them into an array, and then mapping through them and also rendering, and then returning the children as the result of rendering them within this file. And then here would be where we will have to handle the components, so in case our type is a function, that means we are trying to render a functional component. So we are putting the type into this functional component, and here now, we'll have to actually render this, and we talked quite a bit about the vdum. So let's talk about the vdum. So the vdum ideas is to have a representation of the current tree that we have in React in a way that we can store it in memory, and we can have easy access to different components within it. And the reason, or some of the reasons why we would want that, one of them is later on when we try to introduce the hooks, then we'll have to know which component a hook belongs to. So we'll have to have some kind of representation of vertical, and over time, as we re-render, we will need to know, you know, like the previous state of this component was this. So when we re-render, we have to have the state again. And once we also get into diffing and targeted updates, at that point you will have the similar importance where we can compare the current one to the previous one so that we can actually understand what has changed over time. So let's think a bit about what that can look like. If we imagine an HTML page, something like this, where we have a main div contains as a first child, another div then this div contains as a child, a span, and this says, hello world! And the core div, the main div, root div also contains another child, which says another span. And we try to think about how we can identify all of those and the way React actually does it is based on where they are within the tree, right? So it's all based on their position in the tree. So one way we can represent this structure in a tree-like structure could be something like this. We have our top div, it has two children, and the first child is also a div which also has a child, which is span, which also has a child, which is a text, and the root div has a second child, which is span, which has a child, which is a text. Now, if we think of it like this, we could use their index within each of their children array to understand where they fall and use that to identify them. So if I take the same version but annotated, this top div is the root, right? So it doesn't really need like this ID and it is like basically root, and then within it, there are two children, and the first one could get the ID zero. So that's the children at index zero, and this one could get the ID one. That's the children at ID one, and then we do the same thing as we go down, but we just append to the already existing pointer as we go. So this is the first child of this div, and as it's also a child of this div, it will start with zero to identify this div, then a comma, and then within this div, it's the first child, and this one is the first child of this one. So it inherits basically the whole structure that went before it and then adds a comma zero, and on the other side, this is the second child. So that's index one and its child would be one comma zero because that's the index zero within this one. All right. That's not it. Yeah, okay. And the next thing we need to think of, right, which we already started talking about is some of the things are not actually rendering in the final browser DOM, but they're only important for our vDOM, and most of what this is are components. So if we imagine now this JSX, it has a div, a h1, then a section, and then a component here. But this wouldn't be valid in HTML, right? It only works in JSX slash React. But still, let's continue with this example. And let's imagine that our counter looks something like this. So it's a component that has a state, starts at zero, which is a count, and then it renders a div, a span that says the count is the count that is here, and a button that just increments the count if you click it, that has plus as a text. So that knowing this, we can guess that for the first render, this will be the structure that we have at the end in HTML within the browser. And in this case, right, the counter is not here anymore, but instead has been replaced by our div, which is rendered by our counter. So when we talk about the renderable VDOM, we are imagining this, right? Like without components.

8. Understanding the VDOM Structure

Short description:

And when we talk about the VDOM, we imagine a structure that represents the virtual DOM. Our.div element is similar to the GSX element, but with an added VDOM pointer. The render children are the result of rendering components or transforming children, with VDOM pointers added. Primitives are handled differently, with a type of primitive and their value as their value. Sections and counters are examples of rendered children, each with their own VDOM pointers.

And when we talk about the VDOM, we are imagining this, but this also contains children that have to be rendered later. Now, this is a bit of a scary graph, zoom in a bit. And it's trying to represent the way we imagine the virtual DOM. There would probably be a thousand ways to imagine this, but the way we landed on was to try to stick a bit to the structure that we have but handle the children and the components a bit differently. So the way it works is our.div is similar to, sorry, it has two elements, all of them. And the first one is the element that's quite similar to the GSX element that we dealt with so far, but we added this VDOM pointer, which is one of the pointers that we saw above, right? Points to where it is within our VDOM. And the second part will be the render children, which is basically the result of, for example, rendering the component or going through the children and transforming them and also adding the VDOM pointer to all of them. And if we think about the example from above, right? The first one was a H1 here, with the VDOM pointer 0 and render children with just one item. And within it, there was another one, and now primitives we're handling a bit differently. We will have them as type primitive and their value will be their value. And they also come with a VDOM pointer. And this doesn't have any rendered children. And on the other side of the things, right? We have our section, which has VDOM pointer 1 and has just one rendered child. And this rendered child is our counter, which is of type, the function counter has a VDOM pointer 1-0 and has 1 rendered child and so on and so forth, right? As we go down the loop, we continue having the same elements where the rendered children are the result of trying to render them. And the element is very similar to our j6 element, just slightly changed for practicality. Let's ignore this.

9. Understanding VDOM Pointers and Renderable VDOM

Short description:

If we wanted to point to the button, we would go through the tree, keeping track of the index. We use helper functions like get-v-dom-element and set-current-v-dom-element. The rendered children are handled differently from props.children. The renderable v-dom replaces the component with the div it renders, but the v-dom pointers remain the same.

So this is a bit of a recap of what we just said, right? So if we wanted to point above to the button, we would go through the tree and every time keep track of the index we went through. So first we would have to select the section, which is at index one within the root. Then we would select the counter, which is index zero within the section, the div index zero within the counter and the button index one within the div. So that would lead us to an array, something like this, 1001. And we will use those pointers everywhere to like find back elements in the DOM or set them in our virtual DOM. And we created a few helper functions to help do these kinds of things. One is get-v-dom-element, and you give it a v-dom pointer and the current v-dom and it will return a v-dom element. One is set-current-v-dom-element, takes pointer element and the v-dom that has both current and previous. And we will set this element in the previous v-dom at the pointer you passed here. And the last one is to create this v-dom element that we are talking about here and here, which you give a jsx-element to plus a pointer, and it will create the structure that looks something like this with the v-dom pointer as well and the element encapsulated and the rendered children. And so, if we imagine using those, right? We could try to do get-v-dom-element of this pointer in our v-dom the previous and that should return the button with the example above. And let's pretend we wanted to replace the button with a span. So our pointer would be the same and we would create our component v-dom element with children replaced and a class name as props. And it will be a span and have the v-dom pointer that we just set here. And then we would set it this way to replace it. And the last bit that might be a bit weird is about the rendered children, right? Why not using props.children? And the idea here was that we didn't want to mutate the props.children in the v-dom that we keep in case later on when we are defying we would want to have the original props. And maybe the last part between the renderable v-dom versus like the v-dom with all the components. So this is what the renderable v-dom for the above would look like. And as you can see, right, the only difference is the component is not here anymore. Instead, it's directly replaced by the div that it renders but the v-dom pointers are still representative of the structure we've components. So while this is v-dom pointer one, this is one, zero, zero because before we got to renderable there was a component here.

10. Rendering Components and State Management

Short description:

We set the different helper functions already within this function so that you shouldn't have to do too much. Your main task here is to think again about this functional component and how to render it. We were within our index and we were looking at our functional component. We had to call this functional component with its props and render it into things that are ready to be rendered within the DOM. We are now beginning with chapter two, which focuses on state. State allows us to store data within our components. React has introduced hooks, and one of these hooks is called use state. It allows you to set state within your functional component. The use state function takes the initial state as an argument and returns an array containing the state and an update function. In our app, we're now rendering components, but we need to update the state to see changes on the page. We'll focus on the use state function and the use of higher-order functions.

All right, that was a lot. Normally you don't have to deal too much with the v-dom just yet. We set the different helper functions already within this function so that you shouldn't have to do too much. And your main task here is to think again about this functional component and how to render it. So basically how to transform this function that should return GSX into actual GSX that can be rendered by the dom. And maybe the last thing as you go through it, right? Like for now is trying to just render this simple test which is here. If you manage this, then you can try to render a simple test with props where you would have to handle props. And then you can try to render a mega tests which basically uses the simple test with props so that you can be sure that everything works within your solution.

We are on tight schedule unfortunately. So I will go to the next step and walk you through what we did. And after that, we should have a little break so we can have some time to digest a bit what I just went through. That's quite a little already. Cool. So we were within our index up here and we were looking at our functional component but I'll ignore the hook stuff. And what we had to do was call this functional component with its props here. And then we can render that into things that are ready to be rendered within the dom and just return that. And normally, once this works, if we look here, we are rendering our app. And if I refresh the app in my brother, then we have a first version of the app working. It's all static, right? So nothing happens if you do anything. That's our next step. But it's already pretty cool to see that we have GSX working together with components and children of children of components are also handled correctly.

That seems like there's a few of you, unfortunately, experiencing some sound difficulty. Yeah, apologies for that. And also, yeah, we didn't test on other node versions. And so, node version 16 is the version that you require. Yeah that was, that's our bad, we have an oversight from us for anyone that's still struggling with that. And also, if you run git fetch from wherever you are, you should now, when you change branches, you should have access to the Vdom docks. Which we also didn't push, if you do little things there. But now let's begin with chapter two, which is focusing on state. And what is state? So we're gonna be focusing on local state, so similar to props or the data that's provided to a component, but state is private and it's fully controlled by that component. And it allows our applications to be dynamic. And it's essentially allows us to store data within our components. And before React kind of moved towards the functional programming paradigm it's now move towards... You had to use class components to set state, but yeah, it added hooks now React. And a hook is simply a JavaScript function that allows you to encapsulate code in it. And one of these hooks is called use state. And this allows you to now set state within your functional component so it's really handy. And on the right-hand side, you can see what this looks like. So as I said, a hook is a function, so this use state is a function and it takes one argument and that's the initial state. So for example, in this count, it's zero and then it returns an array containing two values. The first is the actual state and the data that we're interested in and an update function. So the second is the set count and that's what we use to update our state. So simply put, it's a function, takes a value, returns an array, which contains state and the updater function. If we head over to our app, you can see in this stage, so you should be on the branch chapter two, step one. Now you can see that we're now rendering components, which is great. So our app looks a bit more like an app should. But if we click our counter, for example, nothing's happening and that's because we actually need to state to update what's on the page. So let's head over to our code. And a key thing here is what we're going to focus on in terms of the challenge is the actual you state function, but we've had to add a lot for the state to actually work and be updated on every render, for example, and a key kind of- I'm going to run you through kind of what we did loosely, but a key idea and concept is this use of higher order functions. And that's a function that returns a function.

11. Initializing Functions and Higher Order Functions

Short description:

The createRoot function in our dom handlers is used to initialize a function that can be called later on. The render method is called once when we first render our application. We've also added a new function called start render subscription, which is an example of a higher order function.

And this has been super useful for our implementation because it allows us to kind of initialize a function and then call that function that's returned from the function later on, such as when we update a function, update the app or on re-renders. So let's start in our dom handlers. We've got this createRoot, which we've already looked at, and the createRoot has function on it. There's a render method that gets called on once when we first render our application. It takes the route child element, which are react App, which reacts to use users then to know what to render the application inside of. Here we've added a new function, start render subscription, which is an example of one of these higher order functions.

12. Initializing Hooks and Handling State Changes

Short description:

On render, we subscribe to DOM changes driven by state changes. We instantiate the start render subscription function to listen to state changes and make updates. If it's the first render, we append the child to the root element. Otherwise, we replace the child. We create hooks to handle state changes and pass the update function to the hooks. The create hooks function initializes hooks for each component using a hooks map referenced by the VDOM pointer. Each hook object contains a state field with information about the component's state. The register hooks function is a higher-order function that creates hooks for each component on the initial render.

It takes two elements, the route child, and then another function. So what this function is essentially doing is on render, it's allowing us to subscribe to DOM changes, which are driven by state changes. So, we instantiate this, can people hear me, I just saw something around using sound. Yeah, okay, cool, I'll continue. So yeah, on our first render, we're kind of calling this a start render subscription, which allows us to subscribe and listen to state changes so we can make updates. And it takes the root child and also then a function, and this is the function that we'll be calling on our updates, which takes the render all vDom, so we know what to update. And here, if we're looking at, if there's no last child, it's pretty much the first render because nothing exists. So we're appending the child to the root element. If there is a last child, that means we already have elements on our page and we used the replace child rather than appending it. So we've got this function here, this is the key thing that kind of persist throughout the app and it's like our update function. So where is this called then? If we had, we're now into our index.js or define, sorry, I meant, so this is where the startRender subscription function is defined and remember it takes that element and then that second argument was the update function. So the function that handles that updating of the DOM. So our actual function here, this is where we kind of store our previous VDOM, the virtual DOM and the current one. And this is called once, remember all the code here, this is where we're going to create our hooks so the definition for what are hooks, remember the useState function is a hook, it's just a function that contains some code. But this is where we initialize it on that first render. So we need to create our hooks. So our application knows how to handle and deal with them. And then we pass into our creation of the hooks, this update function here, which contains our update callback. So what's happening is here is we're passing in the update function to our hooks. So when our state changes, for example, we can call the update function and our DOM can get re-rendered in the correct way with the correct information. Yeah, this is quite like complex and we're using like the higher order function so it's a little bit difficult to follow, but yeah, I wanted to kind of run through you so you at least know the kind of work behind the scenes. So let's look at this create hooks function, which is where we're kind of initializing our hooks on that first render. And we've got like a new file that's been added on this branch hooks.js. This is the file that will handle all of our like use state logic. And this is the function then that's called on our first render. It takes the update function. Remember, we want to know when state changes, we want to call the update functions so that we can actually make changes to our DOM. And here, key thing is we're creating our hooks map. Now this is a map, and it contains all of the hooks of our application, but it's referenced by the VDOM pointer. So we know which hook relates to which component. So for example, hooks map with the following VDOM pointer 0 0 0, is the hook for that specific component. So we can identify and reference the correct hooks. And it's important to know that, for example, if I called this each hook has the following structure. So it's an object which contains a state field, which is an array because, each component can have multiple states and also affect we'll get onto that in the last chapter of this. So the key thing we're here is we're focusing on is we have a hooks object for each component. We can access that hook by using the VDOM pointer, which is kind of our like reference. And then on that hook, we have a state field and that contains information about that components state. And another thing important to hear, so this is like the initialization of the hooks. We then like creating what that logic looks like here. And we're also registering the hooks. This is very important because let's have a look at what that function is. It's another high order function. So we call this once on our class render. We give it the hooks map. And we give it the function that creates our use state and it returns a function. And then this function is called for every single component with the VDON pointer. And if it's the first render. So this allows us to create hooks for each component when we need it. And when is this? So it's important to note this function is called on the initial render. And then we have this register hooks function.

13. Registering Hooks and Handling Renders

Short description:

And then that is also a function that we can call for specific components. So we can create hooks for that component. So when is this register hooks function called? If we go to... The zoom bar came up in my way. So that register hooks function, which is kind of responsible for handling our hooks is kind of registered on every render.

And then that is also a function that we can call for specific components. So we can create hooks for that component. So when is this register hooks function called? If we go to... The zoom bar came up in my way. So that register hooks function, which is kind of responsible for handling our hooks is kind of registered on every render. So we call the register hooks when we are in our render method, this is our render component element. So when we want to render a component, which is the type function, we know that we need to call that register hook functions with the specific feed on pointer for that component. And if it's the first render, and that's gonna replace the existing hook that we have with the new one and create the required hooks for that function. So the data doesn't become stale and it remains updated. So, yeah, this is the function.

14. Creating useState Function and Handling State

Short description:

This is the key part: returning the function that gets called when you're in your app and you type in use state. It expects initial state, can be null, and returns an array. The first value is the state and the second is a function responsible for updating the state. We need to think about how to store the state and set up each new state. This part is specific to React and checks if it's a function for Lazy State Initialization. We need to update the set state function to make comparisons with the previous state and the given value. We also need to update our app and return both the state and the update function. The creation of the use state ensures that the data is specific to each component by utilizing the hooks map and VDOM pointer. The current hook value is initialized, and in the useState function, we can access the current state and update it. State is an array because a component can have multiple states.

This part of that function is getting called that. With the V-Don pointer is first renter. And then, so under, when the component's getting rendered, this gets called, and then that's when we wanna use that information to create the specific useState for that each component. So we have this make useState function, and we're passing through the V-Don pointer, which is the reference to the component and is first render. And then let's have a look at that part, which is the part that we're specifically interested in, which is the part that gets called when you're in your application, you're in your component, and you're then referencing useState. So. Here it is. This is where the logic, and this is what we'll be tackling first happens. So this is... So again, this higher-order function kind of logic, where this gets called on the first render. This part, this function gets triggered. It gets triggered with the onUpdate callback, which is important because then this needs to be called in our useState in order to update the DOM. So we need to update what's actually rendered to our page. That's the function that handles that. We have the hooks map, so we can retrieve the specific hook that we want and relate that to the specific component. And then we have the function, this next function that's called for each component and it's given the VDOM pointer and if it's the first render. So using all the variables available, we can then get the correct hook or the correct state logic from our hooks, we can kind of compare the values and then we can trigger state updates when there are changes that we're interested in and then also update our DOM from within here. So what's this function doing? So this is the key part is returning this function and this is the function that gets called when you're in your app and you type in use state. This is what gets returned and as I said at the beginning, that use state expects initial state, it can be null, and it returns an array. The first value being the state and then the second one, which we're gonna replace is a function that's responsible for updating our state. So the key things before I let you tackle this is we need to think about how we wanna store the state so that we can still access that state and make comparisons whenever that function is called. So that's kind of the first step of this task. How do we store the state and store a value that we can then reference? And we then also need to consider the setting up of each new state. So the fact that we need to return an updater function. And we only wanna do that on its first render, hence why this is first render block because we only wanna set up the update function once and then we can use that same definition of the function throughout the life cycle. Yeah this part is specific to React. The reason why we're doing this and checking that it's a function is because React allows you to, has a feature called Lazy State Initialization and this essentially allows you to pass in as that initial state value a function and React then sees, oh, you've passed in a function. I'm gonna call this once and only once. Which allows, it's called Lazy State because it allows you to pass in or encapsulate highly complex or functions that have a high computational value such as when you're using local storage to only be triggered once on the first render and not over and over again. So that's kind of taking consideration of that. But that's really done for you. And then we need to update our set state. So this is our data function that will need to return. And we need to handle in that update function we wanna make the comparison of our previous state which we stored already. That's our first task. And then we're making the comparison of the previous state and the value given. And then we then also need to update our app. So remember what we're given here at the top. And remember that we need to return both the state and our update function. And there are some hints in the code. If we check out chapter two, slash step two. And then let's have a look at what we did. So, and we realize we're throwing a lot of information at you and asking quite a lot. I think this workshop could be done in a week rather than three hours, but yeah, thank you for your patience. So the key thing about this creation of the use state in this first part is ensuring that the data that we set is specific to that component. So we want that hook to be tied to a specific component. And the way that we do that is we initialize the current hook value and we utilize this hooks map that's passed in, which again is the collection of all the hooks in our application, but with the key being the reference to the VDOM pointer that that kind of represents. So, and we can use the VDOM pointer, which is the specific pointer for that component and that's how we can store and also retrieve the specific hook for each component. So we have our current hook now. And then when we move into our actual useState function, we can then in the updater function, which we've called setState, we can access the current state and at the moment we're taking the first value of the state, remember state is an array because a component can have multiple states.

15. Updating State with Multiple States Per Component

Short description:

We're taking the first value and then we are comparing it to the value that's passed in, so the new state value. And then we're taking the previous and the current. You've got this helper function, this stateStiffer, which just does a simple check on the previous state and the new state. And then if it should update the state, we're setting the state value on our hook. So the one up here, currentHook, we're changing that value to represent the new state. And then the important thing, so that we can reflect the changes on our page, we need to call our update function. And that was the function remember that's passed all the way through and actually makes the changes to our DOM elements. And of course, this is where then we make the final changes to the initial state. And we return the updates function too. And then the final thing is returning that state from the use state function. Let's have a look at what that means for application in this current state. So I made the error of saying that the counter should work. The counter shouldn't work at this point, and we'll see why. But, if I add an item now, we can see that item is added correctly to the to do list. But something weird has happened in our counter field. It's taking the state that we set for our to do list, and it's put the state in our counter as well. Clearly that's not what we want. And that is because our current implementation is only accounting the component can have one state per component. So if you can see if we go to the code, it's coming up with that. So this is essentially, if you look in this common, what our kind of app component looks like. It has the counter in the set counter, and then the same component, it also has, well for in the app it's the to do items. So the items. So our component has two states, but we are only updating one state and assuming the component can only have one state. These two states are sharing the same state at this point. So what we need to do now is we need to allow for our new state function to handle multiple states per component. So that each kind of state gets updated with the correct value. And a key thing to know that is how React handles this or it handles it sequentially, so it's by index. So this is the first index that we have, the index of zero, this first one then the next one will have an index of one and so on. And remember our state value in our current hook here is an array of the different state values. So current hook dot state with an index of zero will return our counter, current hook dot state with an index of one will return, in this case the clicker. And that's what we now need to account for. We need to somehow store this index so that in here we know which state we actually are referring to and wanna update. The next branch, which is chapter three step one. That was the final step for the new state. We're checking out chapter three, step one.

We're taking the first value and then we are comparing it to the value that's passed in, so the new state value. And then we're taking the previous and the current. You've got this helper function, this stateStiffer, which just does a simple check on the previous state and the new state. And then if it should update the state, we're setting the state value on our hook. So the one up here, currentHook, we're changing that value to represent the new state. And then the important thing, so that we can reflect the changes on our page, we need to call our update function. And that was the function remember that's passed all the way through and actually makes the changes to our DOM elements. And of course, this is where then we make the final changes to the initial state. And we return the updates function too. And then the final thing is returning that state from the use state function. Let's have a look at what that means for application in this current state. So I made the error of saying that the counter should work. The counter shouldn't work at this point, and we'll see why. But, if I add an item now, we can see that item is added correctly to the to do list. But something weird has happened in our counter field. It's taking the state that we set for our to do list, and it's put the state in our counter as well. Clearly that's not what we want. And that is because our current implementation is only accounting the component can have one state per component. So if you can see if we go to the code, it's coming up with that. So this is essentially, if you look in this common, what our kind of app component looks like. It has the counter in the set counter, and then the same component, it also has, well for in the app it's the to do items. So the items. So our component has two states, but we are only updating one state and assuming the component can only have one state. These two states are sharing the same state at this point. So what we need to do now is we need to allow for our new state function to handle multiple states per component. So that each kind of state gets updated with the correct value. And a key thing to know that is how React handles this or it handles it sequentially, so it's by index. So this is the first index that we have, the index of zero, this first one then the next one will have an index of one and so on. And remember our state value in our current hook here is an array of the different state values. So current hook dot state with an index of zero will return our counter, current hook dot state with an index of one will return, in this case the clicker. And that's what we now need to account for. We need to somehow store this index so that in here we know which state we actually are referring to and wanna update. The next branch, which is chapter three step one. That was the final step for the new state. We're checking out chapter three, step one.

16. Optimizing Rendering with the Diff Module

Short description:

To optimize rendering, we need to calculate what should be re-rendered instead of rendering the whole page on every state update. We'll start by implementing the diff module, which identifies five types of diffs: node-added, node-removed, node-replaced, primitive node-update, and props-update. The diff module includes helper functions to create objects that describe the diffs, such as node-added and node-removed. These objects contain information about the position and type of the diff, as well as any necessary payload. By implementing the diff module, we can determine the specific elements that require re-rendering based on changes in the virtual DOM. This optimization improves performance and prevents unnecessary re-renders.

Okay, so the key part of this step was firstly we need to somehow track the index and store the index for each state that's created. So, we have a current hook that denotes which hook we're referring to and we also created this, at this point, just a state index reference. And we're kind of using this reference of using current to ensure that the values persist and are updated and don't remain stale. So, we've got our index value, which we're initializing at zero to begin with. And then we come into our use state function, so mine 29. We take, so remember this new state is gonna be called for every state within each component. So, the first time we see a new state and a component, we're gonna run into here. We're gonna take state index current, which is zero on the first one. Then what we're gonna do is, cause we're initializing here to its own variable, we're then gonna bump the current value by one. So when we enter this function again for the next new state, that's called, that value is gonna have an index of one and that's how we're differentiating and maintaining the index through our different new state cause. So, we have the state index that represents the correct index of the state that we want and where do we need to use this? Well, now when we have a current hook state, remember state is an array of all the values, we use our state index so we return the correct state and we're making the comparison on the correct state value. The update remains the same and then where then if there has been required changes in our updater, we need to apply those changes to our state on the current hook. And then of course we're returning the specific state at that index. So a few changes that need to be made but it was key thing is kind of storing that index and updating it correctly for each call of the use state function. And if we go to our app, you can see that the counter is now working, and then if we add something to our to do list, that works as well and doesn't update the counter because we are catering for different states within a component. So we left at this working, right? But one problem we have now is every time a state updates, it will rerender the whole page. And one way you can see this is when you start typing something here, you can see that I lost the focus and I will lose it every single time I type something. And that's because every time, right, the whole page is re-rendered. So it's a fully new input that is put down here, which means it will obviously like keep on losing focus. And on top of that, it's obviously not very performant, right to re-render every single element on the page on every single state update. So our goal now will be to think about how we can calculate a bit better what should be re-rendered within this whole page and to try to only re-render those things that should be. And the first part we will look at for that is called the diff. And within this chapter three, step one, you will find a new module here, which is called diff. And maybe before we run through it, let's go to our index. And for now, what we added in our index is just a calculation of the diff here, right? We are calling this new function that we will walk you through to get what is the diff between our current renderable vdum versus our vdum that has the previous. And for now we are only logging this diff so we can verify right what happens. And so if I come back to my app, we can see here we got a bunch of diff items, and for now you can see that they're all props. And let's walk a bit through this diff module where you will have to modify things later on. So when we thought about the diffing, we thought about five types of diff that can happen. One of them is node-added, which means a new node was added to the vdum at a position where there was no node before. One of them is node-removed. So a node was removed at a specific position where they used to be a node. One of them is node-replaced. So a node that was at the specific position now changed to a completely new node. One of them is primitive node-update. So a node that has, for example, a specific text now has a different text. And the last one is props-update. So some of the props of a specific component or tag element have been updated between the previous render and the current render. And within props, we have two different types. One is update, so the prop existed already, but has a different value now, or remove, the prop existed before, but does not exist anymore. And to facilitate a bit the task here and to avoid that you have to think too much about some of the structure. As you go through, we created a few helper functions where we create this type of objects that will help us describe our div. So for example, this one will create a node-added div. It requires to have a Vedon pointer, so we know where to look for the element that we want to... At which position, the new element should be added. It's of divtype, not added. And as payload, it has the node that we want to add and the parent pointer, so it's a bit easier to find where we need to add this node and add it within the real DOM. And we did the same for node-removed. It has the Vedon pointer and the type node-removed, and it doesn't need any payload. And then we have node-replaced.

17. Creating the Div of Changed Elements

Short description:

The function creates a div of all the elements that have changed between the previous render and the current render. It takes the current renderable V DOM element and recursively compares it to the previous V DOM. The div is an array of div items, each representing a type of change, such as node-replaced, primitive node-update, or props div. The function also handles the calculation of props div by comparing the previous and current element props. The recursion allows for traversing the renderable V DOM and building the div from the bottom up. The function ensures that the parent pointer is correctly assigned to avoid issues with rendering components. The goal is to optimize rendering by only updating the necessary elements based on the changes in the V DOM.

Takes our current element so that we can add the Vedon pointer and the new node, the old node, which we also pass down to the payload and the parent pointer, and its subtype node-replaced. And we have our primitive node-update, takes our current element and the new element, which will be this basically, will hold the value of what we have to change, and this gives us the Vedon pointer here and the subtype primitive node-update. And then we have our props div, which is going to be applied at a specific Vedon pointer so that we can find a component where to apply it. It's of type props div, and as a payload has change props, which is basically a mix of props removed and props updated. So to create props removed, we only need the old value and its props-removed type, and for props updated, we need the new and old value, and we pass that down to the object so that we can retrieve them later on when we'll try to apply those div, and it's of div type updated.

And then here, this is something that you will not need yet, but will need later on when we are trying to apply these divs to the DOM. The order in which we apply them matters, because for example, if we remove a node, then, you know, it will shift some of the indexes. So we created this small array to explain in which order they should be applied, and then we get to our main function that we saw on the other side, which goal is to like create a div of all the elements that have changed between the previous render and the current render. And a div will be just a big array of div items where each item is one of the types that we saw above. And what this function takes is our current renderable V DOM element. So at first, this will be the whole renderable V DOM, and then we will go recursively for this function. So as we go down, right, it'll be sub trees of the root until we reach some of the elements that reach the ending condition. So for example, primitive elements, right, we don't have any children to go through anymore. And then we'll go back up to build the whole div. It takes the V DOM so that we can have access to the previous V DOM so we can compare things. And if for convenience, it takes the parent pointer, that's mainly because within our renderable V DOM, as you remember, we don't have components anymore. So eventually your parent might not be like within the renderable V DOM, your parent, your direct parent, as in, if you remove it, just your own index of the pointer might not be present in the renderable V DOM. And it might be something above it that renders it. So that just avoids that we have to look for those. And now we can get into creating our div. So the first step is finding the previous element from our V DOM dot previous. And here would be your first task. If we don't have a previous element, this will be a specific type of div that we want you to add here. And if we do have a previous element, then we will access its dot element because this is a V DOM element. So it has the element and the rendered children. So this is like the actual final element that we would render. And for the current renderable V DOM element, those don't have the rendered children, so it's directly the element. And we will compare their type and if their type mismatch. So for example, it used to be a primitive and it's now a div, then we will create a non replaced div type. And if none of this was true, we continue down. And if they are both primitive elements, so the previous element and the current element are both primitive, then we want you to replace this condition with the final version of it. And this should create a primitive node update. So you have to think about what condition here would create a primitive node update. And there is a small also reminder of how a primitive node looks like. So it's a bit easier hopefully to go through. And then here we are trying to calculate the props div. So to do that, what we do is we look through all the keys between the previous element props and the current element props. And we use a set here so that we get a unique, a collection of unique prop keys. The idea here is that both of those might have different props, right? Like maybe the previous element had more props than the current, but we want to look through all of them so we are sure not to miss any when we are calculating the div. And then we look through all these keys. Children will be handled later, so for now we ignore it. And our current prop value is current element props of key. Our previous prop value, same thing, but with the previous element instead. And here if we don't have a current prop value, then we create a div type removed for this specific prop. And if we have a current property, previous prop we compare its value to the current one, and if they are not equal, then we create a props div type updated for it. And here we create a div array, so we will be able to store more div items within this function. And if we had any change props div, then we can create a div props, right? So that we keep track of all the props that have changed. And now the fun part, the recursion. So as we said, we ignored the children so far. Here we will take our previous children.

18. Looping through Elements and Calculating Diffs

Short description:

We loop through all elements between the two arrays, considering the lengths of the previous and current elements. If there is no current child, we return a specific diff. If there is a current child, we enter recursion, going deeper into the subtree. We keep track of the current diff and add the diff items calculated for the subtree. Finally, we return the final diff.

Again, this is a bit weird, right? But this is a regular vdom elements, so we are using this render children to represent the result of like calling the component or like going through the children of an element and adding vdom pointers. And on the one that we actually want to render, we have replaced props.children with what we want, so we can use props.children here. And now we want to loop through all elements between these two arrays. So the way we decided to do it is we look at their lengths and we will take the maximum of the two lengths. So we are sure that if this previous element has more children than the current one, then we will still look through all the children of the previous and the other way around, right? If this current one has more children than the previous, then because we are using the MAX Lengths, we are sure to look through all of them. So then we will look through our index and for each of them, we are looking in the current children, at this index and we are putting it into this currentChild. And if we don't have a currentChild, that would be something for you as well to think about what does that mean in terms of a diff? So here we should also return a specific diff. And if we do have a currentChild, then we can enter the recursion, right? So we can call this function again. But our subtree is now one level deeper. Our vidum is still the same, right? We keep on accessing through vidum pointer. So that's all right. And our parent pointer is the current element that we are in the recursion at four. So with this, we'll basically write like, go down every children of every children of every children until we reach diff items that are above, including the primitive node that don't have children, right? So for primitive node, we wouldn't run into this at all. And at some point, all of them will return one by one into this subtree diff. And what we do in this subtree diff is, we keep the current diff that we already had, right? This is all the diff items that we calculated so far. And we are adding all the ones that were calculated for the subtree. So we are sure to have like a very, very long list of all diff items that we found while going through the whole tree structure. And once we have our final diff, we can just return it and be ready to go.

19. Applying Diffs to the DOM

Short description:

We need to understand the different types of diffs and how to apply them to the DOM. The three main types are node added, primitive node update, and node removed. For a node added diff, we create a new node when there is no previous element at that position. For a primitive node update, we compare the values of the primitive element and create an update when the value changes. For a node removed diff, we create a removal when there is no current child at a position where there used to be one. To apply the diff, we use diff applicators, which are functions that apply specific types of diffs to the DOM. The diffs are applied in a specific order, and we use a rendered elements map to keep track of the rendered DOM elements. The diff applicators are called with the rendered elements map and the diff item to apply the changes. We have already implemented an example of applying a primitive node update diff.

And so as a quick reminder, the three things we would like you to have a look at, thinking about what type of diff it is when we cannot find a previous element. Thinking about which condition would lead to a primitive node update. And thinking about what does it mean if a previous child existed, but there is no current child at the same position. So let's start with a what we're looking at here that you had to do. The first one was what happens if there is no previous element at this position. And if you guess this should be a node added, you were correct. So whenever there is no previous element means the element that we are trying to add now didn't exist in the previous VDOM, which means we should create a node added. And on the primitive element, if so in which condition do we consider a primitive node was updated. The solution here was to check the value of each primitive element. And as long as this doesn't change, we know that we are rendering the same thing. As soon as it changes, we should create the primitive node update. And this can be a simple comparison because here, right? The both of those are primitive values. So this should be safe to compare. And the last and most complicated part was probably here. What happens if we don't have a current child at the position that we used to have one. And when that happens, it means that a node was removed from the DOM. So we create here, a node removed, diff item. So now we have the right diff being calculated. And the next step will be, how do we apply, oops, wrong. Fine, how do we apply this diff to the DOM? So if we are coming back to our DOM handlers where we actually implement things that happens within the browser. And we look at our render subscription. Now we have added in the callback that updates, that will try to update the DOM, we have added our diff here. And the idea from no one is, on the first render, we are still doing the same thing we were doing before. But after that, we will try to apply the diff instead. So that we are not completely right, we are not upending the child again or replacing the child like we used to. But now we would like to only apply the changes that are mentioned within this diff. So let's look a bit at how we structure that. So we can help you going through the tasks at hand. So what we decided is to create those diff applicators, which is a map where the keys are the type of diff that we're trying to apply, and each value will be a function that will try to apply the given diff to the DOM. As you can see, not all of them are completed. That's okay, we'll complete them afterwards. But now let's look at where we are trying to apply them. So we have this apply diff here. It receives the diff that I just mentioned, and the first thing it will do, as we mentioned, the diffs have to be applied in a certain order. So it will sort them to make sure that we will try to apply them in the right order. And then we will look through them, and for each of them, we will find the applicator, and we will call it. And as you can see, we'll always call it with two arguments. The first is a rendered elements map, which is a convenience we decided to create, whereby we are keeping track of the actual DOM elements that we have rendered. And the idea of the map is, it will associate vdom pointer to actual elements rendered within the DOM, so that we can easily find them back to apply the changes that we need to apply. And the renderable vdom is useful sometimes to find back information about certain elements. I think we will have an example of that, so we can walk through that maybe with the example. And finally, we have our diff item, which is the item that we create on the other side, right? So that will have a vdom pointer and the payload of the diff, so we can understand what to do with it. Now we have implemented already one of them for you. So you can have an example of a simple one that can maybe help you also go through implementation of the others. I mean, we will not try to make you implement all of them. Don't worry. So this one is the one to apply a primitive node update. So as mentioned, right? It gets the rendered elements map in the first argument. It would also get the renderable vdom, but that's not useful for this one. And it would receive the vdom pointer from the diff item.

20. Applying Diffs and Updating Rendered Elements

Short description:

To apply the primitive node update diff, we update the rendered elements map with the new element value. For prop updates, we iterate through the props changes and apply the new values to the corresponding elements in the DOM. The node added diff requires converting the GSX node to HTML and finding the correct position to insert it using the findNextSiblingOfVDOMPointer helper function. The rendered elements map keeps track of all elements and needs to be updated with each diff item to ensure accurate rendering.

And this specific diff item, the primitive node update comes with a payload that has a new element. So we also get that. And what we do is we take our rendered elements map, which holds all the actual elements within the DOM, and we access it for the vdom pointer that we told us the diff was for, and we will update its nodes value, which is basically the text that it renders, and we will put inside the new element of value and the new element we received from the payload. So that should replace now for primitive nodes that should replace correctly, that should replace them correctly within the DOM, and maybe we can have a quick look. So this counter, for example, is just a text. So within it is a primitive node. And if we try to do plus, that works, that's cool. But as you can see, this one doesn't update because this is a prop update. So we don't have yet the implementation for that. This doesn't update either. And, for example, this also doesn't update. So we cannot add elements. So we'll have to figure that out. And the first thing that we would like you to look at is the apply props, which I think is amongst the simplest, not simple, but amongst the simplest application of diff we have. And so same thing as the primitive node update, it will only care about the rendered elements map. It gets the v-dom pointer from its diff. And there's a payload. It gets the props changed. And the code we wrote already for you is to look through all this props changed. And for each of them, we'll try to apply them. And each prop's change has a key, which is the attribute that we should try to replace, a prop diff type, which will be either updated, either removed. You can ignore remove. We won't be asking you to do it. So you can focus only on updated. And it gets the old value and the new value. And here's the code that we would like you to think about. So the first thing you should try is to think about how to apply these new value for this attribute to the right element within the DOM, which the rendered elements map should help you with. And if you get through this, you can look into what should we do for event handler. There might be something a bit specific we have to do for them. And the last thing you can try if really you are a superhero and you went through both of those is the node added, which is here. And this one is much more complicated to apply, so it gets also the rendered elements map. This one also needs the renderable VDOM. And then same thing, the if item has the VDOM pointer at which we want to add the element, the node that should be added, and the parent pointer so that it's a bit easier to find the parent. But here there are several things that you have to think about. The first thing is that this node is GSX, so we would need it to be HTML before we tried to insert it. And then we would need to find the right place to insert the HTML we created. And for that, we created a small helper function here, which is called findNextSiblingOfVDOMPointer. So you can call it, as you see, with the same things that the node added applicator receives and a VDOM pointer and a parent pointer. And what this will return is, within the DOM, which element should be the next sibling of this VDOM pointer so that you know where to insert it basically afterwards. We won't go through the implementation. We don't have enough time for that. But, of course, you can always go through it later on after the workshop. And another thing that you would be careful of if you get there is that some of the nodes don't actually render anything to the DOM. So, for example, undefined is valid to have within this function. But it shouldn't actually render anything within the DOM. And the last part is this rendered elements map where we keep an index of every single element. We need to make sure that we update it every time we do a def item update so that the next ones have the right map. And maybe one last thing on this rendered elements map, for now, it might look a bit mysterious how it works. But technically, so far, the main thing of how it works is here. So anytime we are trying to render something, here, we will create the HTML version of the element that we receive.

21. Rendering Elements and Handling Side Effects

Short description:

For the props updated task, we use the apply prop to HTML element function with the key and new value. We retrieve the element from the rendered element map based on the VDON pointer. For event listeners, we remove the old listener before adding a new one. For the node added task, we find the parent element and next sibling using helper functions. We render the node and insert it before the next sibling. The rendered element map is updated accordingly. The diff applicators and other details can be explored in the repository. useEffect is a hook that allows components to handle side effects independently of the render function. It can be used for tracking events, animations, and local storage. By using useEffect, we can persist data in local storage and handle it separately from rendering. The code within useEffect is triggered on every render.

And for this element, video pointer, we will put within the rendered element maps the rendered element. So that allows us to always keep track of the different elements that we are creating. So as a recap, hopefully, OK to go through is here. If you are a hero, you can try this one out. And if you are a superhero, you can try the node-added one. And we will walk for you through the solution afterwards. And I'm realizing you forget what I said about superhero. You are all superhero for still being there. So let's go through the branch for the next step. And I will walk you through the things that you had to do. I realize that this is missing a few of the other applicators, but it was just too much material to go through. So you can always go to the repository and go for it. And as Straub mentioned, we are thinking to also create blog posts around it. So that might be also an easier way than going yourself with the repo that should come out later. So in terms of the props and specifically the props updated which was one of the tasks that he had, for the regular case, we can pretty much use the same apply prop to HTML element function we already have and give it the key that we want to change and for the value, the new value. And then the element is we can just retrieve it from our rendered element maps, elements map based on the VDON pointer. And the extra thing we have to think about is in case this is an event listener, if we re-add it, then it would keep on adding and adding and adding event listeners and never cleaning them up, which wouldn't work very good. So instead, we have to remove it first. So we created this small Unity function that pretty much does element.removeEventListener and then uses the correct final key for the event and removes that old function that we have here. And for this slightly more intense node added, which is here, what we did is we find the parent element within the rendered elements map from its parent pointer. And then we find the next sibling with this helper function that we described, so we know where we will need to insert it. And then we render our node, right? So, that we have here, HTML ready elements. And then if this is something that isn't renderable, so probably undefined, then we won't render it, but we still update our rendered element map so that it also has the undefined at this specific position. And if it's not something that we cannot use, then we will insert it into the parent element and we will insert our newly added node before the next sibling that we retrieved from the function up here. And then we don't forget to also update our rendered element map, so we are sure that always this is up to date for the next renders that we could go through. And so, yeah, in the interest of time, we won't go through all the other diff applicators, but you can certainly have a look on your own afterwards. And I will hand it over to, oh, maybe one last thing before leaving you right now, everything here should start to look like it works. So that's pretty good. And as you can see, we are not re-rendering every time because this keeps the focus, so that's what we wanted as well. And I will hand it over to Sean for the last part about useEffect.

So useEffect, this is the last chapter I've made. I'm going to just do one step in this chapter. And so what is useEffect? UseEffect is another hook that's provided by React. And a hook again, is just a JavaScript function that contains some logic that we wanna run. And, well the good thing about this useEffect hook is it allows components to handle side effects. And what we mean by a side effect is logic that we wanna be independent of the render function. So some examples of tracking events that we wanna fire independently of what's rendered, animations, and also local storage. So, currently our application. Yeah. Hello. Currently we can do everything. Looks great. But if I hit hard refresh, I lose all the data because it's not persisting. How can we get around this? Well, we can use local storage to store some data that persists on refresh. But in order to do that, we kind of need to do that in a side effect. So we need to update our state, but also then we need to kind of handle that independently of what we're rendering. And that's what we can use effect for. So for example, we've got a simple component here. Here, we've got the state count and the update the function that we've seen. And here in this part, if I set local storage set item like this within our component, the code here is getting triggered on every render.

22. Using the useEffect Hook for Side Effects

Short description:

We can move this line into a user effect and in this user effect is a function. The use effect hook allows us to specify that the function should only run if there are changes to the count or any value in the dependency array. It encapsulates the logic as a side effect that is independent of the rendering of the component.

Now we don't want to do that sometimes for this local storage. We don't need to set the item on every render. So how can we do that? We can move this line into a user effect and in this user effect is a function. So let's look at the kind of construction of it. Again, like you say, it's a function that we call, but it takes a function. As the first element, again, and then also the second element is an array of dependencies. So looking at the first element is a function, which is the effect. So the function that we want to call to trigger some sort of code or some logic. So in this example, we've got a function that when it's called is setting the item to local storage. And the second element is an array of dependencies. And the cool thing about the use effect hook is we can tell this effect, this first argument, which is a function to only run if there are changes to the count or any value in that we include in this dependency array. So this function will only be called if the count changes. And one thing to note is it will be called on first render of the application. But apart from that, the count then will be, it will kind of listen to changes in the count in this dependency array. If there's a change, then we'll hit local storage. And that's cool because we're kind of encapsulating it and we're enabling this logic to be a side effect that is independent of the rendering of the component. If it was up here still, we'd be setting it on every render, which is not what we wanna do. But this allows us to handle logic away and independently.

23. Handling Effects and Triggering Callbacks

Short description:

We need to make changes to handle effects, which are functions that need to be called after the DOM is re-rendered. The after update callback is responsible for re-rendering the DOM and triggering the effects. The create hooks function initializes the use effect logic and passes the hooks map and register on updated callback. The register hooks function is responsible for calling the hooks on each render, and we need to do the same for the use effect. We define a callback of effects that triggers all the current effects. This callback is responsible for calling all the other functions. The effects need to be called after the render, so we have a function called register effects for after DOM update that instantiates the database of effects and triggers the current one.

So let's go over to our code base, which is here. And you can remember from the use state chapter that we had this start render subscription function, which is called on every render. And that essentially as subscribes us to listen to state changes, and handle the kind of updates that we need to re render our application and make changes to the DOM. So now that we're looking at the use effect hook, we also need to make some changes here to account for that. And the important thing to know is I mentioned it before, but we want our effects to be triggered after the render. A good example of why, because with animations, for example, we for the animation to work correctly, the logic needs to know what's actually on the page so it can update the correct elements. So we know that we need to call our effects, which are those functions that contain the specific logic we wanna trigger, we know we need to call them after our DOM is re-rendered. So if you see here, we are kind of calling this after update, this update callback is responsible for re-rendering the DOM and calculating our differences. And then after update, that's where we're gonna trigger our effects. So all those functions and our components that we wanna handle independently of the render or after the render. How we do that, so what is actually happening in this after update, we're just creating this register on updated callback, which is a function that we'll take a callback, so a function itself, we're saying after update to a function, so after update is a function, and then we're passing this function to our create hooks, which will then when the update function is called will be triggered. So let's move into our create hooks. Zoom bar again, getting in the way. So we're in our create hooks function now. Let me go to the bottom where we're initializing it. This is initialized on the first render. We need to, just like we did for state, we also need to now handle our user effects. And remember, hooks contain the following structure and it's an object with state, which is array and also now our effect value, which is also an array. So it can contain an array of all the effects of each component. You can see here that like similar to the make use state, we've got a make use effect. So on first render, we're creating our use effect logic that needs to happen to set up the kind of use effect functionality. And we're passing this hooks map so we can identify the correct effects that we wanna trigger or update. And we're also passing this register on updated callback, which comes from the subscription function. And the important thing to know here is how we handle this is put simply is, this is a callback. So it's a function that will trigger all effects, which are function. So we're essentially triggering one function that then triggers all our effects, which are functions also. And that's how we kind of implement all our effects when the app, after the app renders. So, again, remember we have this register hooks function, which is responsible for calling the hooks on each render, each component. So remember we had here, so that's how we pass in the specific read on pointer. So we can kind of instantiate the hook for each component. And we need to do the same for our user effect. And we need to ensure that the correct data is also being passed through from this make register hooks so that we can also now create our user effect logic. And then, one here. Yeah, so similar to our kind of use state logic, we've got another higher order function, so a function the returns a function. The first function is getting called on that initial render. Set everything up on the first render so we know what's happening, how to handle things. You can see it's taking that call back that we're gonna update. Remember this is the call back, the function that contains all the functions we wanna run. The function that contains all the effects that we wanna trigger. And again, it's taking hooks map so we know how to retrieve the specific hooks for each component, or like database of hooks. And we come into here. So this is where we're kind of defining that call back of effects, that function of all our functions. We just set it to a dummy to begin with. And then we're creating another function that'll be called, that essentially is responsible. Is that one function that triggers all the current effects? So this is the function that will call all the other effects that we've created. See how, so there's a lot of functions that are calling functions, but the key thing to remember is that we have this one, one function that is responsible for calling all the other functions and the effects need to be called after the render. So after the update. So we have this function register effects for after dom update, which is creating function that allows to be called. And we can see here that we are instantiating the massive kind of database of all our effects. And we're triggering the current one, and then we're also adding the callback that's passed through.

24. Handling UseEffect Functions and Dependencies

Short description:

This is the function that will be triggered when you reference the user effect in your component. It's specific for each hook within each component and gets the VDOM pointer. We instantiate the current hook and return a function that takes the effect callback and the dependencies array. We handle the use effect function by storing the index and using it to get the previous effect. We bump the current index for each subsequent effect. We check the dependencies and update the effects on the current hook value at the specific index. We set the dependencies value with the new dependencies for future comparisons.

So similar to I use state, this on line 93 is the function that is called when you reference the user effect in your component. So this is the function that will be getting triggered. And again, it's specific for each hook within each component. And again, it gets the VDOM pointer. So we know which one we're referring to. And again, it's first render. And again, we're instantiating the current hook by taking the VDOM pointer and applying it to the hooks map. So we've got a specific reference for that hook in relation to its component. And then we are returning a function. And this is what takes the effect callback. So our specific logic that we wanna happen. So in the example, it was around the local storage, adding to local storage and our dependencies array. So we've got the function that we wanna trigger and we've got the dependencies array, which is an array of those values that we're essentially listening to. We're listening to changes for... Yes. My advice here is yeah, read the hints. Also reference the, how we did use state because there are a lot of similarities there. We've also created this helper function, our dependencies equals, so you can easily compare the dependency and make changes based off that, the result of this function. So in this next step and our final steps, and this is the last bit of code you'll be writing, we want to be handling our, specifically our use effects function. So here, and also we need to remember to register our user effects and make sure that the data's being kind of passed through the steps correctly. But yeah, look how that's being done for use state. That's a kind of good tip. Now going to take you through the solution quickly and then wrap up. So the solution is available on chapter four, step two. So if we check out that branch and we head over to the code, so the first kind of starting point was in this MakeRegisterHooks function. And we needed to do a similar thing to a state. So we needed to call our MakeUseEffect and we needed to give that the Vedon pointer and the IsFirstRender, so we can create the use effects for each specific component. And then we also needed to replace the UseEffect on our global hooks replacer so that every time the UseEffect is called, we're using the one with the correct data and information. And another important thing was to, we now need this MakeUseEffect as well available. So we had to add it to where we're calling it and the createHooks function down here. So yeah, that's kind of the additions that are required from these two functions. So we're calling things in the correct order. And then when we get to kind of the more interesting point, which is our UseEffect functions, similar to State, we need to store the index because just like State, we can have multiple effects for each component. Again, we set it to zero. I think, yeah, this is probably redundant, which was a clear among the comments, but we probably, yeah, I think we were just trying to safeguard. You could just set it to zero. And then in our actual UseEffect function, so this, the function that's called when we call UseEffect in our component, we use, we instantiate the effect index by referencing the reference. We get the previous effect by using that index. If it doesn't exist to make the comparison function work, we set it to an empty object. We bump the current index because, again, this will be called once, for the first effect in our component, and then we bump it for the next one, which is one. So when the next TimeUseEffect is called in a component, the current value would be one, which we used here, and then we bump it again to two and so on. We do our dependencies check. If they're equal, we don't care. So we just return, we X out the component and don't do anything. If they're different, we need to update our effects on our kind of hook, on the current hook value. And we need to do that at a specific index. So that's how we do with state. And then yeah, we set on that the dependencies value with our new dependencies. So then this can be used for the next call of the user effect. So we can make a comparison again in the future.

25. Handling Effects and Cleaning Up Hooks

Short description:

We handle effects by calling them after the DOM is updated. The utility function allows us to add effects to a database of callbacks, which are triggered by a function called after the DOM update. This ensures that effects are properly handled. If you're interested in building your own React, there is an incomplete step in the branch you're currently on. It involves cleaning up hooks and resetting them when necessary. Once completed, the final branch contains the complete logic of your own React. Thank you for your time and patience. We plan to publish blog posts with detailed explanations of each chapter and a visual map to help understand the flow of function calls. Thank you for joining us and have a great evening!

And then again, remember we wanna actually call our effects so the effect call back the function that's passed in, we only actually wanna call that after our update. And we created that utility function that essentially allows us to add our effect to that kind of larger database of callbacks. So the one function that triggers all the effects, we then like add our effect call back to that list but then is called after the DOMs actually updated. So we do that by putting it in a function, giving it to this register effect after DOM update and that when that function is called, it actually calls the effect call back that we're passing through here. And that's how we handle a user effect.

You can see now, I go to the app and I add and now I'm doing a hard refresh. The data is still there because the saying to local storage is in the user effect and that's working correctly now. One minute, so I don't wanna keep you past, you've already stayed so long. But thank you so much, this was a crazy amount of information to doing such a short period of time. So I really appreciate you being patient. The plan is for us to publish a series of blog posts which will go through each chapter in detail and also include a more visual map because I think we said the word function about a million times. A visual map that allows you to follow the flow of each function call a bit better and clearer because that's a key point and also a key blocking point to maybe understanding the whole infrastructure of this of how React works. There's a lot of higher order functions.

And if you're really keen and wanna keep building your own React, the branch you're on currently is an incomplete step and it is the last step, it's around cleaning up hooks. So, with each hook, you wanna kind of reset that hook when a component unmounts or before the dependencies are checked, because, for example, if you have a set timeout in your use effects, you wanna clear that timeout before a new timeout is created. So that chapter four, step two kind of handles that, and once you're completely done, there's the final branch, and that contains the complete logic of your own React. Yeah, so I think the feedback phone will be sent out, which will also have links to the recording of this session and kind of more edited recording will be available I think in a week or so. Yeah, but thank you for your time. I hope that wasn't too crazy on a Monday. For real, thank you so much everyone for staying with us for so long and going through all of this together. I think it was fun, but it was definitely intense, I think for us and probably for YouTube, and have a nice evening. Try not to dream too much of React for now until at least tomorrow.