React for Beginners: Build an App, and Learn the Fundamentals | Ryan Johnson | Skillshare

React for Beginners: Build an App, and Learn the Fundamentals

Ryan Johnson, Full-Stack Developer, React, Node.js

Play Speed
  • 0.5x
  • 1x (Normal)
  • 1.25x
  • 1.5x
  • 2x
18 Lessons (58m)
    • 1. Introduction

      1:18
    • 2. Introducing Codesandbox

      1:54
    • 3. Fundamentals: Components

      5:05
    • 4. Fundamentals: JSX

      3:40
    • 5. Build an App: Introduction

      4:07
    • 6. Build an App: Component State

      5:11
    • 7. Build an App: Lifecycle Methods

      3:55
    • 8. Build an App: Add Search Form

      3:56
    • 9. Build an App: Save Search Query

      2:23
    • 10. Build an App: Submit Serach

      3:40
    • 11. Build an App: Render Search Results

      2:49
    • 12. Build an App: I'm Feeling Funny

      2:20
    • 13. Build an App: SearchForm Component

      4:26
    • 14. Build an App: Refactoring and Clean Up

      3:13
    • 15. Build an App: Adding Styles

      3:41
    • 16. How it Works: Rendering into the DOM

      1:43
    • 17. How it Works: Rendering Updates

      2:58
    • 18. Conclusion: Wrapping Up

      1:22
55 students are watching this class

About This Class

Learn React without all the distractions. This course will stick to the fundamentals, and guide you through the creation of your first app.

A lot of React newcomers tend to get hung up on external dependencies like webpack, and redux. While tools like this are great they tend to add additional complexity when starting out. 

In these lessons I will be avoiding all these distractions, and sticking to React fundamentals, and guiding you through the creation of your first app.

Some of the topics covered include:

  • Elements and Components
  • Working with JSX
  • State and Lifecycle
  • Working with Forms
  • Creating Dumb/Stateless Components
  • Building your first App

In addition to the above I’ve also included two bonus videos on React rendering, and updates. If you have ever wanted to learn a bit about how React works under the hood these are for you.

After taking this course, you’ll have a solid foundation in the fundamentals of React, and will be confident enough to move onto more advanced topics, which I hope to cover in future courses.

What knowledge & tools are required?

  • JavaScript, HTML, and CSS fundamentals are required
  • Not required, but experience with JavaScript ES6+ is beneficial
  • No prior experience with React is required

Additional Resources

Transcripts

