React Hooks Tips Only the Pros Know

Rate this content
Bookmark

The addition of the hooks API to React was quite a major change. Before hooks most components had to be class based. Now, with hooks, these are often much simpler functional components. Hooks can be really simple to use. Almost deceptively simple. Because there are still plenty of ways you can mess up with hooks. And it often turns out there are many ways where you can improve your components a better understanding of how each React hook can be used.

You will learn all about the pros and cons of the various hooks. You will learn when to use useState() versus useReducer(). We will look at using useContext() efficiently. You will see when to use useLayoutEffect() and when useEffect() is better.

177 min
09 Jun, 2021

Video Summary and Transcription

This workshop on React Hooks Tips explores hooks beyond the basics, covering common mistakes, advanced hooks, creating custom hooks, and the rules of hooks. It demonstrates the use of useState, useEffect, and useContext hooks, as well as creating custom hooks for managing state and performing side effects. The workshop also covers the use of useRef, useReducer, and useLayoutEffect hooks, and provides insights into how React hooks work under the hood. It concludes with the implementation of a provider component and the use of context and reducers to manage complex state in functional components.

1. React Hooks Tips: Beyond the Basics

Short description:

Welcome to the workshop on React Hooks Tips Only the Pros Know. This is an advanced workshop where we'll explore hooks beyond the basics. We'll cover common mistakes, advanced hooks, creating custom hooks, and the rules of hooks. In the second part, we'll create a simplified clone of a UI or forms library using hooks and context. Make sure to have Node, NPM, and the GitHub repository ready. Clone the repository, switch to the start branch, and run NPM install. We'll be using vita or feet to develop the application in a lightweight and efficient way.

So, welcome everyone to this workshop on React Hooks Tips Only the Pros Know. This is an interactive workshop, so you're expected to fire up Visual Studio Code or whatever editor you use and write code. I'll share the repository. In fact, I already shared the repository in the chat window over on Discord and in Zoom as well. But, Discord is the main chat window, so make sure to keep that open.

So, who am I? My name is Boris De Beier, also known as The Problem Solver. I'm a Microsoft MVP Developer, Instructor, Team Lead, do all sorts of things like that. A lot of front-end developments with React. That's kind of the main thing I do. My Twitter handle, at borisdb. If you want to follow me, send me messages later, or my email address, in case you want to do any of that. So, among the many things I do, I also publish a React newsletter that's sent out every Wednesday. So, yesterday, there was another one. Feel free to subscribe. Just click on the image, or the QR code, or scan the QR code with your phone. If you register, you'll get one email a week with six React articles and one video. Don't like it? You can just unsubscribe. I'm not gonna use your email address for anything else. I'm not gonna sell it to god-knows-who. There is no point in doing that with all the data leaks. I can't really compete with those any, if I would want to, and I don't.

So what are the goals of this workshop? Well, we're gonna take a look at hooks. We're gonna skip on the basics. This is an advanced workshop. So I'm expecting everyone to be familiar with React, familiar with the basics of hooks. And we're gonna take it beyond that. We're gonna take a look at how you can go wrong, because even with the simple hooks, they are pretty simple, but you can still use them in the wrong way and get a bug or some unintended result or maybe a performance degradation or something like that. And then we're going to go beyond the basics hooks. How can you use the more advanced hooks? When would you want to? And which are there, of course. We're not going to look at all of them, there are a couple of pretty edge case, ones like use debug value. We're not going to use that, but we'll take a look at most of them. We'll create some custom hooks. We'll take a look at the rules of hooks from why you need to follow them, what can happen if you don't, and how they're actually built up. Part of that is we'll take a look behind the scenes at how the ESLint rules and how React actually work under the hood with hooks. And then the second part of the workshop, we're going to do a more complex example. The first part is pretty simple examples where we're focused on one hook at a time. The second part, we're going to create a bit of a UI library or a forms library, maybe you're familiar with formic, which is a really nice library if you want to do forms over data in React. Well, we're going to create a simple, pretty, very much simplified clone of that. See how hooks and context and a couple of different React features will work together to create a very elegant solution to do something like that. I'm going to provide you with all of the code you need in the Git repo, so if you want, you can copy everything. But I would highly recommend to type as much of it as you can. Like there are some bigger snippets where I'm going to copy it as well, because there is no point in watching me type 25 lines of code with lots of typos. But for the most part, I would recommend you actually type it in because you'll make mistakes. I make mistakes. Everyone does. And you'll make those in real life as well when it's not some workshop. And if you've made them now, you've seen what goes wrong. You can get help. Then you'll recognize them when they happen in some other scenario. So it will help making errors actually is a great way to figure things out. Prerequisites for the code you'll need Node and NPM and the GitHub repository. With Node and NPM, I'm pretty much expecting everyone to be good there. Given that this is an advanced course, and I'm expecting everyone to be familiar with React. I've got Node 12.18, I've actually got a newer version of NPM. What is it? I've got NPM 7.11 here. I've actually got Node 14.15, unlike what it says in my slide. I guess that's a bit old, a bit dated, but a relatively new version of Node should work and a relatively new version of NPM, it doesn't need to be exactly this version. The other thing you'll need is the GitHub repository. Now I've pasted links to it, both in Discord and in the Zoom chat window. So you can get there. You should also have received an email with links to it. So plenty of ways to get at it. If you've got the copy of the slides, which I highly recommend you keep handy because there are lots of links in there. You can click on the QR code here or on the image and it will take you right to the repository, except it does so on my other screen. So here it is. Basically, all the codes we're going to write this in here, there are a bunch of branches in here. When you clone this, we just leave it on the main branch to do the npm install. And after that switch to the zero zero start branch. That's the one we're going to start with, which doesn't have all the code we're going to write. The main branch is complete. All the other branches here are kind of at the end of a step. So zero one custom hook is after we've done a custom hook, zero two layout effects is the same thing, et cetera. So you can see we've got 14 different steps to go through. Actually, is that all? I think so. All branches here. Yeah, 14 of them. So make sure to clone that and run NPM install on that. And that shouldn't take that long, but you'll get a bunch of packages. This is not done with create react app. So it's actually a bit smaller than that. A bit of an experiment, but I'm using vita too, or feet. I should actually say that's how it's pronounced to build and run the application in development mode. Maybe you've heard of it, maybe you haven't. It's a lightweight way of hosting applications or sorry, developing applications where the development server starts up really fast. It uses ECMAScript modules to serve everything up. So code will be compiled as needed, but it's not like everything has to be bundled with every change. So completion steps are really small and it's only compiled as needed, which actually works really well. I haven't used this on large projects yet where it actually shines even more because then the startup time would create React that becomes kind of slow. But the smaller projects I've used this on, I was really happy with it. So I used it for this workshop and it works very well.

2. Introduction to React Hooks

Short description:

Welcome to the workshop on React Hooks Tips Only the Pros Know. This is an advanced workshop where we'll explore hooks beyond the basics. We'll cover common mistakes, advanced hooks, creating custom hooks, and the rules of hooks. In the second part, we'll create a simplified clone of a UI or forms library using hooks and context. Make sure to have Node, NPM, and the GitHub repository ready. Clone the repository, switch to the start branch, and run NPM install. We'll be using vita or feet to develop the application in a lightweight and efficient way.

And I can recommend it for other stuff as well. Another thing you'll notice with the code, all the code is in TypeScript. Now, not everyone is a TypeScript fan, I am. I'm not going to apologize for that. I very much like TypeScript. I very much think it's well, a huge benefit when you're doing serious applications. With a small like demo application from proof of concept, I'll probably use JavaScript as well just to do things quickly. But any serious work I do in TypeScript. Now this workshop isn't a serious application, but it is meant to be realistic even though it's pretty tiny. So I decided to use TypeScript. Now, if you're not used to TypeScript, there is a file. Let me open up Visual Studio Code first, and show you where is it? TS config down here. This controls how TypeScript works. There is this setting here, strict is true. That's means that TypeScript will do lots of checks. If you don't really want or have the experience with TypeScript, you can set this to false and then it will start bothering you a lot less. There's also the any keyword. You can type things as any and get rid of a lot of compilation warnings that way as well. But I would highly recommend you to stick with TypeScript and use it even in serious projects because it works really well. Another couple of links to the repository and these slides. That's not the point of this slide though. The point is, you'll see lots of images like this where I have code snippets, use person, function in this case. And if you hover over it with the mouse, you'll notice that that's actually a link which opens up on my other monitor again. So the point of this link and the code that shows up, it's a diff of what we're actually doing in this step. So in this step, we can see that I added to use ref here and I changed something, et cetera, exactly what I removed, like remove some code here, stuff like that. Useful if you want to do something and you're not quite sure what the changes are. There are some subtle highlights here where you can see where code was changed, which will help as well, but you can open up the code. And of course, if it's a bigger change, you can always go in here and just copy stuff from here, which I'll occasionally do. Also, you'll see this gentleman come along. Jean-Luc Picard, the captain of the Enterprise. And basically any time you see him come up, it's a cue that it's your turn to do something. So basically, I'm gonna show you what we're gonna do. I'm gonna do it. And then after that, it's your turn to do the same. Of course, I'll explain how and everything, how everything's supposed to work, et cetera. But during those interactive parts where you're actually coding, I'm gonna open up to Zoom breakout rooms. I'm gonna split the, well, we're currently at 23 people. So I'm gonna split that up into something like seven, maybe eight rooms, depending on the actual number then. So there will be something like three to four people in a given room. Unfortunately, sometimes in Zoom, people are dropped from a room. In that case, I'll add you back. But I typically don't know exactly which room you were originally in because they're randomly created. So sorry if you hop from one room to another as a result of that. Nothing much I can do about that. So that's the intro part.

3. React Hooks Basics

Short description:

Let's start with the basics of React Hooks: ustate for state management, use effects for side effects, and use context for accessing provided values.

So let's get to actual React Hooks. And we'll start with the basics. Now, these are the three basic hooks that were added to React. There are a whole bunch more we'll take a look at, but these were the basic. And I'm actually not gonna explain a lot about them. I'm gonna use them. I'm gonna explain a bit about them, but it's kind of assumed everyone is familiar with that. And the three basic React Hooks where I'm assuming everyone's familiar with are ustate or how do I do state management, manage some state in the lifecycle of React Component. Use effects, how do I do side effects over the lifecycle of a React Component and use context or how do I get a provided value, context value, which is provided somewhere higher up in the component chain.

4. React Hooks: useState, useEffect, and useContext

Short description:

React hooks are only usable in functional components. They have been available for a couple of years now and have gained popularity over class-based components. useState is used for state management, while useEffect is used for side effects. useContext allows accessing context values. Custom hooks are also introduced.

And of course, I'm assuming you're all familiar that React has both functional components and class-based components, but hooks are only usable in functional components, not in class-based components. But we've had hooks for well couple of years now. And to be honest, since hooks were released, I haven't really written a lot of class-based components and the majority of people are these days writing only functional components. In almost every scenario, I find them easier to work with than class-based components. So little reason to write class-based components.

In fact, there are only two features I'm aware of that you can't do with functional components and hooks which you can do with class-based components. The first is create error boundaries, but there are some pretty good MPM packages around. So I typically don't write an error boundary anymore, I just add react error boundary, the MPM package. And the other is that I actually forgot which lifecycle it is. Components did receive props, I believe. Well, one of the life cycle functions will give you a rarely used feature where you get a reference to the actual DOM objects just before they are updated. So you can maintain something like a scroll position, et cetera. And you can get that value back just after it was rendered and do something with it. But again, that's something which is very rarely used only in very specific scenarios. And I don't think I've ever had to use that in the last two years. So I've never written a class-based component over the last two years to do that either, except maybe in some demo, but not for real.

So first, useState. Give us some state which is tied to the lifecycle of a React component and a function to update them. Basically, here on line four, you see we call the useState hook. Provide some initial value, this could be a function, or in this case, it's just string. It returns tuple, which basically means an array with a fixed number and type of parameters. So the first is the actual value. In this case, just a string. And the second is a function which we can use to update that value. So like here in the Unchanged, we actually update a value by calling that setName function with whatever is the new name. Really simple, used by a lot. I would be very surprised to hear anyone say, well, that's nice, useDate, never used that before. No, it's pretty much the first hook everyone starts using. Turns out it's a bit more advanced. Like I just said with the useDate, when you initialize it, you can pass in a function instead of just a fixed date. This can be useful because the initialization might be expensive. Might have to do some computational work or in this case actually iterate or change an object. In that case, this function will only be called once to determine the initial state, not afterwards. So it might be a little faster. Another nice feature is the update or function. You can just pass it in a new value as on the previous slide, but you can also pass it a called back function which gets the current state correct and returns the new state. So in this case, the state is a date and this is next month button. So we grab the current dates or the current month from the date add one to it, add that or set that on the date and create a new one and we've changed date. Not everyone knows about these but still they're pretty basic stuff and pretty useful to use.

Probably the second hook ever everyone starts using is useEffect. Also pretty basic and very few applications are not gonna use useEffect. So useEffect is basically meant for any side effect you do inside your component when it renders like when it mounts when it unmounts or maybe when values props are updated, et cetera. There are a couple of different ways you can do it. So useEffect works like this. You call useEffect hook, you pass in a callback function and that callback function will be executed after the component mounts and potentially many more times. How often depends on the second parameter which I've got as an empty array here. That's optional, but in practice I think you should always include that because if you don't, your side effect is gonna run with every single render. Now that might be valid. It might be what you want, but it hardly ever is. So if you put it, pass in an array as the second argument any dependency you put in there determines how often that useEffect is re-executed. So in this case, it's an empty array, that means there are no dependencies. So this useEffect starts or is initialized when this component mounts, then we're done with it. If there are any values in here, then the effect would be reinitialized if any of those values change. You can also have a clean-up function. Like in the useEffect callback function, you can return a function and not like I did here. Sorry about that, but I should not just write code and put it in the slide. I should actually test my code, oops. But this should be a function that function will be executed whenever this effect is reinitialized or when the component is unmounted. So if this demo component is unmounted, the clearer interval will make sure that this setInterval still stops running. Because in the case of a setInterval otherwise it would just keep on running and keep on changing states for something which doesn't actually provide any UI anymore, which would be wasteful. Here's an example with a dependency. So now the interval is actually part of the prop definition. So it's provided from some external source. So we provide it in the dependency. And as long as the interval is the same, say a thousand milliseconds, then we're gonna stick with the same setInterval for the life cycle of this component. But if it's re-rendered with a different interval then the first setInterval is cleared using clearInterval and a new one is started and the second one will be removed when this component unmounts. Again, still pretty basic stuff, but useful.

And of course this should have been a function as well. So the third hook which is probably not quite as commonly used but still pretty common and pretty basic is useContext. This means that somewhere in the component chain higher up in the component chain someone has used a context provider to provide some kind of context value. And then we use context. You can actually get a reference to that value. So you use it like this. There is an authContext for authorization somewhere higher up that was provided. And now I need to know the current values for the user and get a reference to the login logout functions, for instance. Well, I call useContext passing that context object and I get back whatever was provided or the defaults if none was provided. And then I can do something like in this case based on whether there is a current user I render a logout button or if there isn't I run the login button which will do whatever it's supposed to do. Probably not used quite as often as the first two useData, useEffect hooks but still because it's so easy this is considered one of the basic hooks. Most people will have used useContext and there isn't actually that much more to say about it. The reason most people will have at least used useContext is because there are so many different libraries that use it. If you say Redux, for instance, there is a context there and you can get your hands on it. And the same is true with many other libraries. Now, sometimes they use useContext and they actually wrap it inside of another hook, so custom React hook and that's what we'll actually be doing later on. But we're still using useContext then.

Well, the first thing we're actually gonna do is we're gonna start with custom hooks. And before I do that, let me actually show you the application we're gonna start with, NPM start. If I can type start. Oops, where did it go? There it is. So this is the application we're gonna use. There are a couple of menu items here.

5. Creating a Custom Hook for Person Editor

Short description:

There's a person editor where we can edit a person, add email, custom link, and new topic. The person object can be seen here. The Formic clone is mentioned, along with other things. The code uses state and local forge library for IndexedDB. The next step is to create a custom hook to extract the code from the component. The hook will save and load a person. The usage is simple, passing the initial person. Cleanup is done by removing unnecessary code. The usePerson hook is created. Breakout rooms are opened for further discussion.

There's a person editor, which is basically a simple input form where we can edit a person. And then after we've made some changes, we can submit that and we can see the results. We can add an email. We can add a custom link. We can add a new topic. We can actually see the person object in here. Can remove once again. Kim Rolf is that Formic clone, which I mentioned. And there are a couple of other things, which we'll use and a link to the Formic documentation. But for now, the person editor is the one we're interested in.

If we look at the codes, there's this person editor form, for folders are here and person editor components. There's an index.ts, but that doesn't do anything other than just exports that person editor just to make life a little easier. The whole form is pretty much in here, except there's this labeled input component, which is used, which actually gets the label in an input. So that's basically responsible for these bits. But other than that, it's completely, that's not the one I wanted to close, this one. I want to keep open. This is the complete component. You might note as well, okay, it uses state, the user state and the state is either a person or a NULL, kind of what you typically get when you fetch some data from the server or so. Not actually fetching data from the server, I've actually got some data in the IndexedDB or potentially it's in IndexedDB. So I'm using this library, local forge, which is a really nice and easy to use wrapper around IndexedDB, makes life bit simpler. It's asynchronous because the whole IndexedDB API is very much asynchronous. So it uses the weight and once it's found a person there, it loads that or if it hasn't, it loads this initial person, which is actually what we got to see the first time. But if I make some changes here, add some values and I hit F5, I just refresh the screen or do it again, you see, it actually saves those values and retrieves them because down here in my Application tab, if I go to Index DB, local forge and then the key value pairs right here, you can see that that value is actually there, and there's also a bit of code that every time I make a change, you can see that it actually saved that object into Index DB. Now, that's what we're gonna start with, and the first thing we're gonna do is we're gonna create a custom hook because everything is inside of that component, and well, right now, that's not that bad actually because it's pretty much UI, the actual non UI bit is just this, load some code, but we're gonna add quite a bit to it, and it's going to get more complex, and it really doesn't belong inside the UI component. And in the old days, you would have probably created some kind of wrapper component, have a presentational components, and another components to contain this, but nowadays, we can do that with hooks much better. So we're gonna extract this code, put it in the custom hook and just use it. Now, quite often, when I create custom hooks, I do so because that makes all the code reusable. In this case, it's not going to be all that reusable, I could make this more reusable, but I'm not going to. It's going to be very much still hard coded about getting a person from IndexedDB or the initial one. In the second part, we're gonna do stuff like this with custom hooks where it is gonna be much more reusable. But we're gonna start off, stuff simple. So basically what I'm gonna do is I'm gonna say, well, this hook is about people or a person. So I'm going to create a usePerson hook, which will save and load a person and return that to the component. And in the component itself, I'm basically gonna just say, okay, give me a person, give me a function to change that person so we can still change it from all the unchanged functions. And get that from the hook and we'll pass in that initial person. So doing so is pretty simple. All we do is create a new function, usePerson.ts. So with TypeScripts, and React component needs to be a tsx file, but this is just a normal typeScript function. It's kind of special for React, but for TypeScript, it's not. So we can kind of just keep using normal TypeScript file. Although, if you use a tsx file, that's pretty not bad as well. We'll grab all of this code. I just copied all the imports in the save function. We'll do an exports function, usePerson. I'll paste all the code we wanted in there. And this is basically what we're going to return. Isn't quite going to work yet. We'll need to make a few more changes, but first let's do the usage. We'll get a tuple with those same things. And we'll say, we'll get that as well. And we'll say, we'll get that from usePerson. Now we're getting some compile errors. We need to fix that. Basically I'm returning just an array with a person and a function and TypeScript isn't really aware that that's a tuple with exactly the same types over time. So what I'm going to do is I'm going to help TypeScript a bit, a couple of different ways you can do it. But I'm going to say, well this is going to be a person or no, the first arguments. And the second is going to be a function which returns nothing so void. And that takes person object which is of type person or no again. And that should take care of my compile errors. Now I've still got that initialPerson imported. I wanted that as a parameter. So let's grab that and just define this as parameter which is again of type person. So now if I go back to my component I've got the compile error here saying, well it expected one argument but got zero. So we want to pass in initialPerson here. We can do some cleanup here. There's a bunch of stuff we don't need. That save function is in the hook. Let's see what else. Actually let's Visual Studio figure that for me. Clean up those imports. The same here, there are a bunch of imports we don't need. Remove that. Remove those. So now I've got a use person hook. Remember all hooks need to start with the use key name. So it's always use this, use that, not just the standard hooks. It always needs to start with use. I'll explain more why that is in a bit. Let's first make sure that's everything's still works. Can I still save? And after refresh, is it still there? Yeah. And if I submit, we still see the values appear. So it's still completely functional. So that's your first step you're gonna do. So I'm gonna open up the breakout rooms. If you think, hang on, this doesn't seem all that advanced yet. Don't worry, we're starting off easy. We're gonna make it more complex the further we get along and the more challenging things are gonna become. So there are 23 participants now. So I'll turn that into seven breakout rooms. So most of you should be in a breakout room with three people.

6. Breakout Room Instructions

Short description:

When you get to the breakout room, introduce yourself to the others. I'll open up the breakout room for five minutes, followed by a grace period. If you get stuck, you can invite me to your breakout room or ask questions on discord. We're flexible about how you can ask questions.

So when you get to the breakout room, introduce yourself to the others. Hopefully you should be in the breakout room with the same people all the time. I'm gonna open up this breakout room for five minutes. After five minutes, you'll get a message, a notification that the breakout room is about to close down. You'll get another minute of grace period where you get a countdown timer saying it will be shut down in 50 seconds, 49, et cetera. So you can wrap things up. And after that, it will automatically shut down and everyone will be back in the main room here. If you get stuck for any reason, you can invite me into your breakout room so I can help you out there, or you can ask questions on discord, or you can come back into the main area and just ask me a question either voice or, well, with chat or something like that. Whatever works best for you. We're flexible about those kinds of things.

7. Extracting Code into Custom Hooks

Short description:

Before opening the breakout rooms, the speaker addresses a question about switching to the branch. They instruct the participants to start with the 00start branch and explain the purpose of the other branches. The speaker then demonstrates how to check if the code is valid by showcasing the expected behavior in the browser. They explain the importance of naming custom hooks with the 'use' prefix and provide insights into the React source code and ESLint rules. The speaker also highlights the potential issues with default exports and emphasizes the role of tooling in enforcing naming conventions. The segment concludes with a mention of other useful hooks.

Before I open it, I see there was a question about switching to the branch. So the branch you're going to start with is that 00start branch. So just go to 00start origin and you get to the branch which kind of had everything in the person editor. Remember the other branches here are if you get stuck. So suppose you get stuck here, then the 0 1 custom hook is the end of this step, which will also be the beginning of the next step. So that's why all those intermediate branches are here. So with that, let me open up the breakout rooms and I'll see you all back here in the main room in five minutes.

Question. How do we check if our code is valid? Could you just demo on the, in your browser, how the behavior should actually work if the code is correct? If the code is correct, if you start the application here and you go to Person Editor like this, you can type something and you'll see the changes reflect here. Suppose, let me break this for a moment. Suppose I don't do this, but I pass in some dummy function like that, doesn't like that actually. Now it does, so I'm back here. And now for instance, I can't type anymore. The same if you submit, you should see the changes you made in the pop-up window, in the alert window. The other thing you can do is check the console window here, if you see any errors. Anytime you make a change, you should see a saving message like this. So right now I can't, but if I do my changes, if I undo my changes, I should be able to save again. So now if I make changes, you see saving messages. Yes? Excellent, thank you. Okay, other questions? No, okay, see you in five minutes.

Okay, that's everyone back. So pretty simple, easy step, just extract the code into a separate function. Now, I mentioned that Custom Hooks must be used or must be named use something. In this case, use person. And just like the standard hooks, useState, useEffect, useContext, useReducer, useCallback, we'll see a whole bunch of them. They're all called use. But why is that? Why do we have to name them use? And would we actually break React if we didn't? Well, let's take a look. First of all, you might notice there are some squigglies here. I've got some React hook exhaustive depth warning from a missing dependency. It's missing that initial person, which really should be there. But suppose I didn't call this usePerson but I just called it a person hook or something like that. And I go back into this editor and say, right now here at the top, I'm calling person hook. There are a couple of, linting warning here saying, new state called in the function person hook that's neither a React function component nor a custom React hook function. So there are some complaints but is my application actually broken? If I look at the console window where the server is running no compile error messages whatsoever. If I go back to the application, I can still refresh it just fine. I can still make changes to it just fine. I can submit. It was actually saving those changes. So it actually kind of looks like everything is fine. So why do we have to do that? Well, let's take a look at some of the actual React source codes. To be exact, I'm inside the React source codes in the ESLint plugin React Hooks. So this is part of the React source code but it's a plugin for ESLint. And to be more specific, I'm in the rules of hooks hook or rule I should say. And first this little function here which checks on the ASD. So the abstract syntax tree which is created when your source code is parsed. And then the ESLint rule works on that. And it basically checks, well is this node, is that a hook or not? So it looks if the type is an identifier and the name is a hook name. And that hook name function is defined up here. And that basically says, okay well something is considered a hook. So if it's an identifier and it's named, use something with a capital letter A to Z or a number, and then some zero or more other letters. Then it's a hook. Otherwise it's not a hook. So the result of that is that we get errors here saying, well you state that's a hook that can only be used inside of a hook because this is not use something. As soon as I changed this back to use something and then it has to be used say like this, use and then a capital letter. Now those checks are perfectly fine again. I still get that warning here because that's actually something I need to fix. But that's basically all there is to it. It's just a function and the naming conventions make it a hook for the ESLint rules but technically speaking, it doesn't need to be any function will work. If it's not called use, it will still work. Now, what's another way you can know that for sure? What happens to this file if I create a production build? Well, this code is going to get minified. And I'm exporting this function but it's just a local name inside of this module. So, if I make a production build, this use hook is going to be something like X or something. It's not going to be called use anything anymore. It's going to be called whatever is a short name. So even then it still works. Now, where can you go wrong with this? Let me actually undo these changes. What was it undone here? Yeah, inspect to use person. So we're good. You might've noticed that I do export functions, so I use named exports. But what happens quite a lot is that people do this, export default. And that's not bad per se. I do have to change my import a bit though, because it's a default import now. Everything still works perfectly fine. Like all of this still works, no problem. But what do I sometimes see people do? Well, they're lazy. They don't, they think, well, it's gonna be a default export. I really don't care about this name. We'll make it an anonymous function and we'll do this. Export default like this without the quote. An anonymous function. Is this a hook? Well, according to the rules of React, it really isn't. It's not a hook. It still thinks it's okay. Maybe that's based on the file name. I'm not quite sure why it doesn't complain about used state. But this is just any random unnamed function. So one reason not to use default exports because you can start doing things and you can well limit the number of ESLint rules actually executed and miss potential big issues there. So I'm gonna undo these changes again. So that's really why we have to name them not so much because of the runtime but because of the tooling. Now let's look at some other hooks which can be useful.