1. Introduction: My passion for web development, definitely stems from my love of problem-solving. There's something very satisfying about taking a problem and bending it to your wills on code. Hi there. I'm Ryan Johnson. A web developer from Canada, where I've had the privilege of working in the tech industry for 15 years now. Over that time, I've been lucky enough to work at a variety of different places, ranging from small startups to large enterprise companies. Today, I want to introduce you to one of my favorite libraries I work with, React JS. Not only is React JS backed by Facebook, but it also has one of the most vibrant development communities out there. I started using React three years ago and I haven't looked back since. For me, React makes UI development more fun. React bring some much needed order to interface development by allowing you to split up your interface into nice, clean components. This course, is geared towards developers that all ready have a good understanding of JavaScript, but want to pick up React. A lot of newcomers to React, tend to get hung up on external dependencies like Webpack, and Redux, and Babel, and linting, and testing, and so on. These lessons, I'll be avoiding those distractions and sticking to React fundamentals, and then guiding you through the creation of your first app. To wrap things up, I'll do my best to unravel how React works and what makes it so efficient. If you're like me and like to understand what's going on underneath the hood and the tools you use, these lessons are for you. I'm super excited to introduce you to React and I really hope you enjoy working with it as much as I do. Let's go ahead and get started. 2. Introducing Codesandbox: Before we get started, I want to introduce you to a powerful online code editor called CodeSandbox, which we'll be using for the majority of these lessons. I'm going to start by creating a new project by clicking on Create Sandbox. As you can see, CodeSandbox supports not only React, but many other popular frameworks as well. I'm going to go ahead and select the React project, and with a single click, I now have a fully functioning React app up and running in my browser. CodeSandbox has allowed me to skip the setup and focus on the React fundamentals we're here to learn. First thing I'm going to do, since I already have an account is sign in to CodeSandbox as my GitHub account. Feel free to create an account of your own so you can follow along the lessons. The final editor portion of CodeSandbox is pretty standard and works the same as most popular code editors out there today. You have your file browser. We can view all the files and directories in the project. As you can see, CodeSandbox has gone ahead and created some files for us already. If I want to add a new file or directory, I can use the buttons over here at the top. Below the file editor, we have the Dependencies section. This displays all the NPM dependencies installed for the project. You can see that CodeSandbox has already installed some React dependencies for us. Adding new dependencies is easy as well. Just click on Add Dependency, search for the one you want, and then click it to install. A single click on a file will open in the preview mode, while double clicking on a file will open the file in edit mode. You may have noticed on the right something that looks like a browser window. This is a live view of the app that will auto-reload when a file changes. To demonstrate, I'm going to go ahead and update our h1 to say Hello React Beginners. Now you can see, as soon as the update is done, it automatically updates here on the right. Lastly I want to show you CodeSandbox's built-in console. This pretty much mimics the functionality of your browser's console. So if I go ahead and console log something out, I can see the peer hearing code sandboxes console. And just like the browser console, I can also execute commands as well. If I go ahead you in Alert that says Hello from the console, you can see that it gets executed and appears here at the top of the browser. Enough of our code sandbox already. We're here to learn about React after all. But that being said, let's go ahead and get started. 3. Fundamentals: Components: When it comes to react, it's all about the components. Components are probably my favorite part of developing a react. With components you're able to take any UI and split it into smaller, reusable chunks that can work independent of each other. For example, let's take the code Sandbox interface we're working in and think about how we might split it into components. Ignoring the header and footer areas, I see the potential for three top-level components. A File Browser component, File Editor component, and a Browser Window component. Each of these components can be made up of their own components as well. For example, the file browser has an Accordian menu that could be its own component and each item inside the Accordian, can also be a component. If you wanted to go further, each Items icon can even be a component as well. Breaking your UI into smaller manageable components like this has all sorts of benefits including making your code easier to reuse and test. But we can't talk about components without mentioning elements. An element is the smallest building block and react and describes what you eventually see on screen. To demonstrate, I'm going to start by creating a simple H one element. Inside that element I will have some hello text and then I'll include it in the app components we can render to screen. This is an example of a very simple element, but elements can be more complex as well. Here's an example of more complex element named wrapper that's made up of a div with some paragraph text inside. You can think of elements as the building blocks that are used to piece together your components. So what does a component look like? A component in its simplest form is just a function. For example, let's say we had a simple adding function that took two parameters and return the sum of two values. You can see if I pass the add function two and two, I get four log to the console. If I change this second parameter ten, I get 12 back in the console. But what is really cool is that we can easily convert this add function to be a react component instead. First, use a capital A for the component function name as all components need to start with a capital letter. This is because react will treat components starting with lowercase letter as DOM tags. Next, instead of the function taking two parameters, they'll take a single parameter named props. Props are how you pass data into your component. A component is an immutable function, meaning as long as you give it the same input and reacts case props should always receive the same output. Now I can take the Add component and have the app render it. To pass in the values for a and b, you just add them as properties to the component. Now you can see that the sum is being rendered to the screen. When it comes to components, react actually provides two different options. The first and the simpler of the two are functional components, which is a function that has a props parameter and returns a valid react element. The Add component we just created as an example of a functional component, the second or class components, and like the name says, the component will be a class instead of a function. For example, I can easily convert the Add component to a class component. Start by converting const to class. Then have the class extent from react dot component. Then add a render function and have that return the element instead. Lastly, since props is now an instance variable, add this in front of it. There you have it, a class component. But the output looks exactly the same as before, which begs the question why use a class component at all? It just so happens that there's a very good reason, only class components have access to react state and lifecycle methods. Neither of these features are available from functional components. Don't worry we'll get into both state and lifecycle methods later. Another nice feature of components is the children prop. For example, let's say I wanted to create a reusable page layout component. The idea being that all my pages show the same Header and Footer. But either pass in different body copy for each page. So how do I do that? Enter props dot children. Now I'm going to render the layout component, but instead of a self-closing tag, I'm going to use an opening and closing tag. Then I'll add some paragraph elements for this layouts body copy. Just like that, we can see that my body copy is being rendered in between my Header and Footer. You can think of props that children as a placeholder for whatever content you include between the opening and closing tags when you render your component. I'm going to go ahead and add another layout component, but this time I'm going to pass in different body copy. Because layout is using prompts dot children, I'm able to pass in different body copy to the same layout component. This is a very powerful feature that allows you to write components that require no knowledge of the children there including. You may have noticed that when our creative layout component, I wrapped it in a div element, same with the app component when I added the second layer component. This is because a component should only have a single root element unless using something react calls fragments. To demonstrate I'm going to remove the wrapper div from the layout component and see what happens. You can see as soon as I do that I get an error. Adjacent JSX elements must be wrapped in a closing tag. Pretty much this is saying I'm expecting a component to return a single element and you've given me three instead. Now there is a way to return multiple elements and that is to use fragments like the error suggests. This is handy as it prevents having to wrap the elements and what is a useless deal in this case. To update layout use fragments, I just need to wrap the elements in the react dot fragment component. Now when our page renders, there won't be an extra div element and our error should go away. That wraps up everything I wanted to cover in component fundamentals. Next I will be taking a look at the HTML like syntax we've been using to write our components called JSX. 4. Fundamentals: JSX: We have one more thing to cover before we jump into creating your first reactor, JSX. If you recall earlier I mentioned JSX is an HTML like syntax used to describe react elements. Although it may look a lot like HTML, it's actually an extension of JavaScript and most people find it easier to work with them reacts top-level API calls like react create element. With that being said, it's important to remember that JSX still gets compiled down to react create element calls. To demonstrate or use a tool that actually show us what JSX gets compiled down to. I'm going to just go ahead and copy our app component. On the left you'll see the JSX version of the component and on the right you'll actually see the compiler output from that component. If we take a closer look at the compiled version of the component, you can actually see it's made up of multiple calls to react.createElement. Each one of these calls corresponds to an element in JSX component. We have our main react create element call, which is a div, which goes back to our main div here. That has a class name of App, and then it has some child elements as well, an h1 and h2, which we in both see represented here by two more calls to react create element. When comparing these two versions of the same component, I find that JSX version much easier to read. I think this will hold true for most developers that are familiar with HTML. All the lessons to follow will be using JSX. But I wanted to show what is going on under the hood when you create a JSX component. The real beauty of JSX is it comes with the full power of JavaScript behind it. Let's start with some JSX fundamentals, first, embedding expressions. Now this all sounds really fancy, but we've actually already worked with an expression here. Our props.version that's being rendered on screen is an expression. In JSX, your expressions will just be wrapped in curly braces. The contents of the curly braces are what are actually being evaluated. In this case, we're just outputting the value of props.version to the screen. Before we continue I want to make a couple quick twist CodeSandbox. We're going to start by collapsing our file editor by clicking on the file editor tab. Then I'm just going to shrink our browser window a bit here on the right. Now I'm going to go ahead and update our expression, that actually renders a message conditionally based on the value of version. If version is greater than one, I'm going to render out a message that says invalid version. If version is less than or equal to one, then I'm going to actually render out the props.version like we were doing before. I go ahead and update a version to be two point although, you can now see that we actually have a new message, invalid version rendered to screen. As you can see, we didn't have to do anything special here to get this to work. This is just a plain old JavaScript ternary operator that renders one message or another based on a condition. This is what I love about JSX, it is just JavaScript. No need to learn yet another templating language. Now that we've made some changes, I'm going to go ahead and save our work. You may have known as soon as we save our code actually auto format. This is because CodeSandbox is actually using a tool called prettier, which takes the monotonous job of code formatting and does it for you automatically. Now that we've covered JSX expressions, let's move on to attributes. When specifying attributes and JSX, there are some key differences when compared to HTML attributes. ReactDOM uses camel case for attribute names. For example, lowercase on click becomes camel case on click, lowercase tab index becomes camel case tab index. When specifying attribute values, you have two options. The first is string literals, and it's straightforward enough. The second is expressions which allow for creating dynamic attribute values like so. Just to show this was working, I'm going to pop open the console here. You can see that this is actually evaluating to a tab index of two. Be careful when using expressions as attribute values, not to wrap it in quotes. This will result in the expression not being evaluated and being treated as a string literal instead. That concludes the basics of JSX. But don't worry, we'll be getting into more advanced JSX usage in the upcoming lessons. 5. Build an App: Introduction: I found the best way to learn a new framework is to actually build something. Now that we've got the fundamentals covered, I want to walk you through creating your first React app. So what are we building? For our first app, we're going to be creating a simple Google clone. But instead of being able to search for anything, our Google is going to search for dad jokes and only dad jokes. If I go ahead and enter "cat", click "Search", I get back a list of five prime cat-related dad jokes. If I enter a new search term and click "Search", I can see the search results are refreshed with new dad jokes. Lastly, unlike Google's "I'm Feeling Lucky" button, we have the "I'm Feeling Funny" button, which will return a single dad joke in this case. Although this app may seem pretty straightforward, we're going to cover a lot of growl in creating it, so let's get started. We're going to keep using CodeSandbox, the creator app. But before we begin, I want to kind of start with a clean slate. So I'm going to go ahead and delete our previous work and start with a nice blank app component, and disregard the arrow that appears for now. To start with, we're going to keep things really simple. We're going to have a single button that when clicked, generates a single dad joke. So I'm going to start by going ahead and creating that button, and I'm going to label it with "Tell me a joke". So you can see we have our new "Tell me a joke" button being rendered on screen here. But when I click on it, not a whole lot is happening. To handle click events in React, you use the onClick event.Let's go ahead and add one to our button here. The onClick event actually takes a function as its value. So we're going to go ahead and add a simple function that logs out a value when clicked. So let's just go ahead and open our console here. Now, you can see that every time we click the "Tell me a joke" button, the word "click" is logged to the console for each of those clicks. Now that we know our onClick event is actually working, I want to go ahead and move our onClick handler into its own function. I'm going to go ahead and call it onTellJoke, and it is going to log our click just like its doing right now. So I'm going to take this new onTellJoke, and I'm going to replace the inline function we created earlier, and I'm just going to go ahead and save. Now, when I click "Tell me a joke", we should still see the click logged out. Now this joke button still isn't doing very much when we click on it. What we really want to happen is every time you click the joke button, you get a joke back, a dad joke, specifically. But before we could do that, we need a place to get some dad jokes. Since there's an API for everything these days, we'll be using a free API from icanhazdadjoke.com for all our dad joke needs. Let's take a closer look at API docs to see what we're dealing with. First, you'd notice that no authentication is actually required for these calls, which is one less thing to worry about. Next, you'll see the API actually supports multiple response formats. In our case, we're only concerned with the JSON option and that's what we'll be using for our tutorial. For our "Tell me a joke button", we're going to be using the "Fetch a random dad joke" end point. The way this will work is that every time the button is clicked, a new random joke will be generated and sent back to us. We can see a sample of the response that will be returned here, which is an object that contains the joke along with a unique ID and an HTTP status code. Now that we have our end point, let's go back and hook up our joke button. In order to call the "Random joke" endpoint, we'll be using JavaScript's Fetch API, which allows for fetching resources across the network. We will be passing Fetch two parameters. The first is the resource URL, which in our case is the "Fetch random joke" end point. The second parameter is an options object but we will set the HTTP method to GET since this is a GET request. Next we will set the "Accept" header to application JSON type to ensure our data comes back in JSON format. Calling "Fetch" returns a promise that resolves to a response object. In our "then" method, we'll use the response objects built in JSON method to parse our response data. Since the JSON method also returns a promise, we can just chain on another "then" method. This time the callback will be passed to parsed JSON data when the promise resolves just to make sure things are working with log the results out. Now, let's go ahead and open the console up. Now, when I click on the joke button, you can see that the response from the dad joke API is being logged out. Since we're using the "Random joke" endpoint, we get a different joke back if I click the button again. Now that our joke button is working, that wraps up this lesson. Next up, we'll be looking at a key React feature component state. 6. Build an App: Component State: In this lesson, I'm going to do a deeper dive on component's state. But before we jump in, let's go over where we left off. To recap, we have a joke button that fetches a joke every time it's clicked, and we currently logged the joke to the console. Next I want to update our apps so that the fetch joke is rendered to the screen. In order to do that though, we'll need somewhere to store the fetch joke value. In a non-react setup, you might think to store the value in a variable like so. When a new joke is fetched, I'm going to update the value of the joke variable with the fetched joke. If you call, the joke EPI returns an object. That includes ID, joke, and status. Instead of storing the entire joke object, I'm just going to store the joke itself. I'm also going to log out the value of that joke variable, just to make sure it's updating. Lastly, I'm going to add a rapid development around our button, and insert a paragraph element, and then set the contents of that paragraph to the value of our joke variable. Let's give this a try and see what happens. We can see that our joke variable is being logged in the console every time the button is clicked, but we're not seeing anything update on screen. How come? The reason we're not seeing any updates on screen is that React doesn't know anything has actually changed. A React component will only re-render when it's prompts or state have changed. In the case of our app component, a render occurs each time the app function is run by React. I'm going to add a console that logged to our app function. That way we can see something logged every time the component renders. As you can see, render is logged out for the components initial render. But notice each time I click the joke button, render is not logged out, even though the joke variable is updating. This is because changes to our joke variable are not being tracked by React, and therefore aren't triggering a new render. It looks like if we want app to app render joke changes, we'll need to store joke in either the component state or props. Which one do we use in this case? Props are passed into a component and are read-only, meaning they can't be changed, where a state holds information about the component, and can be updated. With that being said, state looks like the right choice as we will be needing to update the joke value as we go. Since functional components don't have access to state, the first thing we'll need to do is convert app to a class component. Start by changing the app function to a class that extends from React to a component. Then convert joke into a class property. Next, we'll need to convert the onTellJoke function to be a class method instead. Then lastly, instead of returning elements directly, we use the render method and return the elements from there instead. Now that onTellJoke and joke are both class variables, we need to add this in front of them. It looks like we're rendering without errors, but let's try the joke button again. Looks like we broke something, and Code Sandbox is nice enough to tell us exactly what the error is, cannot set property joke of undefined. It turns out we caused this error when we change the onTellJoke function to a class method. This is because class methods aren't bound by default. Since onTellJoke is called from a click handler, the reference to this is no longer scope to the component, but to the button, hence we get an error when trying to set a value on this thought joke. While there are multiple solutions to this issue, I choose to explicitly bind each method of the component in the constructor. Our component doesn't have a constructor right now, so let's start by adding one. Since all components extend from Reacts component class, you need to ensure that you call the super method before doing anything else. I'm going to take this onTellJoke method, and reassign it to a version of that method that is bound to the component. Now even though onTellJoke is called from a click handler, this will reference the component and not the button. With that fixed in place, let's go ahead and try a joke button again. Awesome, no more errors. Now that our component has been successfully updated to a class component, we're ready to start using local state. Let's start by removing our old joke variable as it's no longer needed. Instead, we're going to create a new joke variable in our component state. When adding state values to your component, I'll usually start by adding default values. This is done in the constructor by directly adding values to the component state property. I'm going to go ahead and add joke to our state and set it to null to start with. It's important to remember that this is the only time you should be modifying the component state directly. For state updates, you always need to use the components set state method, which we will now use to update our state every time a new joke is fetched. Let's start by removing our reference to our old joke variable. Let's replace that with a call to this.set state. Next we will pass set state an object with our updated state values. In our case, that will be passing in our new fetched joke value. When using set state, it's better to think of it as a request, rather than an immediate command to update the component. This is because set state does not always immediately update the component, it may batch or defer the update until later. This makes reading this.state right after calling sets data potential pitfall. To avoid these pitfalls with set state, I recommend reading Reacts official docs on the subject, as it's a critical piece when dealing with more advanced React topics. Now that we've hooked up our set state calls, let's update our paragraph element to use the joke state instead of our old joke value. Let's see if that made a difference. Nice. Now I'm going to click the joke button, our new joke is being rendered to the screen. You'll also notice that renders now being logged every time I click the button. This is expected as each button click is triggering a state update, and each state update triggers a re-render. That wraps up the introduction to component's state. Next up we'll take a look at another important React feature, life cycle methods. 7. Build an App: Lifecycle Methods: In this lesson I'm going to introduce reacts life-cycle methods. But before I do that, let's do a quick recap. In the previous lesson, we hooked up our button to display a new joke every time it was clicked. But when our app first loads, our screen looks pretty bare. So I'm going to go ahead and add some new functionality that will fetch and display a joke right when the app loads up. So how do we do that? For this, we'll be leveraging reacts component life-cycle methods. These methods provide hooks that allow you to execute your own logic when a corresponding event occurs in a component. In our case, we want to fetch a joke before app component is rendered to the screen. This is a perfect use case for reacts componentDidMount( ) life-cycle method. If we have a look at the official react docs, it says, "ComponentDidMount( ) is invoked immediately after a component is mounted." It also says, "If you need to load data from a remote endpoint, this is a good place to instantiate the network request." Which is exactly what we were doing with our fetchJoke. So I think this is a perfect fit for our scenario. So let's go ahead and add a componentDidMount( ) method to our app component. Now we could just copy the contents of our on tell joke click handler into component did mount like so and that would work fine, but it creates a whole lot of duplication which isn't great. Instead, I'm going to copy this logic into a new method and call it fetchJoke. Now I'll update both component did mount and on tell Joke to both call fetchJoke instead. With that change, a joke is fetch when the app component mounts, which means if I refresh the page, I should see a joke rendered on the page right away and there we go. When making asynchronous network requests, like our fetchJoke call, it's good to think about what a user sees while that request is loading. My connection is pretty fast so the time between me clicking the joke button and actually seeing a joke is pretty quick. But what about those users on a slower connection? What will they see? I'm going to go ahead and simulate a slower connection using Chrome DevTools. With a slow connection, you can see that the user has to wait quite a while before they see a joke appear on screen and during that time, the user has no indication of what's going on. That doesn't make for a great user experience so let's go ahead and improve that. First thing I'm going to do is create a new state variable named isFetchingJoke. This will be used to track when a joke is being fetched and by default, I'm going to go ahead and set to defaults. The job will start fetching as soon as fetchJoke is called. So I'm going to add a set state called right at the start of this method and set is fetching joke to true. A joke is considered done fetching when we receive a response from the joke API, which happens right here. Since we're already calling set state here, I'm just going to go ahead and as isFetchingJoke and set it to false. To test that it's working as expected, I'm going to render the value of isFetchingJoke to screen. Since isFetchingJoke is a boolean value, I'm going to call two string on it, which will allow us to render the value to screen. Now when I click the button, you can see that the isFetching value is set to true and once the joke is returned, isFetching goes back to false. The first improvement I want to make is to have a joke button disabled while joke is being fetched. This will prevent the user from clicking on the button again if we're already fetching a joke. To get this working, we will use the buttons disabled attribute and set the value equal to our new isFetchingJoke state value. Now whenever a joke is being fetched, the button will automatically be disabled and then re-enabled when fetching is complete. Another improvement we can make is adding better messaging for the user while a joke is being fetched. Currently we still just show the old joke. Instead, I'd like to show a loading joke message for the user. To accomplish this, we're going to use a conditional expression. The condition will be if it's fetching joke is true, then render the loading message and if it's false, render the joke like we're doing now. Let's go ahead and remove our isFetchingJoke label as we don't need it anymore. Let's go ahead and test it out. Now we can see that whenever a joke is being fetched, we get this nice loading message that lets us know that something is happening, this is a big improvement for those users that may be using a slower connection. Next up we'll be looking at the enhancing what we've done here by adding a form for a search. 8. Build an App: Add Search Form: Now that we have the app working well for one joke, let's take this a step further. Instead of just fetching one random joke, let's give the user the ability to search for jokes. The first thing we're going to need is a form. Let's go ahead and create an empty form to start with. Next, we need somewhere for our user to input their search. Let's go ahead and add a text input with some placeholder text. Lastly, I'll create a button labeled search, where the user submit our form. Now that we have a form setup or tell me a joke button is looking lonely, floating around out there. For now, I'm going to just move it inside our form as we will be using it later on. What's next? When working on a new feature, I find it very useful to break it down into smaller tasks. Let's go ahead and create a task list to track the work we're going to be doing for the search form. First up is calling the search joke endpoint and storing the results, then saving the user's search query, then triggering the search on form submit. After that, rendering the search results to screen, then hooking up the I'm feeling funny button, followed by creating the search forum component. Then refactoring and code cleanup, and last but not least, add styles to the app. Now that we have a plan, let's get started with task number 1, calling the search endpoint and storing the search results. The first thing we need is where to search for dad jokes. Lucky for us, the same API we were using to fetch a random dad joke also provides a search endpoint. According to the API docs, we need to pass the search term as a query prem and we'll be returned the search results in the following format. I'm going to go ahead and copy the URL for the search end point. Instead of creating a new method for search, I'm going to repurpose the existing fetch joke method. I'll start by changing the fetch URL the user's search endpoint I copied over from the docs. Then let's remove the setState call for now and just replace it with a call I will log the search results to the console. Next, I want to rename the fetchJoke method to something more appropriate. Since fetchJoke is currently referenced to multiple places, I'm going to make use of code sandboxes change all occurrences option. This allows me to quickly replace all references of fetchJoke with the new name search jokes instead. Lastly, I'm going to do some clean up and remove some unused items that are no longer being used. Okay, let's take a look at the console and see what's happening. You can actually see that we already have a response logged to the console. This is because searched jokes are being called on the componentDidMount lifecycle method, just like fetchJoke was. You may be thinking, how are we getting search results if we haven't actually entered as search yet? If we pop over to the API docks again, you can see that if no search term are valid, it'll default to returning all jokes. This is what is happening in our case. Another thing you may notice is that a search response contains more than just search results. In our case, we're really only interested in the array of jokes contained in the results property. For that reason, I'm going to simplify things and store the results directly in a variable called jokes. Now we can see that we're just getting an array of jokes back, which will be easier to work with. Now that we know our search calls working, we need somewhere to store the results. For that, let's create a new state variable named jokes and set it equal to an empty array to start with. Next, let's replace our console.log call with a call to setState and pass in the jokes returned from the search. While we're at it, let's go ahead and add back our isFetchingJoke state and set it to false. Also just like before, we should set isFetchingJoke to true when searchJokes is first called. Lastly, let's remove the old joke state variable since it's no longer being used. Just to double check that our search results are being stored, I'm going to go ahead and render them to screen. Since jokes is an array, we would usually iterate over the data to render it. For now I'm just going to call toString on it so we can see something come up on screen. There we go, our search results are showing, albeit they're looking pretty ugly right now. Don't worry though, will be giving those search results in a much needed TLC soon enough. For now though, I think that takes care of task number 1. I'm going to go ahead and cross that out. See, isn't that satisfying? We're making some progress here. Next up is saving the user's search into state. 9. Build an App: Save Search Query: Let's get started on task number 2 on our list, saving the user's search in the app state. Before we dive in, I want to talk a little bit more about forms and react. Forms are a unique case as native HTML form elements already managed their own internal state. For example, if I type into our search input, the value's being stored in the inputs internal state. This raises the question, where should form state be managed in a React app, in the react state, or in the inputs internal state. In our case, we're going to be managing our search input value using react state. The first thing we'll need is a place to store the search inputs value. For that, I'll create a new state variable called search term, and set it equal to an empty string by default. Next, we need a way to have a search term stay in sync with what the users actually typing in the search input. For this, we can use the inputs on Change event, which like the name says, fires every time the input's value changes. For quick test, I'm just going to add an event handler function that logs of inputs value. The event handler is passed in an event object, which is very similar to a native JavaScript event. But it's actually what React calls synthetic events. This synthetic event is a wrapper around the browser's native event. That follows the same interface, but works identically across all browsers. With that being said, we can access the input's value at event.target.value. Now if you open the console, and start typing, we can see that the inputs value's being logged out as we type. Next, let's remove the test function, and create a proper on change handler method named on search change. In this method, we'll take the event.target.value and set our search terms state equal to it. Then update the search inputs on Change event to call our new on search change handler. Let's go ahead and try to type something into our search. It looks like our change is creating an error that occurs when you type into the search input. If you recall, we've seen this same error before, and it is occurring because we forgot to bind the on search handler to the component. This is the same thing we had to do for the onTellJoke handler before. Let's go ahead and fix that up. Perfect. Now typing into the search input works without error. Just to ensure that a search term's being saved, let's temporarily render it to the screen. Now we can see that as we type, the search term value is being updated as well, with the search term successfully being saved in app state, we can consider this task complete. Next up is triggering in a search to happen on form submit. 10. Build an App: Submit Serach: Next up on our task list is task number 3, having a surge triggered when the search form submits. Up to this point is search form doesn't do a whole lot. If we click Search, it looks like nothing happens. Let's go ahead and change that. First thing we need to do is capture when the form is being submitted. To do this, we'll use the forms onSubmit event, which we will need to pass our event handler function to. I'm going to create a new method named onSearchSubmit that takes the form event as a parameter and for now just log out but the form is being submitted. Also don't forget to bind this new handler to the component or we will receive the same error we did last time we forgot to bind our handler method. Next, let's add our new handler to the onSubmit event for this search form, let's see what happens now if we click the Search to submit the form. That's strange, we're not seeing anything show up in our console. It turns out the form is doing exactly what it's supposed to do. It's submitting the form as a GET request to the page, which then causes the page to refresh and the constant to be cleared. Which is why we're not seeing our form submit message being logged out. In our case though, we want to manage the form submission and react. The first thing we will need to do is prevent the form from being submitted. Prevent form submission will use the event object pass to the handler and call the preventDefult method on it. Once in place, it'll prevent the forms default behavior on submit from happening. Now when we submit the form, we can see that forms submit is being logged to the console as expected, and the page is no longer being refreshed. Things are looking good but our form still isn't searching for anything. Let's go ahead and replace the console.log and instead call our search Jokes method. Before we go ahead and test this out, I'm going to remove our search jokes call from componentDidMount as it's no longer required. Now let's go ahead and submit the form and see what happens. It looks like the form is working, but we're still just rendering the stringified version of the jokes array, which unfortunately doesn't provide much detail on the search results. For now I'm going to log the jokes returned from the search to the console. This way we can get a better idea of what's actually happening. I'm going to go ahead and submit two different joke searches. One for cats and another for dogs. Let's have a closer look at the first joke returned from our cat search. "I'm tired of following my dreams. I'm just going to ask them where they're going and meet up with them later". Now that was a choice to add joke, it doesn't have too much to do with cats. Let's take a look at the first joke in our dog search. Not only does this joke have nothing to do with dogs, it's the same exact joke we actually got it the cat search as well. This is actually expected behavior as we're still not sending the search string to the API. Therefore, all search calls will return the same default joke lists, which is what's happening with our cats and dogs search right now. Let's refresh our memory and have a look at the dogs again. If you recall, the search endpoint requires that you pass the search term as a query string parameter, which they demonstrate here. In our case we were replacing the hipster value with a search term state value. Now at that refresher, let's update the search jokes call to use this query string parameter. First I'm going to update the URL to use JavaScript's template literal syntax. This way I can easily embed our search term value as an expression. Now that we have that in place, let's try doing some searches again, like before, I'll start by searching for cat jokes and then try a second search for dog jokes. We can already see that each search returned a different number of results, which is already a good sign that something has changed. Now let's have a closer look at the first joke from the cat search, "Put the cat out, I didn't realize it was on fire." That's sounding more on topic than last time. Now let's check out the first dog joke. "Why did the cowboy have a wiener dog? Somebody told him to get a long little doggy". Feel free to take a moment to compose yourself after that gem. Let's continue. Things are really starting to come together. We now have a functioning form that successfully submits the search on submission, which actually takes care of task number 3 on our list. Next, I will give the render search results a much needed attention. 11. Build an App: Render Search Results: Next up on a task list is test Number 4. Rendering in the search result. As it stands the search results aren't too much to look at. We can't even see the actual jokes from the search, which isn't very useful. In order to properly display a list of results in JSX, you'll need to iterate through the collection. The first thing I'm going to do is change the p tag into an unordered list tag. This makes more sense since we're rendering out a list of jokes. Since jokes is an array, I can call map on it. For each item in the array, I'm going to return an LI element. Inside each LI element, I'm going to insert the actual joke copy. If you recall, a joke object contains a unique identifier under the ID prop and then the actual joke itself under the job prop. In our case, we want to just render out the joke. Okay, now let's try a search and see what happens. Nice. It looks like our jokes are finally rendering. But I can see that our console log has a warning in it. React will usually give you a warning in the console if you're doing something that doesn't like. In this case, it's telling us each child in an array or iterator should have a unique key prop and even provides a link for more information if we like. It looks like React is telling us that we need to give each LI element a unique key prop. This key attribute allows React to identify individual items in the collection. This becomes especially important when sorting large collections as it allows react to only render the items that change instead of re-rendering the entire list. The important part is that each key value be unique. In our case, we can use the ID prop from our joke data. Now that each joke in the list has unique key prop our react warning has disappeared. You may have noticed that we are still setting that is fetching joke state from before, but we're no longer using it. Just like before, I'd like to show a message while search results are being fetched, we can use the same approach as last time and use a conditional expression. If it's fetching joke is true, display the message searching for jokes. If fetching joke is false, render the search results. Below this will work. I'm not a huge fan of this formatting as I find it harder to read. Sometimes you may find it helpful to break rendering up into smaller functions. I'm going to go ahead and do just that with a joke list element. I'll start by creating a new method named render jokes and have that return the unordered list of the jokes instead. Now I can update my conditional expression to use rendered jokes in place of that joke list element, which I think makes the condition much easier to read. To test the loading message, I'll throw it on my connection speed again using Chrome's DevTools. Now when we submit the search, we get a much more user-friendly searching for jokes message while the jokes are being fetched. That takes care of rendering search results on our task list. Next step is hooking up to, I'm feeling funny button. 12. Build an App: I'm Feeling Funny: We're making some great progress in our task lists and we're ready to move on to hooking up the "I'm feeling funny" button. This button is a play on Google's, "I'm Feeling Lucky" button, which when clicked, brings the user to the first search result by default. In our case, will be returning a "Single Dad" joke. For the "I'm feeling funny" button, we're going to re-purpose the existing "Tell Me a Joke" button. Let's just start by relabeling the button with "I'm Feeling Funny". Right now if we click on the button and it calls the "On Tell Joke" method, which then calls the " Search Jokes Method". Since we don't have a search term entered right now, it just returns the default joke list. So how did we get the," I'm Feeling Lucky Button" to only return one single joke. If we pop back over to the API docs for the dad jokes, you'll see that the search endpoint has other query string parameters available. Looking here we can see that there is a limit parameter which allows to control the amount of results returned. Currently we are using the default which will return 20 jokes. Let us go ahead and add limit to our search call and set it equal to one. When I click the "I'm Feeling Funny" button, we are only getting the one joke back. Since we hard coded the limit of one, our search form only returns one joke as well, which is not what we want. We need a way to pass search jokes the limit we want to use. To do that, I'm going to add a limit parameter and give it a default value of 20. This way of Novoalign parameters passed, we still have a valid value we can use. Now let's update the search URL to use the new limit parameter as a value. Let us give our search another try. It looks like the search is working again, but we're back to square one with our "I'm Feeling Lucky" button as it's still returning multiple jokes. To fix this we'll need to update the buttons on-Click handler. First I'm going to remove the "On Tell Joke" method. Since all this method does is call the "Search Jokes" method. We can remove it and just call "Search Jokes" directly instead. Now that I've removed "On Tell Joke", I also need to remove the binding method in the constructor as well. Now we can go ahead and add the "Search Jokes Call" to the on-click handler. In this case though, I want to pass in a value of one for the limit parameter. If I want to pass search jokes parameter, I can't have the on-Click event call it directly. Instead, I'll use a [inaudible] function, which will allow me to call search jokes with parameters and in this case a limit of one. Now when I click on the "I'm Feeling Lucky" button, I get back a seal joke as expected. If I submit a search, I'm still seeing multiple jokes return as expected as well. I think we are good to cross the "I'm Feeling Funny" button off our task list. Next step: Creating the search form components. 13. Build an App: SearchForm Component: Next step on a task list is creating a SearchForm component. Thus far we've been doing all our work in a single component or app component. In React, you don't want a single component to be handling too much. Just like functions, if you find a component is doing too much, it's usually a good sign that it can be broken into smaller components. Having a look at our app component, I think the SearchForm is a good candidate to be broken out into its own component. Let's start by creating a new file named SearchForm.js. For now, SearchForm will be a bare-bones functional component that returns an each one containing a greeting. This will be a temporary placeholder until we move the form element over from the app component. It's also a common pattern that the default export from a component file be the component itself, which is the pattern we'll be following. In order to use the SearchForm component from app, you'll need to import it like you would any other file. Next, let's add the component to the render method and see if it works. Nice. We can see our component rendering, but it's not doing a whole lot. Let's copy the SearchForm from app into the SearchForm component file. Once we save this, we can expect to see some errors, as we're still referencing methods endstate from the app component. The first error we get is 'can't read property onSearchSubmit of undefined'. This is because the onSearchSubmit method is referencing our app component. So how do we access methods in the app component from the SearchForm component? This is where props are your friend. Let's go back to the app component, and let's add a new prop to this SearchForm named onFormSubmit. What we'll do is pass apps reference to onSearchSubmit as the value of the onFormSubmit prop. Now back in SearchForm, instead of calling onSearchSubmit, we will access the components props, and use the new onFormSubmit prop instead, which don't forget is a reference to the onSearchSubmit method in app. Now when we save, we can see that the onSearchSubmit error's gone, but now we have a new error, but this time it's complaining about onSearchChange being undefined. So on the form, I'm going to start by adding a new prop to SearchForm named onSearchValueChange and then pass apps reference to onSearchChange as a value. Then back in our SearchForm, we'll call the onSearchValueChange prop in place of the onSearchChange. Now when we save, the error's replaced with a new one complaining that the state is undefined. By now you should know the routine. I'll add a new prop to SearchForm named isSearching and pass the isFetchingJoke state as the value. While I'm at it, I'm going to pluralize the name to isFetchingJokes since we're now fetching more than one joke. Back in SearchForm, replace the reference to isFetchingJoke with isSearching prop. Also I just noticed we only disable the I'm Feeling Lucky button when isSearching is true. We should be doing the same for the search button. So let's add the disable prop there as well. Well, it looks like that we've fixed the errors, but I can see that we're still referencing searchJokes from the I'm Feeling Funny button. If I go ahead and click on it, you can see that we get an error, 'cannot read property searchJokes of undefined'. This appears to be our last leftover reference to the app component, so let's go ahead and fix it. I'm going to start by copying the existing onClick value as we'll be needing it. Then I'm going to add yet another prop to SearchForm named onSingleSearchClick and for the value, I'll paste in the onClick value I copied over before. Now in SearchForm, we'll use the onSingleSearchClick prop instead. Now, let's save and see how things are looking. It looks like the I'm Feeling Funny button is working again, and the search is looking good as well. Now that everything's working again, let's have a closer look at the new component we created. The render method looks much more tidy now with all the formal logic hidden away in the SearchForm component. Another added bonus is the SearchForm component doesn't handle any data on its own anymore. All references to data are handled through props and callbacks. You may have noticed that I've been keeping my prop names as generic as possible. Not having your prop names tightly coupled to your data, makes your component more versatile. This way, if we end up changing our app to search something other than jokes, we don't have to rename any of our SearchForm props. This type of a component is sometimes referred to as a dumb or presentational component in the React Community. These components don't care what type of data is being passed to them, they are only concerned with how they look. This creates a good point that it's much easier to reuse. For example, if we wanted our SearchForm to search for something other than jokes, we just have to update the props and callbacks to pass in different data instead of creating a whole another component. With the SearchForm component complete, we can cross that one off our list. Next up we'll be doing some Refactoring and Clean Up. 14. Build an App: Refactoring and Clean Up: We're almost at the end of our task list, and now it's time to do some refactoring and clean up. You can think of refactoring as housekeeping for your code. It's always good to take a step back after developing a new feature to see if there's any areas you can make improvements on. That being said, I'd like to make a few improvements related to the new search form component. If we look at both the on search change and the on search Submit methods, they both expected an event object as a parameter. Now that we're passing these methods as props to search form, we can't always guarantee that they'll be called with an event. I'm going to start with the on search Submit method, and remove the event parameter as well as the [inaudible] event dot prevent default. Now on search Submit isn't really doing anything besides calling search jokes, that allows us to simplify things a bit. Now, the passing on form, submit the on search Submit method. I'm just going to go ahead and pass the search jokes method directly. Which actually means we can get rid of on search Submit and the binding logic in the constructor with it as well. Also, don't forget to bind search jokes to the component. Now that it's being called as an event handler. In removing the on search Submit handler, we also removed the preventDefault method that was stopping our form from Submitting. If we Submit a search now you can see that the page is refreshing again. Because the forms default Submit behaviors no longer being prevented. To fix this issue we'll now be handling the form submit event from the search form component. First thing I need to do is add an explicit return in the component. This way I can create a new function named onsubmit, which I will then use for the onsubmit event handler now. This means the onsubmit function will receive the event object as its first parameter and now that we have access to the event object, we can prevent form submission using the same method as before and after that we just make a call to the on form Submit prop. Now when we Submit a search, the form is working as expected again. But having search form handle its own form events, we further decoupled it from the app component. Next, let's improve the on search change method as well, and remove the event object parameter. Instead, it will make things more generic and change the parameter except the value directly instead of pulling it from the event object. Now from the search form is to the passing the event, will pull the value from event and pass that along instead. This again helps with decoupling the search form component from the app component and the more decoupled component is, the easier it is to reuse. Just to demonstrate I'm gonna duplicate our search form, but in this form I'm only going to return to jokes when a search is submitted. This change becomes very easy by modifying a single prop and the second form component. Now when I search with the first form, it works the same as before. When I search with the second form, I only get the two search results back. Both forms are using the same component, but can work independently of each other. This may not be the most practical example, but hopefully it demonstrates the benefits of using DOM components. Before we finish up, I want to do a couple of quick housekeeping items. First is get rid of the search term label we were using for testing earlier as we no longer need it. Last thing I'm going to remove the console dot log we were using the test the joke results earlier. Now let me tidy it up a bit. We're ready to move on to our final task; styling the app. 15. Build an App: Adding Styles: At this point, I'm pretty happy with how the app is working, but not so happy with how it looks, which leads nicely into our next subject; styling your React app. When it comes to adding styles in React, there are many different flavors to choose from. There's plain old CSS, CSS pre-processors like Sass, CSS modules, CSS in-jazz and many more I could touch upon. The point is there is no right or wrong solution, as it really comes down to what works for you. My recommendation to you is experiment on your own and see which of these, if any, you actually like to work with. In the meantime, I'm just going to go over some basics of using plain old CSS in React. You may have already noticed that our app contains the styles dot css already. This was created by CodeSandbox back when we first started. If we open up that file, you'll see that we have a single class name dot App with some basic CSS rules. We're not using this class right now, but we can easily add it to an element by adding the class name prop and setting the value equal to the class name. It's really, it's no different than how you would do it with plain HTML and CSS. The big difference here is that you're using the class name prop, instead of the class attribute. Now when we save, you can see that our app is using these new styles as our form is now centered on the page. One other thing you may have noticed is the import styles dot css line at the top of our app component. How was our JS file importing a CSS file? You may ask. If you're unfamiliar with bundlers like webpack, this might look really strange. In our case, we're lucky as webpack was already set up for us by CodeSandbox and webpack could honestly be a whole course on its own. For now, just know that once a CSS file is imported, those styles will be added to your app and are ready for use. Also, you're not restricted to keeping all your CSS in a single file. In fact, it's common for each component to have its own dedicated CSS file. With that being said, let's go ahead and create a CSS file for our search form component. I'm going to go ahead and name this file search form dot CSS. It's also common practice for a component and its CSS file to have the same name. Not only does this help with organization in the file system, it also makes it very clear that these styles belong to this component. Now that we have our CSS file ready, I'm going to go ahead and pull in some pre-baked styles I created earlier. Next, let's import the search form CSS into our component file and save. If we have a closer look at search form CSS, you can see that there is a search form class inside. I'm going to go ahead and add this class to the root element in the search form component. Also, I'm going to go ahead and wrap our buttons in a div as it's just needed to make some of these styles work. Now when I save, you can see things are really starting to look better now that we have some styling. It's important to be careful when splitting CSS into separate files that you're not overrunning styles in another file. Even though the search form css file is only being imported in the component, that component is also being imported in an app. An app also has its own CSS file. To demonstrate, I'm going to go ahead and add a background color to the body element in the search form dot CSS. Then I'll add a background color to the body and app CSS as well and save. As you can see, the body styles and app dot css have overwritten the body styles and search form dot CSS. So you have to be careful when working with multiple files. If you have a chance, I recommend taking a look at CSS modules as you don't have to worry about overriding other CSS classes as it keeps your components styles local to the component you're working in. Okay, to finish up, I'm going to cheat a bit and bring in the rest of the finished styles for the App component as well. Last but not least, our beautiful dad jokes edition Google logo. There you have it, your own fully functional dad jokes search engine built-in react. With the styling complete, that takes care of the last item on our task list. Next step we're going to do some high-level exploration on how React works behind the scenes. 16. How it Works: Rendering into the DOM: When working in React, it's easy to forget that your app is still running in a plain old HTML file. In our case, that is the index HTML file that CodeSandbox created for us. If I opened it up and take a look, you can see that there's not much HTML markup in the body other than this empty div. Just to prove that this is indeed the page we're rendering in the browser, I'm going to go ahead and add a custom message and save. Once I save, you can see that this copy now appears along with the hello CodeSandbox message, which is coming from our app component. But we don't see the copy for the app component anywhere in this HTML file, so how is it that we're seeing it in the browser? The reason is that React is handling the rendering of the app component for us and it manages creating the DOM elements that display in the browser. But how did the elements created by React end up in the index HTML file? React requires that you provide it with a DOM node to render into, which is referred to as the root DOM node. In our case, the root DOM node is this empty div I pointed out earlier, which conveniently enough has an ID attribute equal to root. If we go back to the Add Component, you'd see we are making a call to ReactDOM dot render. ReactDOM package is separate from React and contains methods that are surprise, specific to dealing with the DOM. If you ever end up working with server-side rendering, you'll find yourself working with ReactDOM as well. Now back to our app component when ReactDOM dot render is called its past the component and a reference to the root DOM element. ReactDOM render is usually only called once, and once it's called everything inside the root DOM element is now managed by React. Just to demonstrate, I'm going to pop open my Chrome DevTools and inspect the DOM. Here I can see the root div element that was empty in our HTML file. But now instead of being empty, it contains the elements being managed by React. Now that we know how React renders into the DOM, let's take a closer look at how it handles rendering updates. 17. How it Works: Rendering Updates: More than likely if you ask someone why use React, they might say because it is fast. While React is super efficient when it comes to rendering updates, it doesn't really help answer why is it fast? I wanted to dig into a few key features that contribute to React speed. Number one, React only updates what is necessary. You see in React when a component is updated, it ensures that it only applies what has changed to the DOM. To demonstrate, I have a simple example that consists of component named button. Every time the button is clicked, it increments a click count state by one and renders that count as part of the buttons label. I am going to pop this example open in a stand-alone browser window so I can better access my Chrome dev tools. In the elements tab I will select the button and expanded so we can see all the contents of the element. Now when I click the button, you can see the number value is changing and it is also being highlighted each time it changes. This highlight represents what is being changed in the DOM. We can see that our button and click count label remain untouched. Only the number value is being updated. Now I have the same example in plain JavaScript with no React this time. I am going to select the element in dev tools and expanded just like before. By this time when I click the button, you can see that the entire buttons are being highlighted this time. This means that instead of just the number being changed, the entire button is being changed in the DOM. While this example is simple, imagine how important these efficient updates become when your app grows in size and has hundreds of elements. Another thing React will do is batch state updates together. If you trigger multiple state changes from a reactive vent handler or life cycle method, it will batch all those changes into a single render. For example, let us pretend I have a simple React app that consists of a search input component, a results list component, and a pagination component. When I click on the search button, it triggers a state change in all three components. Now given 3-state changes, you would usually expect to see three renders. But that is not the case here. It is actually just the single render. This is because all three state updates were batched together and React is able to do this because all three updates originated from the same click event. Now on to the last item on the list, React elements are just plain objects. Unlike browser DOM elements, React elements are objects and cheap to create. In fact, your entire React app is represented by an object that is held in memory. For example, let's take this simple HTML markup for a login form with a username and password input. In React, this would be represented by an object that looks something like this. You can see that each of the HTML elements are represented by an object and react. Then when a change occurs, react will compare the previous version of the object to the updated version of the object and make updates based on the differences and then render the changes back to the DOM. Well, this was a very simplified breakdown of React's complex virtual DOM concept. I hope it gave you some insight into what happens under the hood in React. These are just some of the great features that contribute to react speed and efficiency. While these items may not be required learning for React, I believe having at least a high level understanding of these features will help you become a better React developer. 18. Conclusion: Wrapping Up: Before I wrap things up on the class, I wanted to give you a quick overview of the class project. The project involves taking a [inaudible] search joke app we just created, and adding some enhancements to it. Those enhancements are outlined in the project description, but I'll also walk you through them now. Each of these new features will build upon what you've already learned throughout the course. The first item on the list is move search results list into it's own component file. Next, you'll need to add a "no results found" message when a search returns no jokes. After that, add a button that clears the search results and search term. Then add a select input that allows user to select the amount of jokes returned from the search. Finally, if you're up for a challenge, update search input so results update as you type. In order to add your work, you will need to make a copy of my CodeSandbox first by forking it. To fork my project, just open the CodeSandbox project URL included in the class description, and click the fourth button in the main navigation. Ignore the warning as I'm only getting this as I'm the owner of the project being forked. Now I have a copy of the project that I can rename and make any changes I like independent of the original version. If you have any questions or feedback, please feel free to reach out to me on Twitter or leave a comment here in the class. If you enjoyed the class, please feel free to spread the word and share the link. Also look me up on media. If you have a chance as I also post regularly, they're about React. Thanks again for joining me and I hope to see you in future classes as well.