8. Using Layout Effect to Track State

Short description:

Use layout effect to track the state and ensure the component is mounted before performing updates. This prevents potential memory leaks and warnings about unmounted components. The layout effect fires sooner than the use effect, making it useful in scenarios where components need to display after an asynchronous call. By using the layout effect, we can set a mounted flag to true and false and use it in other side effects. In the next change set, we'll fix the issue of using a module-level variable to track the loaded state of a hook.

Use layout effect. And if you look at the documentation it says, well, it's like use effect but it fires synchronously after all DOM mutations. Which kind of implies that use effect fires asynchronously which is true. Use effect fires asynchronously a bit later and use layout effect fires a bit sooner but still after the component has rendered.

Now, basically whenever you're wondering should I use layout effect or use effect, if you've got no specific reason to use layout effect then you should always use, use effect. That should be your default hook for side effects. But the fact that you lay out effect fire sooner can be useful in some scenarios. And let me show you one case. I'm right now, where's my code here? Right now, we're loading the person here. And that can be an asynchronous call, so that could potentially take some time when the component first loads and then we kind of want it to display. Which sort of works. But what happens if this is slow? Let's say we have a sleep function and I'm gonna sleep for two and a half seconds. And we'll await this because we're in an async function. So now, if this component first load, we're gonna take a look at the spinner for two and a half seconds and then the form loads. Kind of like what happens with an AJAX request over a slow network. But let's actually clear this and see what can happen. So I'm gonna click on Person Editor to load the component, but before the two and a half seconds are over, I'm gonna hit Alt-Left Arrow, which basically is the same as hitting this button. And I'm gonna navigate back to this list. And in that case, we'll see an error appear. So the sleep is still running. And actually, I was expecting to see an error. Why am I not seeing an error? That's interesting. Let's see. No, I am not seeing an error here, but something seems to be happening. That's right, I think it is stuck. No, I am not seeing an error. Let's continue. And then I notice something about this as well as this. I can't set this person... I can't call the setter function on new state after a component has unmounted, but for some reason that's not happening. Let me start again to make sure that I've got nothing weird. That's the error I was expecting. Not sure why that didn't happen before. You might've seen this when you were developing React application. I can't perform a React state update on unmounted components. So why does that happen? Well, if we go back to the code, the component mounts, this use Effect Fires, we call getPerson. So getPerson is an async function here. Let me zoom in a bit. And then it starts doing this. So it first waits for two and a half seconds, then it gets the person from index DB, and then it modifies component state by calling that setPerson, which comes here from the use state, which is fine in the normal flow of things. But if this component is unmounted before this setPerson actually happens, then we're calling this on an unmounted component and that could potentially be a memory leak. Now, in this case, it wouldn't be because we're not keeping a reference around, it's not some kind of setInterval. So it's not gonna be a problem, but it could very well be if this was in a setInterval which keeps on firing, it will keep a reference to that setPerson, which keeps reference to the underlying state in the react component, etc., and we've got a memory leak. Because react doesn't know whether we've got a memory leak or not, but it's potentially true, it warns us about it. Now, that's fine. Nothing wrong. This warning is justified. And in this case, we could actually go and ignore it, but if we do start doing that, it's kind of like, okay, we start ignoring warnings, which could potentially be a real memory leak in a problem. So it's much better to prevent this and not ignore it altogether. So we kind of need to know in here whether our components was already unmounted. Well, if I do another useEffects, then it's kind of, well, they all fire at the same time and it's kind of hard to know whether it's unmounted, but with useLayoutEffect, I know that both mounting and unmounting fires first. So down here, I could say useLayoutEffect, and the usage is exactly the same as useEffect. So over here, I can do something which says it's mounted. So I'm going to do something bad first. We'll fix that later. Mounted is false. In here, I'm going to say mounted is true, and then I'm going to return a cleanup function. Set mounted false again. And now I'm only going to do this update if we're actually mounted. Now this bit is fine. The bad thing is in this, but we'll fix that later. But just to explain why it's bad, this is on the module level. So this mounted flag will be shared between all instances of this used person hook. So if it's used 10 times, then the first time mounted will go from false to true. When that's unmounted, it will go back to false. But if it was mounted a second time, well, it would start at true. So it's kind of not the right way. But like I said, we'll fix that in a moment. So if I do the same thing and make sure that everything is in a good state, now, if I go to Person Editor and I navigate back, no errors, nothing. And just to make sure it's not for the same reason as before, let's come on these out. Refresh again. And now we should see that same error appear again. And there it is again. So we kind of used the fact for that layout effect fires sooner. So we can set the mounted flag to true and false and use that in other side effects. Potentially, we would want to wrap this in here as well. So we don't save when we're unmounted, etc. But I'm not going to do that right now. So using layout effect to track the state. So that's another pretty small change. So I'm going to open up the breakout rooms for five minutes again, just like before. And then in the next change set, we're actually going to fix the problem where we use this module scopes variable to track whether a hook was loaded or not. But we'll do that after this. So let me find the breakout rooms because for some reason my breakout room window is gone. There, it's back. So I'll see you all in five minutes.

9. Using useRef to Track State

Short description:

The useRef hook allows us to create a mutable ref object that can be used to get references to DOM elements or other components. We can pass the ref object to JSX and access the current property to interact with the referenced element. useRef is considered stable by React and doesn't need to be included in the dependencies of effects. We can use useRef to fix the issue with the loaded variable by initializing it to false and updating its value in the useLayoutEffect and cleanup. We can also use useRef to track the mounted state without triggering a rerender. The next step is to modify the usedPerson hook to incorporate useRef and track the loaded state.

Okay, that's everyone back. So let's take a look at how we can fix that loaded variable which was in the wrong scope, module scope, instead of the scope of the hook and actual life cycle of the associated function. And we're going to use another hook for that, the UseRef hook. If you look at the documentation, it says UseRef returns a mutable ref object whose current property is initialized to the past argument, which is kind of, okay, well, that's nice. Well, what is it? Where does it come from? Well, useRef, its main purpose is getting to DOM elements or other components. The ref part stands for references. And it is a way where you can easily get a reference to something else. You create an object using UseRef and then you pass that into your JSX, into either a DOM element or another component, and you will get a reference to that. So one example might be something like this. So in line four, we say, okay, useRef of some HTML input element, and we'll initialize it to null, and then on line 14, usingRef equals the reference to thatref object we created on line four. We pass it in, and then afterwards there is this ref.current property in useEffect that's available because that happens after the component is mounted, after this JSX is actually being added to the DOM. So ref.current points to the actual DOM object for this input, so you can get that value or any of the other properties on there. So in this case, we're just setting the value. You could retrieve it or maybe add an event handler or any of those things, whatever you want to do. So pretty simple, useful. To be honest, I use useRef quite a lot, but I hardly ever do this. It does happen occasionally, but not very often. You can also pass it onto a child component like this. I've got a component demo which renders an input field component, on line 5, I created ref in exactly the same way, and on line 20, I'm passing it in exactly the same way. Now, what I get back really depends on how input field is being set up. If it was just a regular component, I would get a reference to that component. But what you can do is here with React forward ref, you can actually say, okay, I'm going to wrap component with a forward ref, I'll get the ref out of that and be able to use it, and I'll get all the other properties as well. So the first arguments here are all the properties, and the second is the actual ref being passed in. Now over here on the input, I'm passing that back in. So the original ref actually is forwarded to this input element, and that's what I get back, which is why I could just say ref.currents.value again and do something with it. Now these are useful, and they're certainly things you occasionally need to do, but it turns out that using refs is much more powerful in different ways, because basically whenever you call the useRef hook in a component or in another hook, you get this object with the current property back, and you can start using that current property for whatever you want. You can store an object in there, a function, a simple value like a string or a boolean or a number, whatever you want. And one really nice thing is that useRef is considered stable by React, so you don't have to include it in the dependencies of effects. So if you use effect, the second argument, the array of dependencies, well, everything which is not referenced should go in there or almost everything. A reference from useRef doesn't need to go in there, so you can just manipulate that current and do whatever you want and don't need to worry about side effects being reinitialized or like you would have with useState. If you call the setter function, then you trigger a rerender on that component because it's status changed. Again, with the ref that doesn't happen. So useful for all sorts of things. And we can use that right here for that loaded state. So on line 16, you can say, well, instead of having some kind of variable outside of the scope of this hook, we're just going to use ref initialize it to false. So loaded.current is gonna equal false. In the useLayout effect, we set it to true. In the cleanup of the layout effect, we set it to false. And now we can use that wherever we want. Really nice. So let's get rid of this mounted because that's wrong. And we'll do const. Mounted is useRef. It wants some initial value. We'll call it, we'll give it false because it's not mounted yet. Mounted now is a mutable ref object of type Boolean. I don't have to specify the type like I did with you state because all these hooks kind of try to determine the type of their states. In this case, if I left the actual type out, it would say, well, it's a state of null. Here, false is fine. So, don't need to do that. So, mounted isn't going to be true but mounted.current is going to be true, the same with mounted.current turning false. And mounted is always an object. So we need to check now, mounted.current. And that should give us exactly the same behavior as before. So, I can go to the person editor, navigate back and we don't get any errors about changing state when the component had already unloaded. And no errors or warnings from ESLint about missing dependencies, even though I'm using that mounted. So, I'm using that mounted reference in here now. Why that is we'll leave until the next part. So, the first thing we're going to do now is actually change our used person and add that use ref to it and track the loaded state using that. So, that's.

10. Understanding useRef and Dependency Tracking

Short description:

Used ref is actually setting a variable within the scope of this hook. There can be use cases to relay that ref to other parts of the code. However, mutating the exported value will not reference the original value within the scope of the function. To trigger a rerender, something like ustate can be used. The ref object doesn't require being added to a dependency array. The same is true for the setPerson function returned from usedStates. The exhaustive dependencies rule in the React source code determines whether a value is stable or not. The setPerson function can be used without adding it as a dependency. However, if the setPerson function is used in a different useEffect hook, it needs to be added as a dependency. This does not have any negative effects at runtime. It is not possible to create a custom hook and specify that a function is stable without using it in a hook.

That's a question. Yes, go ahead. I'm not new to use ref, but I have a question just how do I understand this more better. So, used ref is actually setting a variable within the scope of this hook. Yeah. And, it's not, but are there any use cases for us to relay that ref to anywhere else? Can we export it from the hook and we use it outside or there are no use cases for that? Oh no, there certainly can be and it's just another variable. So, suppose I wanted to export it, I could add it to here. Okay, but it's. No, I'm sorry, mounted, it was called. So, but it will remain, if we mutate the exported value, it will not reference this reference within the scope of this function. If somewhere else you do mount dot current is some new value, you still mutate that same reference, which is tied to this. And it doesn't trigger a rerender, et cetera. So, you're not sure if that would be this wise idea, because there is no dependency tracking there. It's not like the code here is aware of it, so it would start executing. Understood, thank you. In that case you would need to use something like ustate, so you get the component to rerender. The whole point here is not to get the component to render. I'm maintaining a project that is not our own in the company, so we have a lot of legacy bugs. And we are trying to wrap our heads around various stuff, so that's my question. I know it's an edge case, but I needed to cover that in my mind. Yeah, fine. Thank you.

Okay, let me open up to break out rooms. I'm gonna open them up for three minutes because this is real trivial change. And then in three minutes, we'll take a look at why we didn't actually have to change those dependencies for that use effect. So see you all in three minutes. I have another question regarding the REVs. Yes, go ahead. So is my understanding right that whenever I call this hook, a different reference will be created for that, let's say, component, which calls the hook? Yes, in the end, the reference is tied to the components, which initializes this, so this useRef is used inside of another hook, but the actual state in the end is tied to the component, which starts using the first hook. Okay, thank you. You're welcome.

So everyone's back, okay. So I mentioned that that ref object doesn't require to be added to a dependency array, and in fact, the same is true for another thing. You can see here I'm using usedStates, and I've got this dependency or this setPerson function being returned from that, and I'm using that inside of this useEffect hook, but that doesn't need to be added here. Now, it's fine if I want to. If I add it, there are no problem, no errors. But if I don't, I don't get any warnings either. But for that initialPerson, I do. If I remove that, it comes up saying, well, it's got a missing dependency initialPerson. And if I say, update the dependencies to what it should be, it only comes up with initialPerson. So where does that come from? What's causing that to behave like that? So we're gonna go back into the React source code. And, you can see here, I'm actually in the GitHub repository of React at Facebook. Again, in the ESLint plugin, react-hooks. But now I'm in a different rule. The exhaustive dependencies rule. And there is this function, let me make this a bit bigger so I can, so it's readable and I can still scroll. There's this function, isStableKnownHookValue. A note is passed in from the abstract syntax tree, which is parsed. So basically this function and we're inside of useEffect. So basically it sees all these things here like LocalForge, mountedPerson, initialPerson, et cetera. So it's gonna take a look at all of those and it's basically gonna decide whether that's stable or not. And you can see things like if it's an array, then it's false. If it's no, sorry, I'm not even sure what these are, but if it's some kind of variable, it returns false. So they're basically all not stable hook values until I scroll down a bit. There is here one about literals, which can be true, but the one I was looking for is right here. So if this was created by something named useref and it's an identifier, then it's, as the comment says to useref return value and that's considered stable. So it doesn't actually look at what it really is, it just looks at how it was created. And if it was created by something which looks like to useref hook, then it's considered stable. The same is true with, I'm not going to make this completely visible, but if the name is useState or useReducer, it's going to look at the arguments and somewhere in here, right here. It determines that if this is the second argument of the tuple being returned, so the setter function, the setState or the reducer function in the case of useReducer, then it's considered stable. Which again means that this person, setPerson function in here can be used without having to add it here. Now, note I'm also exposing that. The interesting thing is if I go back in here, I've got a reference to exactly that same setPerson. Suppose I create another useEffectHook here, pass in the callback function, and I call setPerson in here with something, I'll just call it with no, and I put an empty dependency array. Now all of a sudden, it starts complaining, setPerson needs to be included. But if I copy this exact code in here, no complaints here, it's fine. It's the same hook or a same function reference. But as you can see from the rule, it doesn't actually look at what the function is, how it was created. It just looks at how we got to it. And in here, it looks at, okay, setPerson, we go from setPerson, which is the second argument from the tuples. Tuple returned by useState. And it knows, okay, well the second argument is stable. Here it says, okay, well we've got this setPerson and let me actually resolve this so the error goes away. We've got this setPerson. It's the second argument from some hook called usePerson. The Linting rules have no clue what usePerson is so it does no reference to what setPerson actually is. So it says, okay, well in that case you need to add it as a dependency. That's why I do need to add it here. Now, does that have any negative effects at runtime? No it doesn't. setPerson is still that same setPerson function or setPerson function from the useState. It is stable so even though I have to specify this dependency here, it will never change. So in the end, it doesn't mean that this hook fires more often or behaves any differently. It's just a little annoying that I have to specify there. And it also explains something which I'm occasionally asked Can I create my own hook and can I specify that this setPerson function is stable that I don't need to use that in some kind of hook? Well, no, I can't because that's really tight into the React hook. It's really tight to a function called useState. So I guess if I created my own hook called useState and not import useState from React but use that, then the limiting rules would actually start thinking that the useState I'm using is the one from React. It doesn't actually check up on that. And you could get some interesting behavior that way. Certainly not recommended to do that. Let me actually get rid of that import again because we don't need it.

11. Using the Debounce Effect for Batch Saving

Short description:

We can use the debounce effect to batch save actions and prevent unnecessary HTTP requests and database updates on every keystroke. The debounce effect waits for an idle time before executing the save action, making it more efficient for realistic scenarios. Creating a reusable debounce hook is simple and requires minimal code using the useEffect and setTimeout functions. We can use the useDebounce hook to optimize the saving process in the Person Editor component. However, there is a potential issue with the inline defined function, which can lead to the cancellation of the debounce cycle. We will address this issue shortly.

But that's why some values are stable. They're basically hard-coded into these rules again. Just like the original use part of the name was hard-coded into the ESLint rules. Not a problem, but just something to be aware of.

Now there's something else I might wanna change. Let me close these. Don't need them. If I go back to my Person Editor, and by the way, I remove that sleep because it just makes things slower and we don't need that. The thing I would want to change in here for a more realistic scenario is saving. Right now, if I clear this and I start typing in the first night, basically every keystroke triggers a save action. Now currently, the save action is local. It goes to index db and that's plenty fast so I'm not too worried about that, but it is a bit wasteful. In a realistic scenario, this would probably mean that an HTTP request was done to a server and a database update was done. Well, do we really want to do that on every keystroke? No, we want to kind of batch those up and not do that quite as often. That's where the debounce effect comes in. So you often hear about debounce and throttle. So, they're pretty similar. The difference between debounce and throttle is that if you've got a function and you throttle it to once every second, they can call it as often as you want. But it's in reality, the actual function will be executed once a second at most. So if you keep on typing, you will see here and it's throttled to one second, you'll see a safe every second. A debounce on the other hand, waits for a certain idle time. So if I debounce saving to one second, then the code will actually wait until I stop typing in the half stop typing for a full second and will then execute safe action. Either it's perfectly valid, different use or different scenarios of different use cases. In this case, saving when I've actually stopped typing is perfectly reasonable. So I'll use a debounce effect. And the terms are creating a reusable debounce hook is really, really simple, takes almost no code at all and it's completely reusable, which is basically the result of a very simple custom hook. Using use effect and using set timeout with clear timeouts. If the effect is reinitialized. So all we need to do looks something like this. We have used debounce hook. We pass in some callback function, which is the actual code we want to execute. We pass in the timeout. We use use effect to trigger that side effect. We use set timeout to achieve to delay. So we say, okay, wait until a certain delay. But if we pass in new values for either function or a timeout, before that delay expires, this effect is canceled. Clear timeout is called. Stopping the original set timeout, and we initialize a new one. So let's take a look, see how that actually works. So I'll create a new hook. Use debounce.ts. Export function. Use debounce. Not with caps lock on. Remember, it's a hook, so it still needs to be called use something. So it takes two arguments. First, the callback function to execute. Which is just some function which doesn't need to return anything. And it takes a timeout. Which is a number. And this doesn't actually return anything, so we can define that as void. So in here we use use effect. And initialize the dependency array. Which we'll need to fill later. So, we'll grab the handle from, and setTimeout. So after some time has elapsed, we call that function which is passed on, and specify the timeout. So now, we can have ESLint figure out the dependencies we need, specify the cleanup function to cancel the setTimeout. Oops, that should be a fat error. Clear timeout with the handle, and we should have completely functional useDebounce hook. So, let's actually use that to save. Now, saving is currently done over here, inside the useEffect hook, which basically says, okay, every time that person object changes, we want to save it. Well, we don't want to do it on every change anymore. We want to use the useDebounce hook, basically. Oops, I'm doing the same thing, with my FedError() function. And let's make that one second, otherwise it still saves quite often, and I still need to resolve that. So pretty small hook to create and really simple to use. Now let's make sure it actually works. So now, if I go to the first name and I start typing, nothing actually happens until I stopped typing for a second and then we save. So looks perfectly good and perfectly fine to use. There is one slight problem though, right now we're calling in a function which was defined inline. and inside the usedebounce hook, we're basically saying well, we've got that function but every time that function changes and the function reference changes, that is, we initialize a new effect. In this case, it actually works as intended but suppose that component was re-rendered for some other reason, this hook would be called in this case, default, it would call usedebounce again, it would call into safePerson again or at least create this function again. It would be a new function. So we would get a new function. The previous debounce cycle would be canceled, a new one would be started. We would still save, but potentially a bit later. And if something else refreshed this component, we'll say every 500 milliseconds, we would actually never get to saving. So that's something we need to fix. We'll fix that in a moment. First step is we'll actually go and do this bit. So create the usedebounce hook and use it to actually save the person. So I'll open up the breakout rooms for five minutes again to do this. And after that, we'll take a look at how to improve this a bit. Although it's already quite usable. Excuse me.

12. Introducing useCallback and useWillUnmount

Short description:

In this part, we introduce the useCallback hook to fix a potential problem with changing or having a new function being passed in useDebounce. useCallback creates a memoized callback function that only changes when the specified dependencies change. We also discuss the useMemo hook, which is similar but used to create a memoized value. We make a small change in the code to use useCallback in useDebounce. We then address a potential issue with useThrottle and introduce the useWillUnmount hook. This hook detects when a component is about to unmount and allows for necessary cleanup.

Yes, go ahead. Are we going to have a break for coffee or bathroom at some point? Yes, we are. About halfway, we're going to add a break. I already added that to the chat window. Oh, thank you. Someone asked, maybe you actually did. I'm not sure. It was me. Okay, I did respond to that, but that was while you were doing the lab exercise. That's why you probably missed it. But that's when I typically respond to chat messages. Okay, thank you. Okay, see you in five minutes.

Okay, let's have everyone back. So I mentioned we had a slight problem or potential problem with the changing or having a new function being passed in used bounce. Well, turns out that's relatively easy to fix and that we've got another nice hook to do exactly that, which is used callback. So used callback is a special hook used to create callback function, which can take some dependencies, but again, create a stable function if those dependencies don't change. And currently in the code, if this person object is still the same, but the component re-renders from some reason, we still get a completely new function here. Well, used callback will take care of that. I'm not separately going to look into used memo, but use memo is pretty much the same thing. Use callback is to create a memoized version of callback function. Use memo is to create a memoized value of some object or value, simple value, something like that, but typically objects. But they work in the same way, they're just slightly different use cases. So how do we actually do that? Instead of just calling use or the use debounds with standard function, I'm gonna call call use callback. This takes the actual function I want to call. It also takes a dependency array, and for some reason it's not complaining about that yet, which is interesting. This returns a memoized callback function. So... And we pass that into here. And now it does start complaining. That's because of compile errors, I guess. So if I say update the dependency array, it says, well, this depends on that personal object. As long as the same personal object is being used, this function reference is gonna be the same. And we won't have any problems with debouncing over a different function with the same actual values. Now, it would be nice if you could do that inside of useDebounce, and of course, it's a hook, so there's no problem in calling useCallback in there. But if we want to do that, we would need to have to call this function and all its dependency, et cetera, in there. And that will kind of make this function call really complex or kind of defeat the purpose. So that doesn't really work, you kind of have to use it in this way. But that's not so bad. And in this case, I'm actually creating a different function because I wanna reuse it later somewhere else, if not, I would just inline this inside of the useDebounce. So let's go make that small change in here. You can actually see the inlined version. Use throttle, you can do that in pretty much the same way as useDebounce. I'm not actually gonna do so now, but the code is slightly more complex. In fact, this use throttle is slightly broken because it should clear the current interval. This will keep on firing even if it doesn't need to anymore. But other than that, it's really, really simple. I thought we're gonna do this first, but actually let's do this first. Let's make this small change and not start combining too many changes. So introduce useCallback to that useDebounce function. Pretty tiny change, so I'm gonna open up the breakout room for two minutes and then with two minutes, we'll actually go and fix another problem which we introduced now, which I'll show you in a bit. So see you in two minutes.

I'm sorry, can you repeat the reasoning for using the useCallback of just calling the function? Yeah, of course. If you look inside of the useDebounce function, you can see that this effect depends on the function being passed in. And every time this changes, any of these two value changes, this effect is torn down so clearTimeout is called and setTimeout is called again with the new function reference. Now, if we inline a function over here as we had before it means that every time this executes, a new function is created so the function reference in here is a new function reference every time. So this dependency changes every time. Even if the actual person object we are trying to save hasn't changed yet. It re-renders for some other reason. Maybe there is a clock ticking away there. So by using useCallback, we can say, well, now we get a stable callback function which only depends on the person object changing. If this component this is actually added to is re-rendered for some other reason then the person changing then Fn is still going to point to the same function and useDebounce is not going to notice the difference. But when the person does change, we get the new function. And useDebounce does notice.

Got it, thank you. Can you show us the code running? I like to see how it runs, the effect again. The useDebounce part? Yes, yes, I like to see. Yes, let me add a few comments, console.logs in here so we can see start useEffect. UseEffect like that. Let's turn this into, no. I want the console.log, calling FN and here we're doing clearTimeout. We'll use the save. So if I refresh that, let me clear everything. Now, if I start typing you see that every keystroke I do, you see two messages, clearTimeout, starting UseEffect. And if I pause for a bit, then you can actually see saving comp, it's gonna call the function and saves. Great, thank you. Yeah, that helps, okay. Oh, we can leave those in. Now, let me show you one thing which we've broken now. Because now if I'm changing, nothing is actually saved until I stop typing and I stop typing for a second. So if I'm deleting this and I'm actually navigating away before we get to the timeout, you see there is no saving message. So I go back into the person and we see Frieda with all those things off of it. So now it's saved in a clean way, so if I leave, go back, I've got Frieda, but if I start typing a whole bunch of ones, I leave the component before it was saved during that second grace period. I go back in, we've lost our changes. Well, that's not so nice, but that's kind of a result of saying, well, we want to debunk saving for a second. So how can we fix that? Well, pretty simple, we use another hook and we kind of create a simple hook to detect the fact that we're gonna unmount the component and doing that is really simple again using useEffect. So we're gonna, let's actually just write the code. We're gonna create another hook, useWillUnmount. So, export function useWillUnmount. It takes the same callback as before, which doesn't return anything.

13. Using useRef for Component Will Unmount

Short description:

We can use the useRef hook to save a reference to the function and only call it once the component is unmounted. This prevents executing the function on every keystroke. By using useRef, we can maintain state for the lifetime of the component without affecting other hooks. The useRef hook has multiple use cases, including getting a reference to a DOM object. This hook provides a simple and useful way to replicate the component will unmount lifecycle in functional components.

And in here we want to execute something. So we want to do so when we're done. So we wanna use useEffect when the component unmounts is when we want to do it. So we pass an empty dependency array, and then we don't want to do anything when the effect initialized, but when the cleanup occurs, we want to do something. So passing a cleanup function. And here we want to call that callback function. Go back in here and say, we also want to use the useWillUnmount hook, and pass in that same function there. Now, this should work, although it's still wrong. So if I start typing and I click on React Hook for Pros, we actually see that there is a saving message, but it says saving null, not the object I was editing. So I guess it sort of works, but not quite. So what's the problem with it not working? Well, the squigglies here from the eslint rule kind of show me to the problem. It wants something fixed and what does it want? Well, it actually wants that function passed in as a reference. But if I do this, it means that every time a new function is created, we start executing it, or executing the previous one as a result of the cleanup. So if I go back in here and I start typing, you see we're saving again on every key stroke, which we do not want. So we don't want to use this as a dependency. Well, we already used another hook. We can maintain state in for the lifetime of a component without having it affect or use affects etc. Which was useRef. So we can just say, well, we're gonna call useRef. We're gonna pass in that function and save it that way. functionRef is and in here instead of calling fn, we call fn.Current. Because now it actually still complains about something. Because just like you state this parameter. Is actually only used for the initial time. Not anywhere afterwards. So in order to capture the last. function being passed in. I kind of have to add this fnRef.Current is fn. So now. Every time this hook is called we save a reference to the function and only once it's unmounted. We actually call that function. So another use for useRef, which has nothing to do with its original intention. Get a reference to a DOM object, but where it's still really fine, really easy to do. So let's see if it works now. I start typing no save action, but I leave. And as soon as I leave the component, it's unmounted. It actually saves and we get back in there and all the values we have are persisted. So, pretty simple and still quite a useful hook to have around. Kind of gives us the component will unmount life cycle we have the class-based components, but now in a hook.

14. Understanding the Unmount Hook and Rules of Hooks

Short description:

The unmount hook can be used throughout the app and is not tied to specific input fields or the use debounce function. The useEffect hook, with an empty dependency array, initializes after the component mounts for the first time and is torn down when the component is unmounted. It is a standalone hook that can be used with any component or other hooks. The countdown timer is started for a 10-minute coffee break before discussing the rules of hooks and what happens when they are broken.

Okay. See, this is adding it to the components which I already did, so nothing to do there. So please add that's usual unmount hook. So a pretty small change but slightly bigger than the previous one. So I'm going to open up the breakout rooms for four minutes and in four minutes, I'll see you all back here.

Okay, that's everyone back, I think. Quick question, Maurice. Yes, go ahead. Can this implementation of will unmount be more generalized? I mean, to be able to use it throughout the app. So not just for this debounce function. So we have here the whole will unmount hook is receiving a function. Can we actually use this for anything or just for input fields? No, you can use this for anything. It's not tied to input fields. It's not tied to the use debounce for that matter. It's just a standalone hook. You pass it some callback function and it will execute the last callback function passed in when the components it's associated with unmount.

Okay, so I'm having trouble understanding how does it know that the component will unmount? Use effect tells it. So, use effect here takes a dependency array, which is empty. And if it's empty, then the use effects is initialized just after the components mounts for the first time. Because of the empty dependencies, it's never re-initialized, but it is torn down when the component is unmounted. The component belongs to. So this is a start a bit, which is nothing, do nothing. The return function is the cleanup bit, which says, okay, we'll just execute whatever function was passed in. And it will execute the previous function that you've received, so yeah, I get it now. So it's not tied to use debounce, it's completely standalone. You can use this anywhere you want with any component you want or in any other hook you want. The main question was, how does it know that it will unmount? Because I presumed it had something to do with debounce or whatever the previous implement, but I understand that user fact actually tells it. Yeah. User facts tells it that's already older to it, which makes these hooks so nice. They're so nice and small and self-contained and they just do things. So we're about halfway through the time. Next thing I wanna do is take a look at the rules of hooks and what actually happens if you start breaking them and why they are the way they are. But first let's have a small break. Let's do a 10 minute coffee break and let's all get back here in 10 minutes. I'm actually gonna start a little countdown timer so you can see that I'm counting down and I'll see you all back here in 10 minutes.

15. Understanding the Rules of Hooks

Short description:

The rules for hooks are simple: call a hook function inside another hook or from a React component. Avoid calling hooks from loops or conditionals. Breaking these rules can lead to bugs and errors. React tracks the order of hooks called by a component and detects changes. If hooks are called in a different order between renders, an error is thrown. The order of hooks is tied to the lifecycle of the owning component. Hooks are stateful and tied to the owning component, even if they don't appear to be. React tracks the calls to hooks and stores their states in a specific order. Breaking the rules can lead to issues and should be avoided.

Okay, everyone back from the break. So I wanted to take a look at the rules of hooks. Why do we have them? And what happens if you actually break them? Because that will tell us quite a bit about how hooks work under the hood and why they are the way they are, which can be quite useful to know. So the rules for hooks are actually quite simple. If you wanna call a hook function, it has to be inside of another hook or from a React component, so those two are the only places where you can call into a hook. So, our React components, the person editor, could call into a usePerson custom hook, which could call into useEffect and useDebounce, which calls into useEffect again. All of that's fine, but in some other function, you can't call into a hook. So that one's simple. The other rule is, you can't call into hooks from loops, or conditionals, or that sort of places. That's actually interesting. And let's see what happens if you don't stick to that. I've got another page here. And let's actually go and see what happens. Actually wanna keep this open. Let's see what happens if we don't stick to that. And there is a little increment button. And right now everything's fine. I'm not doing anything strange. So I click increments, the counter is incremented. No problem. Works exactly as you would expect. At least I hope. So what does the code actually look like? And let's find the components. There, counter. It just has a simple, you state, it starts with zero. And then every time we click, we call the setter function, set counter here increment the count by one. Simple. No conditionals, nothing. And that works perfectly fine. So let's change that. Let's introduce a loop. So basically you have a great little for loop where I'm looping up to the amount of counter and creating another use state. Now I'm not actually doing anything with the result. I'm just calling into use state with an initial value. The actual value doesn't matter either. Make sure this is refreshed and clean. And let's hit increment. Now, all of a sudden we're looking at the big white screen, which means that something went wrong. And if we look at console, we can actually see what went wrong. React has detected a change in the order of hooks called by the counter. And then it actually lists the hooks it detected. So the previous render, which was the first, it says, well, there was a hook being called use state and there was no second hook being called. And the next render, it says, well, there was a use state and then another use state. Well, the first is fine, use state, use state. But the second means that we went from having one hook to having two. That's not okay. So it says here this will lead to bugs and errors if not fixed. That will also happen if you start changing hooks. Let me uncomment the second bit. So again, I've got different hooks. We're starting with that initial use state hook which is fine, and then depending on whether counter is even or odd, we're adding second use ref or use state hook. So let's clear all of this, so we're in a clean situation. Let's hit increments, and we see exactly the same thing happen. Oop, I wanted to zoom in here. Same kind of error message, but now it says the first render was first a use state, then a use ref, and the next render was use state, then use state. You won't always get an error like this, but it's quite likely to happen if you do something wrong like this. But when will you not get it? Suppose I use two use refs here. I did want to make this into use ref. For instance, I change this into false. It is not going to complain quite as loudly now. I can hit increment, and it's actually not happy, but it's sort of happy. Why is it not complaining? Well, it doesn't detect that these are two different use refs, because they're the same type. If I did use effects, both, or use state in both, it would've not complained either. I've got two initial values here, true and false. Which one will be used? Well, that really depends on which is executed first. So in this case, we're starting with the value zero, so it's gonna start with use ref true, so true is gonna be the initial value, and this false is never gonna be used at all. It doesn't complain here, but this is still bad. It's still gonna lead to issues, so I'm gonna undo this, and keep it like that, and comment it out again. It's bad. Now, why is this so bad? Let's see the error one more time. It turns out these numbers have a lot to do with the way React is implemented internally. The way you should think of hooks is they're stateful things even if they sometimes don't appear to be stateful, who are tied to the lifecycle of the owning component. What's the owning component? Well, that's the component which first starts everything. So, in this case I've got a component here, Counter. It creates hooks useState, useRef, useState again, et cetera. It owns whatever these are. If these are custom hooks and they start doing useState or useRef, et cetera, it's still this Counter component which owns the state for all of those hooks. Now, how does that work under the hooks? It's actually surprisingly simple. What React does, it tracks the calls to hooks. So, basically it says, well, the first hook being called does something. It needs to maintain some state specifically here because it's the useState hook. It will return or gets a place to store that state as the first hook being called. Then we've got another hook being called. Let's do it in the way where it's actually correct. Like this and not broken. So, this is the second hook being called, it will get the second slot in the storage space to actually save that. Now, how can we see that in action? Well, we could start diving into the React source code. It's actually relatively easy to understand. The React source code is by no means easy to understand. It's quite complex, highly optimized.

16. Understanding React Hooks Under the Hood

Short description:

React hooks work under the hood by using a simplified version of React called fake React. The useState hook is used to manage state in functional components. The state can be modified using the memoized state object returned by the useState function. The render process in fake React involves setting a re-render flag and looping through the components to check for changes. The currently rendering fiber object is used to maintain the state of hooks in an array. The state is stored in a weak map tied to the component instance. The implementation of React hooks can be done in a few lines of code, providing a proof of concept for useState. Adding other hooks like useEffect follows a similar pattern. Understanding how React hooks work under the hood can be beneficial for developers.

Especially if you're new to it, it's really hard to understand. To be honest, I don't understand most of what's going on in there. But when I dove into hooks and how they work under the hood, it was actually surprisingly simple. And let me actually get rid of all of these for a moment. And let me show you how that sort of works.

Components, hooks explained here. In here, I've got kind of like this. It's called fake React because that's what it is. It's not React, it doesn't actually work the way React does, but it's simplification of what it does. Um, actually that's not the... Yeah, that's the one I wanted. So I kind of create components like I do in React, except I only care about hooks. So I left out all the JSX stuff. So I've got an app component here, but instead of returning JSX, it just returns an array of child components. So simplified in that way, but still the same logic structure there. What do these look like? Let's see. That doesn't work. Counter. Well, this kind of looks like a normal React component. Again, ignore the return bit, but we see that there is a functional component. It uses the UState hook, has an initial value, and changes things based on the value. So basically, the simple counter, the first state hook, keeps increasing by one until it reaches the value of 16, and that's one of my cats wanting to join in on the conversation, sorry about that. Then if its value is 12, it basically says, okay, I'll take the square counter, so the second UState will square it, and once it gets to 14, it will do the same again. And it actually prints out those values as well. And right at the bottom, you can see return empty ray, it doesn't have any child components. So the other thing in the app component was it renders Arthurdent and a ZaevoldBiebelbrocks component. Well, those are both instances of a greeter component using a different initial name. And again, they use setState. And in the case of ZaevoldBiebelbrocks, it actually says, well, if I am, I'm gonna change my name to forPrefect. And Arthurdent just says, well whatever, I'm gonna stay Arthurdent. And both of these print up their initial name and their current name. Now, how does this actually work when I run it? Did I add a script for this or not? Where are my scripts? No, I didn't. So let's just run them with Node. What was the folder? elsewhere.index.js So basically you can see the components render. So we have the first render cycle where Arthur Dent renders, Seyfold Biebelbrox renders, and the counter renders with both Simple and Square at 10. Then you see that Arthur Dent stays Arthur Dent, but Seyfold Biebelbrox it's actually renamed itself to four prefix and we see that that Simple counter starts increasing to 11, Square stays at 10, and Simple is 12, and Square will start updating. So if I Zoom out again, we'll get all the updated values. Here you can see that Simple counter has gone to 13 and the Square has gone to 100 and that Square has been squared again, right there. Now, what would that actually look like under the hood? So what do something like You state actually looks like? So that comes out of this fake React. And the You state function is actually pretty simple. It says, okay, mount some work in progress hook with the initial state being passed in and that gets a hook object back. And it turns out there is something called memoized state on that. And it creates a function where we can actually modify the memoized state. And if that's been called, it sets this little flag re-render, which means, okay, some state has been changed, we need to re-render. And then it just returns those. So pretty simple. The real You state is slightly more complex, but not much more so. So it starts out by rendering components. Well, how does that work? Well, first of all, it sets re-render flag to false because we're assuming that everything's being rendered after this render component's been done. But if there was some kind of set state call, then I'm just using a set timeout here to re-render everything again. So we're basically looping through the equivalent state so we're basically looping back into this function, setting render to false, rendering all the components and checking whether there was another change. So the public API, which in this case is one hook and one render function, is pretty simple.

Now, where does this used state actually get that mount work in progress hook? How does it actually get that? So I did not want to cover that code, like this. So here it starts looking into an array. So there is an object called currently rendering fiber which is somehow set up. I'll show how that was set up in a moment. And that contains an array of hook state and we've got this indexer where we index into that to get the hook for the current. Cool. Which basically means that because of this indexer the first u state gets the first slot zero the second one gets slot one, et cetera. And I've only implemented u state here but with different hooks, it's just a matter of incrementing that index and storing whatever state is appropriate for that kind of hook. Anytime we do this, we actually increment that hook. So next time we call mount work in progress hook we get the next hook with its state. Now, of course, it's possible that you get a hook which is not the first time the first time there is nothing there. So the first time we create a new state we grabbed that initial state, you store it in that memoize state we saw before and we store that into that currently rendering Fiber hook state. So that's how the state is being maintained in this array. Now, in Reel React, that's not an array. It's actually a linked list. I used an array because it's a little simpler but it's pretty much the same effect. They walked through linked list and do the same thing. So where does this currently rendering fiber come from? Well, in that case we have to start looking at render components. Cause where does it start? Well, it renders a component when the component is called, it will start calling hooks. So react actually uses that flow by saying well in this case, we're called render with hooks. So we've got a component instance to the function render with hooks. We find that currently rendering fiber object by looking for work in progress again based on that component. And that component calls into some kind of work in progress for component collection. And it turns out that at least in my implementation that's just a weak map where the component instance is a key. The value is actually the value returned or store it in there is actually the component state. So this is tied very much to the instance of a component, component goes out of scope then the week reference will garbage collects the state associated with it. Again, the real React implementation is a bit more complex than that, but not a lot, it doesn't use the same components doesn't use a weak map like this. It does use things slightly more complex but the main flow is the same. And if you start looking at it and looking at the code, you will really see an object called currently rendering fiber. And you'll really see a component state like this which has, well, not a hook state array but it will have a linked list in there. So you'll find pretty much all of these same with memoIState for to keep it simple I made some changes but I kept the most of these names the same as they were in the actual React code. So it turns out that in 81 lines of codes even with some comments, okay, not very useful but some comments, I've created the simple mock version of proof of concept version of React hooks with the useState hook. I want to add useEffect. Well, it's actually pretty simple. Add useEffect here, the implementation, this part will be different but the rest of the stuff, Mount working progress hook, to get the actual hook, et cetera, would be the same. The thing being stored on there would not be memoIState of course, because that's for the setState hook. But it would be pretty trivial to add. So that's kind of how React hooks work under the hood, which is pretty useful.

17. Managing Complex State with useReducer

Short description:

When dealing with more complex states, useState may not be sufficient. In cases where you need to store multiple related pieces of state that are tied together, useReducer is a more suitable option. Under the hood, useState is actually a special case of useReducer. useReducer is similar to Redux and follows the same principles. To use useReducer, you call it with a reducer function that takes the current state and the current action being dispatched. The first part of the tuple returned by useReducer is still the current state, and the second part is the dispatch function for dispatching actions. By creating a reducer function and using useReducer, you can manage more complex state and dispatch actions to modify the state. In TypeScript, the type checking ensures that only valid actions are dispatched. The initial state is set as the second argument in useReducer. To dispatch actions, you can create functions that call the dispatch function with the appropriate action type and payload. This approach provides a more structured and maintainable way to handle complex state in your components.

So how about more complex states. We've used useState so far. And in our example, let me get back to that example. usePerson. Here, using a useState is actually fine, because we're doing not much complex stuff with state, and there's really not much to worry about, nor no reason to make it more complex. But there are some cases where you do. And one might be that you start storing multiple related pieces of state, which are not maybe completely related, like it's still a person, object, and something else, but it is tied together. And that could be that you're storing a personal object, but you also want to store form state, like, is the form valid? Is the form dirty? Has it been mutated or not? It's not part of the person object, so it shouldn't go in there, but it kind of belongs together. A change to the person will have effects on that dirty state and potentially on that valid state as well.

Well, you could do it this way, adding multiple use state hooks. And in this case it's relatively simple, and we could get away with doing that. And we might do something like this, where the person, the set person or function we expose, is actually not the original set state person function, but one which is a bit more complex and actually changes multiple pieces of state. Works. And in this case, it's simple enough to get away with it. But if your scenario gets more complex, then it's going to be harder to maintain those different pieces of state and to work with it. Insights are a component. We still use that same use-person. This set person is a different function, quite a different implementation, but it never needs to realize. And now, all of a sudden, it gets additional information back, like in this case, is dirty, so we can disable the submit button, for instance, if the form is not dirty and potentially if it's not valid or something. So we could do something like that. That's perfectly fine. And we're actually gonna skip this part of lab because we're a little behind schema, we'll do it in a different way, and that's using useReducer.

So under the hood, useState is actually a special case of useReducer. Now, if you think useReducer, hang on, I've used Redux that uses reducers, right? Well, you're right, and useReducer is very similar to using something like Redux. It's not the same, it's not as powerful as Redux is, but it uses exactly the same principles. And it turns out, if you start looking into the React source code, useState is just a special case with a special reducer, with another hood, just calls into useReducer. So if you look at the actual implementation of hooks, there is no actual hook useStates, which is managed, it's just a little wrapper around useReducer, and useReducer is actually the only really stateful hook which manages components taken triggers re-renders. Now how do you use useReducer? It's actually pretty simple to use. You call useReducer with a reduce function. A reduce function, just like with Redux, is a function which takes two arguments. The current state and the current action being dispatched. How do you dispatch a function? Well, useReducer uses returns tuple, just like useState does, except with useState, it's the second function is a simple function which takes a new state or state mutator function. In this case, you get a dispatch function back and there you can start dispatching actions. The first argument returns, the first part of the tuple is still the current state. So whether it's useState, useReducer, doesn't matter, that's still the current state. Although we use Reducer, that state is typically more complex, but it doesn't need to be. And it's still tied to a component. Redux is similar in structure to Redux but Redux is global state management removed from your component and different components can share the same state by subscribing to it using a useSelector or something like that. But with useReducer, it's still very much tied to a single component. If you start using context and useReducer, you can actually make a sort of a Redux light. Wouldn't advise that. I would still recommend to use Redux if you want to do that. Now, if you want to do that, the first thing you have to do is create a reducer. And this is the first part of that reducer, which is just a bunch of declarations which we need. And the actual implementation is here. So we've got a function, PersonEditorReducer. It takes two parameters, the state and the action. The state is whatever it needs to be for this useReducer. The action is typically an object with hash type and optionally a payload. Usually it does have a payload, but not every action needs one. And the type usually decides what it is. So in this case, we've got a type of set initial person, which loads personal object, which is stored in the payload. And we've got action set property, which actually changes the person by using the payload, which has a name and the actual value. And all of the code in the middle here is because the person could actually be null. So if we start spreading the person as null, we would get a partial person and TypeScript doesn't like that. So this way we work around that. We're actually going to skip this because the next part, we're going to use, create another reducer. So no need to create a second. I'm just going to explain this part. Inside our use person hook or custom hook, instead of use state, we're now going to use use reducer and we pass in that reducer function, which we find. And here's one difference with Redux, you typically set up your initial state by using the first call to the reducer where state is undefined. In this case, the second argument for use reducer is actually your initial state. So we pass in the object. It has a person on that state, which defaults to null. There is a form state, which defaults to not being dirty and being valid. So this kind of sets that up. It returns the state which has this person in the form state, and it has a dispatch where we can dispatch one of those actions. Then, in order to dispatch something, we kind of do something like this. Instead of exporting a setPerson object, now we set, export a setProperty. And in the setProperty, we say, okay, we want to dispatch something in this case, a type of setProperty, and the payload with the name and the value. And the same happens, but that's a little out of screen now with the setInitialPerson dispatch. Now, if you're used to JavaScript and Redux, you might be used to having action creators here, not dispatching objects like this. If you're using JavaScript with useReducer, I would recommend you do exactly the same thing. With TypeScript, you don't really have to, because if I make a typo here in setProperty, then it will actually start complaining because the type is defined as one of these known types. If I go back for a sec here with the different types, I've set slightly lower. Two different actions. A type setInitialPerson and a type setProperty with their associated payload. And the action I'm expecting is either setPerson action or a setProperty action. So if I try to dispatch an object with type, say foo, then it's gonna say, well, foo is not a known type here. It really needs to be one of these two exact strings. And once it knows which of those two it is, it actually knows the shape of the payload. So if I try to dispatch setInitialPayload with the shape of the setProperty payload, I'll get a compile error again from TypeScript saying, well, it doesn't match. That's not a proper action. It should either be this shape or this shape, not some combination or something else that doesn't match that. So TypeScript actually simplifies cases like that. Here, you can see the other dispatch for the setInitialPerson. So like I said, we're going to skip this part. We're actually going to do this with the second part where we create the Foramic clone. Our components changed slightly. Instead of the setPerson here, we get a setProperty.

18. Building the Kimrov Context

Short description:

In the second part of this workshop, we're going to combine all of the different hooks into something more advanced. We'll create a provider component that maintains the state and provides values to different parts of the system. We'll have a React context object to define how the provider makes things available to the children. We'll also create different hooks, including one for the form and one for input fields. We'll start building the context by creating a context object using the createContext function from React. We'll define the initial values and export the context and interface types. Then, we'll use the context provider to provide the values to other components. The value provided will be of type kimRofContext, which includes a values property.

And in the onChanged, we call setProperty, saying, well, we want to change the firstName property to this, and we want to change the surname to this value, et cetera. And our reducer will take care of that, and it will also take care of setting the dirty property. And there is no actual validation in this code, but assuming there was validation, it would take care of running that and setting the isValid property, et cetera.

So pretty simple to use, and we will do so in a second. So let me close all of these, because we're not going to use this example anymore. Let me push, or stageStatChange as well, because in the second part of this workshop, we're actually going to combine all of the different hooks into something which is more advanced, where we combine different functionality into something useful. And the thing where I want to combine it into is Formlink, or at least what I mentioned, a simplified Formlink clone. Formlink is really nice if you want to do forms over data and if you think about an editor like this, it's really forms over data. We've got some form and presumably this will go back to the server and be stored there and then be served up again in some other place. And apparently I didn't restart the application. Let's do that so it actually works again. So, working again.

Now, I've got this demo where we're gonna do the same in kind of a Formics style. It renders a form, but no values yet, no state management or anything. And just to show you why I like Formics so much, let's close these. Gitmore of Person Editor. If you create an input form like this and you've got your input components set up, then a whole form could be as simple as this. Where you basically say, okay, you go and render some component and the name here actually data binds to an underlying object. So there is an person object with a first name property. Having the name means that it grabs the first name from that object, and actually updates it whenever you make changes. But as I mentioned, this is a simplified example. You won't be able to do everything you can do with ForumEQ. With ForumEQ you could actually have something like a person dot first name in here, or even with arrays and loop over things, et cetera. Very powerful, very easy to use. I'm gonna keep this a lot simpler, but still show how to do that. We're more focused on how the different hooks work together.

So the architecture we're gonna go for will look something like this. We'll create a provider component which kind of maintains the states and provides different values to different parts of the whole system. We'll have useReducer in there to actually track the state and do updates there. We'll have a React context object, which will actually make things from the provider or define how the provider should make things available to the children. We'll have different hooks. There are two here, but we'll actually create more. We'll create three. There is one for the form, which maintains the stuff we want to add to a form. Like the fact that it was submitted. We'll have a useFieldHook, which will maintain things for an input fields, which takes the name we want to bind against and then return things like the value and the onChanged handler or onChange handler, I should say. And that calls back into the provider, into the useReducer with a dispatch, updating the state, having things re-render, et cetera. So, how would we start building that? I've got some components here already. We've got a Person editor, which we're going to leave pretty much as is for now. We'll actually update this at the end, but for the most part we won't need to make a change to it. I've got a wrapper component, KimrovPersonEditor, and by the way, if you wonder about Kimrov, it's for a mic, but then reversed, that's really what I kind of reverse engineered here. Right now it doesn't really add anything. We'll start adding a bit more to it, but not all that much. There's a Kimrov folder here, and there are a bunch of files here which are mostly empty. Like there is a used Kimrov field hook here, but there's actually nothing here yet. So we'll start adding stuff there. There is a KimrovContext, stuff like that, nothing here. A Kimrov component, which will be the actual provider, which currently does nothing other than rendering the children. So that's the stuff we're going to implement to get a more complex working solution. Kind of the beating heart is context and providing some value. So that's kind of the first thing we need to do, because if we don't have that, the different parts can't communicate together. Because the context defines what's available and the provider actually makes things available. So creating a context has nothing to do with hooks by itself. It's really simple. You call createContext from React, it creates a context object and I've specified the actual type here, which is kind of useful to do. And we'll start off with really simple and values object, which has nothing on there. So let's actually add that. So I have this file, kimRofContext. Create a kimRofContext object with lower case O, is createContext. And remember, it's not a context hook we're using, but createContext is just a standard React createContext function. We'll use the hook to retrieve this context later. We'll need to define a type for this. So create an interface, and we use capital K there because it's a type. And we'll say, we'll have values collection on here, which is a type kimRof object. I've already defined some types and created some types. It's not typescripts workshop here, so don't need to do all of that. They're provided. The type we want is not kimRof object, but kimRof context. And immediately we get a compile warning saying, okay, that is invalid because it's missing values. So he set up an initial values of an empty array. We'll need to use both this interface and this context object in different places. So we'll start exporting them both. And a P there helps. The next thing we need to do is based on this context object actually starts providing its values to other places. So in here I can use use kimroffcontext.provider. So every react context has a provider, consumer, et cetera. We're not gonna use to presume consumer, but we will use the provider. Still has a compile error saying property value is missing. And the value is the actual thing you provide for the children. So whenever you use use context, the hook to retrieve the value being provided, whatever is pushing here is the thing you actually get back. So we want that to be this shape. So let's create context here. So use it first and then define it. It is going to be shape context provider. I need to resolve that, and that must have a values property. We need to get that value from somewhere. We want to get that as a problem here, so. So whoever, if I can type value. Whoever is providing this can provide that. So that's going to be of type a more of object.

19. Using the Form Editor and Making Values Visible

Short description:

Now we need to use the form editor wrapped inside the context provider. We need the initial value with the correct type to keep TypeScript happy. After opening the breakout room for 10 minutes, we will make the different values visible.

Why this, if we convert the into a two person or a paracluseball doesn't intellisense work, because I'm in the wrong place. I should be adding that in here with the type. And this will actually be the value. So this compiles fine, but now we still need to use it. Where is my form? So this editor needs to be wrapped inside of that context provider. Like this. And now that says, okay, that's fine, but I do want that initial value which I have a typo in, but that's not going to be too much of a problem. And it so happens that I already have the types in there. So this is the same initial person we used before, but I have to cause it as the right type. So that's just to keep TypeScript happy. If I left that out, it would say that initial person is not exactly the right type. So now everything should compile. And if I refresh, that should all be fine. No compile errors, nothing. We don't see any values yet cause we've just hooked up the basics. We need to do a bit more work before that. We're actually gonna make that the next step, but for now let's do this step and make sure this works. So I'm gonna open up the breakout room for 10 minutes to do this. And then after the 10 minutes, we'll do the next step where we're actually gonna make the different values visible. So let me change the time on the breakout room to 10 minutes. And then, unless there are any questions, I'll open them up. No questions. And I'll see you all in 10 minutes.

20. Making Current Values Visible

Short description:

We will now make the current values visible by using a hook to get the value from the context. A custom hook called 'useKimroffFields' will be created to retrieve the value based on the provided name. The value will be hard-coded for now. The 'kimrovLabeledFields' component will be updated to use the 'useKimroffFields' hook and display the value in the input field. Although the value cannot be changed yet, this is a good first step. The 'name' prop will be made required instead of optional. Breakout rooms will be opened for five minutes to implement the changes.

So we've done an initial step. We've not actually used a single hook yet for this, but we will right now because the next step we're gonna do is actually make the current values visible. So right now in the editor, first name, surname, et cetera, they're all empty. I can refresh nothing shows up. Well, that's the first thing we're gonna do, make those values. And we've got a context which contains the values. They can't change it, we'll do that later. But the initial values are there. Just like you would perform it. And we've got a component here that Gimruf labeled field, which is gonna render them. So there is a name on this prop, I've actually made it required. So, suppose I left name out here, removed it. I actually get a compile error there. So, I know I've got a name, somehow in here I actually want to get that value. Well, if we've got a context and we wanna get something from that context, we can use a hook. And in order to make it reusable, I don't want to put that code in here, I actually want to put that inside of another custom hook which I can reuse for different input components. So, I've got a placeholder, use Kimroff field. And I'm actually gonna create another custom hook in here. Export function. Use Kimroff fields. It will take the name we want as a required input. And it's gonna return an object. And for now we want the value in there, so. Let's just hard code it to one, two, three for a moment. Now I can go to, don't have that open anymore, back here, I can say I wanna use this field, pass in the current name. So, props dots name. That will return me an object. And that's basically the additional props I wanna spread into that input. In this case there is only a value yet, but there will be more. So, input props, or field props, whatever you wanna call them. Input props. And we see 1, 2, 3 in all the fields. Still 1, 2, 3 because that's basically hard-coded. And where was my hook? Right here. So, how do we get the actual value? Well, we've got that context objects. So, with use context, we can get the actual provided value. So, parse in Kimmer of context... and const. We get an object from there, and in this case, we're only interested, at least right now, in the values. So, the values are all the initial values being passed in. And we can use an indexer with the name to get the actual value we need from there. So, now the value should be the actual value from the initial name, so Frida, Lauri, et cetera. You still see an error here. You should provide a value prop without... Or you shouldn't provide a value prop without an on-change handler or make it read-only. As a result, we can't make any changes to it yet. We'll do that later, but we should be able to do that now. We'll do that later, but this is a first nice step. So implement that useChemeraFields hook, use the useContexts to actually get the value. And in the slide, name is optional, but I actually changed it later so it's required. So you don't actually need to worry about it being nullable. So just leave out that question mark there, just define it, so name, colon, string, and that makes things simple. So add it to kimrovLabeledFields, get the field props from there, spread it in. Later, we'll actually expand those with more, but for now it will just add the value to the input and it will be happy. So that's a pretty small change. I'm gonna open up to breakout rooms for five minutes to do that. And in five minutes, I'll see you all of that and then we'll continue expanding on this. So see you in five minutes.

21. Adding useReducer and Reducer Function

Short description:

To enable changes to the displayed values, the useReducer and reducer function need to be added. The reducer function is implemented using a switch statement to handle different actions. When the action type is 'set property', the values object is updated with a new value based on the payload. The form state is also updated to indicate that the object is dirty. The implementation of validation is left as an exercise. The initial values and form state are passed into the useReducer hook. An onChange handler is added to trigger changes to the name. The setFieldValue function is defined to update the field value. The default implementation of setFieldValue does not modify the values. The actual implementation can be added here.

Okay, that's everyone back in the main room. So we're displaying values, but if we wanna see values, we also wanna be able to change them. After all, we're showing values in an inputs component and it really should allow us to make changes. So the first thing we need to do is add a use reducer and then reduce your function so we can actually start making some changes.

So the state is maintained inside of that provider so that's where we'll want to use a Reducer Hook. So in here, instead of just passing the initial values, we want to introduce useReducer. It needs a reducer function. So I've already got the boilerplate for that so let's paste that in. Already got the shape, the reducer state so let's define that. And make sure all of those are resolved. So that's going to return us the current state and a dispatch function. this part.

Now, this reducer doesn't do anything yet, so we need to implement this. In this case, all possible actions is actually pretty simple. There is just a simple single one. So relatively easy to do, but it'll still follow the same principles which you normally do. So you create a switch statement or a similar way to map with different functions for different actions. So based on the action.type. In case it's a set property, we're going to do something. In this case, we're going to return a new state object. We start by spreading the current states. Well, what do we want to change? We want to change. that we have a comma there. But we want to change the actual values. So. Spread out the current values again. So we've got a copy of the current values and we want to overwrite one. Which is. Action dot payload dot name. And the new value is action dot payload dot value. So that gives us a new values object. We also want to change the facts that our objects become dirty. So we've got a from state object in here. We want to do the same kind of thing. Spread out the current firm state. And sets is dirty to true. So we can use that to enable the safe button. And that's it for us. The reason is valid in here. I'm not going to implement that bits, but it would follow the same lines with a validation action setting the valid and probably a validation message per field. Basically for mic use is a mechanism where it is a values structure with all the different values and there is an error structure which is exactly the same shape, but the presence of a string there means that that field has an error. But like I said, I'm not going to do that. I'll leave that for you to do later. So we have that. So now we need to pass in the initial value. So values is the initial value and form state is dirty. Why am I not getting completion here? Is false, is valid, is true by default. Generic, reducer state. Ah. No, do I need to pass in? It says reducer state has one generic argument, but that's... Because of a wrong import. Reducer state from react. Do I have a typo? No, I'm missing an export here. That's why it resolved to the wrong one. And now it still doesn't isn't happy. What's wrong? Google is working on a new software there. What am I doing wrong here? And. Let's take a look at the slides. I'm going to go and do a quick test here. Okay. So we've got the welfare page done and let's click the publish button here. For now, you say we want the values from there. And now we've got a set of values. So we've got the means to change values, but we can't actually do so yet. So in order to do so, we want to add something here. We want to add an onChange. And that onChange handler will get an argument, which is a React.ChangeEvent of an HTML input element. And in here, we want to do something with e.target.name. And we've got e.target.value. We want to trigger something to change the name. So in order to get to that reducer, we have to go through the context. So we want something more than these values from that context. And why is it actually complaining here? That's probably because of this. No, that's a missing bracket. So we want another thing in here. So let's add a function, setFieldValue. Where we take a name, which is some kind of string. And a value, which is one of those... ... property. Give it more properties. So in here, I've defined a specific property saying, well, all properties can be strings or number. In a real scenario, that will be more complex but for this, that's enough. So we want that and that doesn't need to return anything. So it returns a void. Um, if my syntax is correct. Almost, now it's correct. So we need to include that in the implementation and this is just a default implementation, doesn't do anything, so this can return nothing. An null in this case, and it doesn't like that because that should be a comma and we can actually leave those out as well because they're not gonna be used anyway. The actual implementation has to go in here.

22. Updating Values with Dispatch and Context

Short description:

We want to use the dispatch function to update the state by dispatching an action. By retrieving the set field value from the context and calling it with event target and event value, we can change the values. The changes are reflected in the console. These changes involve updates to the reducer, contexts, objects, and hooks. In the breakout room, we will go through the steps together.

So, what do we want to do here? Well, here, we want to use the dispatch function and dispatch an action. Oops, without the quotes. Type is a set property, which is really nice with TypeScript. Like, you know exactly what types are there. And then payloads is an object which has that name and that value. So that will make sure that whenever I call this set field value, dispatch, an action is dispatched to the reducer. It will update the state. The values returned from that are updated and provided again through that context to all the children. So we're almost there. What we need to add is that we retrieve this set field value from the context. And actually call that with event target and event value, event target name and event target value. And now we should be able to change values. So let's take a look. And we can actually change them. Don't actually see the effect. So let me add a quick console dot log. And let's do so in the reducer. Actually let's do so here, so we can see the value. The value is the values. The values. So now every time I make a change we see that change appear in the console. So a few changes in different places, reducer, and a contexts, objects, and a hook. But with those different changes together you can start editing inside of this editor. So I'm gonna open up the breakout room for 10 minutes ago again to do this because there are quite a few different steps involved in having to do that. So I'll see you all in 10 minutes.

23. Implementing Form Submission and Displaying Values

Short description:

We want to add another hook and something to the form element to enable form submission. We create a custom hook called Kim Ruff Form that returns an object with an onSubmit function. The onSubmit function calls the user-provided onSubmit function with the current values. We prevent the default browser behavior of submitting to the URL. We call the onSubmit function provided by the user of this library. We add extra properties to the form and spread them out. The form can now be submitted, and the changes appear. We open a breakout room to implement the submit feature. After the breakout room, we make a small last change to enable the save button and display the values. We create another hook called skin growth to get the values and is dirty state. We call the useGimRough hook in the Person Editor component to get the values. We add the is dirty flag and provide it in the form component.

Okay, that's everyone back in the main room. I actually just realized I went a little too fast. This step was actually about adding the reducer and I immediately did the next step about actually editing the data and adding the onChange handler, but no problem we're gonna do that, I just did it a bit faster. So we can skip that as done. So we can see data, we can edit data, the next thing we wanna make sure is we can actually submit the form when it's complete and gather that data. So we wanna add another hook and we wanna add something to the actual form element. So the one which is right, where's the form? Right, right, right. Here, this form object. So we typically wanna add an onSubmit here, but we'll do it in very much the same way as we did with that field by adding a hook and making that reusable so it's not specifically tied to form. A bit more we need to do there is prevent the default behavior as well, because right now if I would actually, I have to enable it first, so I can submit. But now if I submit, we get the default behavior of the browser where all the values are actually added to URL. So don't want that. So let's go and add that. So the first thing we wanna do is be able to add an on submit here in a reusable way. So again, we'll use a custom hook very much like we did with the Kim Ruff fields, but now Kim Ruff form. Export function Kim Ruff Form. It returns an object and that will have an on submit. So there we want to do something. Well, in the end, we want to call on submit function provided by the user. So where does the user actually work at this level? So in the end, we want something like an on submit here, which is a function, which does something, and this should get the current values. And let's just do an alert with those. string of either values, and give them a bit of a format values Now, that doesn't compile yet, because we still need to define them. So we'll define those props. An onSubmit prop, which is a function which takes values of type Tdata, which is the generic type been passed in and returns nothing. So we want the same onSubmit in here. So we can actually start calling it later. In order to call it, we want to provide another function on the context. So to find another function on there. We also call this onSubmit. In this case, it's a function which takes a react.form event. React. No return. The same default. So onSubmit there. Then defaultplaceholder. This doesn't do anything yet. The actual implementation we need will go in here. The first thing we need to do is prevent the default. The default browser behavior where it submits back to the URL and changes that. And then the next thing we want to do is call the onSubmit function provided by the user of this library. So we'll call that and we want to call that with a set of values. Except these are type-slightly different. So here we need typecostStData. So that almost works. We still need an implementation here. So const is useContext. skimOrgContext so we can get that onSubmit function here. And we can just provide that because this already takes that form event as an argument. So that's almost working. We have to go back into this component and now actually call into this. I want to go there. I want to resolve this. So that's extra properties we want to add to the form. So we can spread those out into here and now we should be able to submit the form. So if I make some changes here, click save. We see it's submit and we see our changes appear here. So that allows the user to make changes, save those to wherever they need to go, a server, whatever. So a couple of things to do. Let's actually do this first before we do the last bit. I think I combined them in here, but yeah, I combined them in here but we'll do this bit with the isdirty, etc, in a small next step. So here's the form assignment, isdirty, etc. in a small next step. So I'll open the breakout room for five minutes where we can implement the submit feature. Let me change the time and open up all the rooms and I'll see you in five minutes and then we'll do the last few changes. Okay that's everyone back so one small last change to make. Like we've mostly got a working solution and the one thing we don't have is that if I come into the screen this save button is disabled it doesn't become enabled until we actually made a change and also what I previously had is that I was displaying all the values here. Well, two small things to do. So, in order to do that, we could actually combine that into an existing book with I typically create a separate hook for that so you skin growth which will just get all the generic data. In this case, just the values and is dirty which is needed for that bit. Could do that in the form. You Skin Reform hook as well because especially that is dirty, it's kind of associated with that. But we'll do it this way for now. So. Create another pretty simple hook. Export function skin growth. It uses the same context again. That should be const. We'll get a couple of things from it. Actually, we'll only get the values right now because this dirty state is not provided yet. We'll add that in a second. Add that in a second. So we've got the values. We go back to the Person Editor. We call the useGimRough hook, and we can get the values from there. So back in the editor, they should now appear as they do. And if I make changes, we see the changes being made right here. So I still want to get the dirty flag from there, but that's not provided yet. So let's add that. So we'll default that to false. That means we have to provide it in here.

QnA

Concluding Remarks and Q&A

Short description:

Hooks are simple and elegant to use. They have a few rules to stick to, but they are not hard or restrictive. Custom hooks are powerful and can be found in NPM packages or GitHub repositories. Hooks are a great way to add non-UI functionality to your components. The second part of the lab demonstrated how to combine hooks. Thank you all for attending and learning with us!

But that should already be available over here. So we've got the values, we've got the form state. That is dirtyflag. It is already being set in the reducer, so with this it should work. So we come into the forms. The save button is disabled and I start editing and it's still disabled. So apparently I am missing something. Let's see. In the reducer I am setting this dirty to true. Hmm, hmm, hmm, anyone spot what I missed? It doesn't like that, because that's the step I missed. Let's try again. It is disabled, I start typing and it becomes enabled. I forgot to export it, so it was actually undefined, which is false, so it was never dirty. So another pretty small change, and with that everything we needed to do is complete. So I'll open up the breakout rooms for three minutes to do this. Oops, three minutes. And then after that, we're done. I'll just wrap it up and we'll be done with the workshop. So see you all in three minutes.

Okay, that's everyone back, so that was the last exercise, time to wrap it up. Actually went a bit over time, so sorry about that. I hope it's not a problem. So conclusion. Hooks are typically really simple to use, but you kind of have to know the rules to stick to them. They do exist for good reasons. Not that there are that many rules and that they are that hard or restrictive, so not typically a problem. Hooks are actually done really simple and really elegantly, which is really nice. Nice detail of the implementation of hooks when the React Team first shipped hooks. Other libraries like Preact and Vue actually had similar prototype implementations in a day or a few days. So easy to implement and actually so useful. Custom hooks are great. The standard hooks are powerfully useful, but creating your own custom hooks or finding them on the internet because there are plenty of NPM packages or GitHub repositories with lots of useful hooks. So highly recommended to do that. Create your own set of hooks for usage. Things like that, use debunks, et cetera, are great little utility hooks to keep around. And in general, hooks are a great way to add functionality you need for your components that's not exactly UI itself. So create components for UI functionality, create hooks for all the non-UI functionality, and wire it up together in a really nice way. I hope the second part of the lab where we created that former clone showed a nice way of how you can combine all of these things. So thanks for being here. Thanks for attending. I hope you learned something useful and something you can use in your day-to-day job with React. Remember, you can find me on Twitter, at MauriceDB, or you can email me if you've got questions about this. Everyone will get a recording of this session. You've got all the materials, you've got the slides, you've got the GitHub repo with all the codes. So all of that is useful for reference if you want to. So that's the end. Thanks all for being here. If anyone has any last questions, now's a good time to ask them. Thank you so much. You're welcome, thank you for being here. It was a great workshop, thanks a lot. Thank you for the kind words. Wonderful, thank you. Thank you. Really helpful, thank you.

Watch more workshops on topic

React Summit 2023React Summit 2023
170 min
React Performance Debugging Masterclass
Featured WorkshopFree
Ivan’s first attempts at performance debugging were chaotic. He would see a slow interaction, try a random optimization, see that it didn't help, and keep trying other optimizations until he found the right one (or gave up).
Back then, Ivan didn’t know how to use performance devtools well. He would do a recording in Chrome DevTools or React Profiler, poke around it, try clicking random things, and then close it in frustration a few minutes later. Now, Ivan knows exactly where and what to look for. And in this workshop, Ivan will teach you that too.
Here’s how this is going to work. We’ll take a slow app → debug it (using tools like Chrome DevTools, React Profiler, and why-did-you-render) → pinpoint the bottleneck → and then repeat, several times more. We won’t talk about the solutions (in 90% of the cases, it’s just the ol’ regular useMemo() or memo()). But we’ll talk about everything that comes before – and learn how to analyze any React performance problem, step by step.
(Note: This workshop is best suited for engineers who are already familiar with how useMemo() and memo() work – but want to get better at using the performance tools around React. Also, we’ll be covering interaction performance, not load speed, so you won’t hear a word about Lighthouse 🤐)
React Advanced Conference 2021React Advanced Conference 2021
132 min
Concurrent Rendering Adventures in React 18
Top Content
Featured WorkshopFree
With the release of React 18 we finally get the long awaited concurrent rendering. But how is that going to affect your application? What are the benefits of concurrent rendering in React? What do you need to do to switch to concurrent rendering when you upgrade to React 18? And what if you don’t want or can’t use concurrent rendering yet?

There are some behavior changes you need to be aware of! In this workshop we will cover all of those subjects and more.

Join me with your laptop in this interactive workshop. You will see how easy it is to switch to concurrent rendering in your React application. You will learn all about concurrent rendering, SuspenseList, the startTransition API and more.
React Advanced Conference 2021React Advanced Conference 2021
174 min
React, TypeScript, and TDD
Top Content
Featured WorkshopFree
ReactJS is wildly popular and thus wildly supported. TypeScript is increasingly popular, and thus increasingly supported.

The two together? Not as much. Given that they both change quickly, it's hard to find accurate learning materials.

React+TypeScript, with JetBrains IDEs? That three-part combination is the topic of this series. We'll show a little about a lot. Meaning, the key steps to getting productive, in the IDE, for React projects using TypeScript. Along the way we'll show test-driven development and emphasize tips-and-tricks in the IDE.
React Advanced Conference 2021React Advanced Conference 2021
145 min
Web3 Workshop - Building Your First Dapp
Top Content
Featured WorkshopFree
In this workshop, you'll learn how to build your first full stack dapp on the Ethereum blockchain, reading and writing data to the network, and connecting a front end application to the contract you've deployed. By the end of the workshop, you'll understand how to set up a full stack development environment, run a local node, and interact with any smart contract using React, HardHat, and Ethers.js.
React Summit 2023React Summit 2023
151 min
Designing Effective Tests With React Testing Library
Featured Workshop
React Testing Library is a great framework for React component tests because there are a lot of questions it answers for you, so you don’t need to worry about those questions. But that doesn’t mean testing is easy. There are still a lot of questions you have to figure out for yourself: How many component tests should you write vs end-to-end tests or lower-level unit tests? How can you test a certain line of code that is tricky to test? And what in the world are you supposed to do about that persistent act() warning?
In this three-hour workshop we’ll introduce React Testing Library along with a mental model for how to think about designing your component tests. This mental model will help you see how to test each bit of logic, whether or not to mock dependencies, and will help improve the design of your components. You’ll walk away with the tools, techniques, and principles you need to implement low-cost, high-value component tests.
Table of contents- The different kinds of React application tests, and where component tests fit in- A mental model for thinking about the inputs and outputs of the components you test- Options for selecting DOM elements to verify and interact with them- The value of mocks and why they shouldn’t be avoided- The challenges with asynchrony in RTL tests and how to handle them
Prerequisites- Familiarity with building applications with React- Basic experience writing automated tests with Jest or another unit testing framework- You do not need any experience with React Testing Library- Machine setup: Node LTS, Yarn
React Advanced Conference 2022React Advanced Conference 2022
148 min
Best Practices and Advanced TypeScript Tips for React Developers
Featured Workshop
Are you a React developer trying to get the most benefits from TypeScript? Then this is the workshop for you.In this interactive workshop, we will start at the basics and examine the pros and cons of different ways you can declare React components using TypeScript. After that we will move to more advanced concepts where we will go beyond the strict setting of TypeScript. You will learn when to use types like any, unknown and never. We will explore the use of type predicates, guards and exhaustive checking. You will learn about the built-in mapped types as well as how to create your own new type map utilities. And we will start programming in the TypeScript type system using conditional types and type inferring.

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

React Advanced Conference 2022React Advanced Conference 2022
25 min
A Guide to React Rendering Behavior
React is a library for "rendering" UI from components, but many users find themselves confused about how React rendering actually works. What do terms like "rendering", "reconciliation", "Fibers", and "committing" actually mean? When do renders happen? How does Context affect rendering, and how do libraries like Redux cause updates? In this talk, we'll clear up the confusion and provide a solid foundation for understanding when, why, and how React renders. We'll look at: - What "rendering" actually is - How React queues renders and the standard rendering behavior - How keys and component types are used in rendering - Techniques for optimizing render performance - How context usage affects rendering behavior| - How external libraries tie into React rendering
React Summit Remote Edition 2021React Summit Remote Edition 2021
33 min
Building Better Websites with Remix
Top Content
Remix is a new web framework from the creators of React Router that helps you build better, faster websites through a solid understanding of web fundamentals. Remix takes care of the heavy lifting like server rendering, code splitting, prefetching, and navigation and leaves you with the fun part: building something awesome!
React Advanced Conference 2021React Advanced Conference 2021
39 min
Don't Solve Problems, Eliminate Them
Top Content
Humans are natural problem solvers and we're good enough at it that we've survived over the centuries and become the dominant species of the planet. Because we're so good at it, we sometimes become problem seekers too–looking for problems we can solve. Those who most successfully accomplish their goals are the problem eliminators. Let's talk about the distinction between solving and eliminating problems with examples from inside and outside the coding world.
React Advanced Conference 2022React Advanced Conference 2022
30 min
Using useEffect Effectively
Can useEffect affect your codebase negatively? From fetching data to fighting with imperative APIs, side effects are one of the biggest sources of frustration in web app development. And let’s be honest, putting everything in useEffect hooks doesn’t help much. In this talk, we'll demystify the useEffect hook and get a better understanding of when (and when not) to use it, as well as discover how declarative effects can make effect management more maintainable in even the most complex React apps.
React Summit 2022React Summit 2022
20 min
Routing in React 18 and Beyond
Concurrent React and Server Components are changing the way we think about routing, rendering, and fetching in web applications. Next.js recently shared part of its vision to help developers adopt these new React features and take advantage of the benefits they unlock.In this talk, we’ll explore the past, present and future of routing in front-end applications and discuss how new features in React and Next.js can help us architect more performant and feature-rich applications.