React.js: Getting Started
-
Introduction
Hello, and welcome to the React.js: Getting Started course from Pluralsight. Don't judge this course by its publishing date, I've re-recorded the entire course to work with the latest version of React. All the examples we do in this course should work on any React version greater than 15. In this course, we're covering the basics of the React.js library. No previous React.js experience is required to take this course. We will be starting from level 0 here, but you do need some JavaScript experience first. You need to know how to define and use variables, classes and functions, loops and conditionals. If you're not comfortable with the basics of JavaScript, you need to do that first. There are so many great resources on Pluralsight to learn JavaScript. Pick a modern course and learn the modern JavaScript features, especially classes, as we'll be using them in this course. I created a few interactive labs that you can try as well. Those labs cover some important concepts in JavaScript like function scopes and closures. I highly recommend that you walk through those first. We're not going to be using super advanced JavaScript; a basic knowledge of the language will be enough for you to survive this course. Each clip of this course will have a GitHub gist for the code we write in that clip. I'll include a short link for these gists on each clip, this way you can skip clips if you need to and sync with the next clip's gist. If you're coding along and your code did not work as expected, you can simply compare your code to the code gist for that clip. Before we begin, I want to let you know that this course is going to be mostly about how to get started with React and not about why you should pick React. I'm assuming that you've already made that excellent choice and you're here to learn React. If you have not made that choice, I've written a lot of articles that can help you with that on edgecoders.com. For the rest of this first introductory clip, I'll give a brief introduction about React and its design concepts. If you've done your research about React already, you can skip the rest of this clip. React's official definition states that it's a JavaScript library for building user interfaces. It's important to understand the two different parts of this definition. React is a JavaScript library, it's not a framework. The terms library and a framework mean different things in different contexts, but what I want you to remember here is that React is small and it's not a complete solution. We'll often need to use more libraries with React to form any solution. React does not assume anything about the other parts in any full solution, it focuses on just one thing and on doing that thing very well. The thing that React does really well is the second part of the definition, building user interfaces. A user interface is anything we put in front of users to have them interact with a machine. User interfaces are everywhere, from the simple buttons on a microwave to the dashboard of a space shuttle. If the device we're trying to interface can understand JavaScript, we can use React to describe a user interface for it. Since web browsers understand JavaScript, we can use React to describe web user interfaces. I like to use the word describe here because that's what we basically do with React, we just tell it what we want and React, with its ReactDOM library for browsers, will build the actual user interfaces on our behalf in the web browser. Without React or similar libraries, we would need to manually build user interfaces with native web APIs and JavaScript. When you hear the statement that React is declarative, this is exactly what it means. We describe user interfaces with React and tell it what we want, not how to do it. React will take care of the how and translate our declarative descriptions, which we write in the React language to actual user interfaces in the browser. React shares this simple declarative power with HTML itself, but with React we get to be declarative for HTML interfaces that represent dynamic data, not just static data. Some people like to think about React as the V in MVC. MVC stands for Model View Controller, and React is the library to take control of just the view. This is an okay analogy, but honestly I am not a fan of MVC anymore, and React with its ecosystem, especially GraphQL, is challenging the entire MVC model. If you're interested in learning more about that, I have a few Pluralsight courses about GraphQL, but you should probably take this course first. React has three main design concepts that drive its popularity. The first one is its components. In React, we describe user interfaces using components. You can think of components as simple functions in any programming language. We call functions with some input and they give us some output. We can reuse functions as needed and compose bigger functions from smaller ones. Components are exactly the same; we call their input properties and state, and a component output is a description of a user interface, which is similar to HTML for browsers. We can reuse a single component in multiple user interfaces, and components can contain other components. Unlike pure functions, however, a full React component can have a private state that hold data that may change over time. The second concept is React's nature of reactive updates. React's name is the simple explanation for this concept. When the state of a component, the input, changes, the user interface it represents, the output, changes as well. Remember, it's just like a function. This change in the description of the user interface needs to be reflected in the device we're working with. In the browser, we need to regenerate the HTML views in the Document Object Model. With React, we do not need to worry about how to reflect these changes, or even manage when to take changes to the browser; React will simply react to the state changes and automatically update the DOM when needed. The third concept is the virtual representation of views in-memory. With React, we write HTML using JavaScript. We rely on the power of JavaScript to generate HTML that depends on some data rather than enhancing HTML to make it work with that data. Enhancing HTML is what other JavaScript frameworks usually do, for example, Angular extends HTML with features like loops, conditionals, and others. When we receive just the data from the server, in the background with AJAX, we need something more than HTML to work with that data. It's either using an enhanced HTML or using the power of JavaScript itself to generate the HTML. Both approaches have advantages and disadvantages, and React embraces the latter one, with the argument that the advantages are stronger than the disadvantages. In fact, there is one major advantage that can make the case for this approach by itself. Using JavaScript to render HTML allows React to have a virtual representation of HTML in-memory, which is known as the virtual DOM. React uses this concept to render an HTML tree virtually first, and then every time a state changes and we have a new HTML tree that needs to be written back to the browser's DOM, instead of writing the whole tree, React will only write the difference between the new tree and the previous tree, since React has both trees in-memory. This process is known as tree reconciliation, and I think it's the best thing that has happened in web development since AJAX. But enough theory, let's write some code.
-
Your First Component
The most important concept to understand in React is the component. A React component can be one of two types, and we're going to use both of these types in this course. A component can either be a function component or a class component. Sometimes you'll hear different terms to describe these two types, like stateless and stateful for example. Function components are also often associated with the presentational concept. I'll simply refer to them in this course as function components and class components. A function component is the simplest form of a React component. It's a simple function with a simple contract. It receives an object of properties, we call them props in React, and it returns what looks like HTML, but it's really a special JavaScript syntax called JSX. We'll talk about JSX in a little bit. A class component is a more featured way to define a React component. It also acts like a function that receives props, but that function also considers a private internal state as additional input that controls the returned JSX. This private internal state is what gives React its reactive nature. When the state of a class component changes, React will automatically re-render that component. State and props have one important difference; the state can be changed while the props are all fixed values. Class components can only change their internal state, not their properties. This is a core idea to understand in React, and we're going to see examples of that. Let's look at an actual example of a component, a simple one, without any input and with just a simple h1 in a div output. On the left side, the component is written in the special JSX syntax. JSX allows us to describe our DOM in a syntax very close to the DOMs we're used to. It is, however, optional. React can be used without JSX, as you can see on the right side. In fact, React just compiles the JSX you see on the left to the pure JavaScript you see on the right, and then just works with the compiled JavaScript in the browser. What you see here is a JavaScript representation of the DOM, which React efficiently translates into DOM operations that it performs in the browser. Let's write a React component. I will be using jsComplete's REPL for this example. This REPL is a playground tool where we can test our JavaScript and React code right here in the browser; there is no need to install or configure anything. Go to jscomplete.com/repl, and you'll see this simple two-tab interface. The left tab is the editor; we write our JavaScript here. The latest version of React and ReactDOM are both already preloaded here, and this editor understands the JSX extension and all of the modern features in JavaScript so we'll get to focus on the React API itself, rather than wasting time configuring and compiling. The right tab is the preview tab. We have a predefined mountNode element, so when you execute your JavaScript code, anything you put in the mountNode element shows up in the preview tab. This tab will also show any error you encounter when you execute the code. This playground is also a simple JavaScript REPL where you can test quick JavaScript functions and expressions. To execute the code at any time, press Ctrl+Enter. To create a React component, simply define a new function. Let's make that function return an actual HTML button. What we returned here looks like HTML, but remember that it's not, it's going to be compiled into JavaScript. The actual JavaScript that the browser sees when we use this simple button element in JSX is this line, a call to the React.createElement function with a button argument and its child text node. You can actually use React this way without JSX, but that would make it a lot harder to code and maintain, so let's stick with JSX. This function here is a complete and very simple React component, let's use it. To use a component, we need to give it a name that we can reference, so let's put it in a constant and call it Button. Once we assign that reference, we don't want to change it, and that's why we use a constant statement here. The syntax to mount a component in the browser is ReactDOM.render, which takes in two arguments. The first is the component to render, which is just Button in our case, and the second argument is the element in which this component should be rendered. In this REPL's environment we can use the special mountNode variable. Note how I used a title case for the Button constant here. This is a requirement in React so that the library can distinguish between React elements like this one and regular HTML elements like this one. A React function component receives one argument, props. This argument allows us to make the component more reusable. For example, instead of hardcoding the label of the button, we can pass this component a label attribute, just like we do with regular HTML elements, then we can access this attribute inside the component with a curly bracket for props.label. So props is an object that holds all the values that were passed when the component was rendered. I like to use arrow functions where I can, so let me do that here. I don't need to pass any props yet to this component, so let me undo this to keep things simple for now. We have a button element, and it's rendered through a React component. Let's now add some interactivity to this so-far boring example. Let's make that button increment a counter on every click and display the value of that counter as the button label itself. So the label of this button is going to be a number, like 5, and when I click the button it will change to 6, 7, 8, and so on. Since this is something that needs to be changed in the component, it belongs to the state of the component. We basically need the component to re-render itself every time the counter changes. We can't use a property here because component properties are immutable. However, our button component is currently a function one and function components cannot have state, so we need to upgrade this component to a class component first. This is very simple. We first define a class that extends React.Component, and in that class we define a render function. This render function is what returns the component's JSX, the HTML button in our case. We don't need this previous function definition anymore. Now the button element is being rendered with a class component and we can start using a private state on that component. To use a state object we first need to initialize it. We do that inside the constructor of the Button class. We just define a constructor function, which receives a props object, and we'll call the super method here to honor the inheritance of this component. After that, we initialize this.state to whatever we want. The properties of this state object are the various elements of the state. For our case, we need a counter state, which should start from 0. Inside the render function, since we can write any JavaScript expression within curly brackets, we can read the value of the counter element that we just initialized on the state using this.state.counter. The this keyword refers to the component instance we're handing off to ReactDOM here. We can see here how the button got rendered with the value of 0. You can try and change that state to see how the button will render the value you put on the state. There is another shorter syntax to define this initial state, which is to simply to use a class property like this without a constructor call. This is not yet part of the official JavaScript language, but it looks like it's going to be. The syntax works in here because the jsComplete REPL playground uses Babel to transpile it to normal JavaScript that the browser will understand. Since this feature is likely to become part of JavaScript soon, I am going to use it throughout this course. When you configure your own React applications, you'll have to use something like Babel JS anyway to compile JSX into JavaScript, so it's a big easy win to also include and use the JavaScript features that are well on their way to become an official part of the language. We have a state object and we have a button that displays that state. Now we need to change that state when we click the button, so we need to define a click handler on that button. React comes with normalized events that are easy to use. For this case, we need the onClick event, which we define right here on the Button element. Unlike DOM event handlers which use a string, React event handlers use an actual JavaScript function. This function can be a global one like this or an inline function like this, but the standard practice is to define this function on the class component itself. I'll call it handleClick, and we can define it on the component as an instance property. We're again using the modern class property syntax here, which allows us to use arrow functions that are bound to the component instance. HandleClick will now act as a prototype function on this class, and inside handleClick the keyword, this, refers to the component instance that we're sending to the DOM. HandleClick's job is easy, read the current counter out of the state. We can do that using this.state.counter, and then increment this value, and then update the component state with the new incremented value. We can use React's built-in setState method, which is available on every class component instance, to update a component state. Our button will now increment its label on every click. Let's review. We defined an event handler for the click method, and every time you click this button the handleClick function will be executed. The function reads the current state of the counter value, increments it, and then sets the state to the new value of the counter. React takes care of all the rendering needed after these changes so you don't have to worry about that. Simple and powerful. There is one tiny improvement that we should make on this component before moving on. React's setState method is an asynchronous one, which just schedules an update, and multiple setState calls might potentially be batched for performance. Since we're both reading and writing to the state object in this example, theoretically we might hit a race condition. The general rule of thumb here is whenever you need to update the state using a value from the current state, use the other contract of the setState method, which is a function instead of an object. This argument function receives a prevState object that we can confidently use without worrying about race conditions. This function returns the object that we want to use to set the state. You only need to use this second syntax of setState if your update depends on the current state. Since we're just returning an object here, we can use the arrow function short syntax for that, but we'll need to wrap the object in parenthesis, like this.
-
Reusable Components
So far we've only seen one component, so let's add more. Let's split our one component into two, the button to be just the incrementer, and let's add a new Result component, which is going to just display the value of the counter. Since this Result component is a presentational one with no state of its own, we can safely use the function component syntax here. Define a function named Result, and just put a placeholder div in there. Notice that this new Result component did not show up because I have not included it in the rendered element yet, I just defined it. Let's include it. Let's create a new App component to include all the other components. I'll use the class component syntax for this one because in a minute we're going to need to introduce a state object in this new App component. We'll make the App component render a simple div, and inside that div we'll render both the Button component and the Result component. The parent div here is not actually optional. A React component can only return one element, you can't return multiple adjacent elements, so when you need to return sibling elements you have to wrap them in one parent element. Now we can change our ReactDOM.render call to render the new App component instead of the Button component. Execute and we now have both the button and the Result component showing up in the DOM. Since we no longer need the button to display the counter value, let's just change its label to +1. When we click that button now, we want the Result text here to change. But we have a problem. The counter is currently a state element on the Button component, and we need to access it in the Result component, which is a sibling of the Button component, so this is not going to work. The state of a component can only be accessed by that component itself and no one else. So to make this counter state accessible to both components, we need to move it one level up and put it on the parent of both sibling components, which is in our case the App component. We just move this state class property line down to the App component. The logic for this handleClick function will need to change. We'll come back to that in a minute, let's just comment it out for now. I used Cmd+/ here to toggle commenting out these lines together. In the App component, since the counter state is now here, we now need a function on this level to handle updating the state. We'll call it incrementCounter. The logic for this counter is actually the exact same logic we had before in the Button's handleClick function. It'll update the state to increment the counter value using the previous counter value. The onClick handler on the button element now has to change. We want it to execute the new incrementCounter function on the App component, but a component can only access its own functions, props, and state. So, to make the Button component able to invoke this incrementCounter function, we can pass a reference for incrementCounter to the Button component. We can name this reference anything. I'll name it onClickFunction and pass it this.incrementCounter, which again is just a reference to that function. Now inside the Button component we can use this reference directly in the onClick value. It will be a prop on this component, so we access it with this.props.onClickFunction. This onClickFunction property allows the button to actually invoke the App component's incrementCounter function. This is powerful. Basically, when we click the button, the Button component reaches out to the App component and says, Hey parent, go ahead and invoke that incrementCounter function for me. So the syntax here to access properties of a class component is this.props, remember that. Our Result component needs to read the counter value, and we can use the same trick to pass in that counter value from the App component as a property to the Result component. Again, we can use any name here. I'll just simply use the same name, so counter is this.state.counter in the App component. Within the Result component we can just read the property as props.counter where props is the only argument this Result function component receives. Note how the syntax for accessing properties on a class component is different than that for accessing them in a function component, but the idea is the same. We can test now. The Button component invokes a function to update the App state, which in turn is read by the Result component. Note that from the point of view of the Result component, the counter is not a state, it's just a value that the App component is passing to it. The Result component will always print that. The state changes on the App component level. The place where you define the state is an important question to answer when designing your React components. Components are all about reusability, right? So let's make this Button reusable by changing it so that it can increment with any value, not just one. So we'll use a new Button component to create a +1 button, a +5 button, and so on. We need to upgrade this +1 here to something dynamic. It will be a property of the component, the amount to increment. Let's name this amount incrementValue and pass it a numeric value. In the Button component we can use that property as usual with this.props.incrementValue. So now we can reuse this component with different increment values. Let's do a +5, 10, and 100. The UI rendered four buttons now with different labels, but they're not going to work yet. In fact, all of them would still increment with a +1 at this point because we did not change their handlers. This incrementCounter function will now need to receive an incrementValue, and instead of using +1 here it will use that value. Every button component needs to invoke the onClickFunction with an argument now. We can simply use an inline function here and invoke the onClickFunction inside of that with the component's incrementValue. So we wrapped the action that we want to happen with a function for the handler, and through the magic of closures, this will work just fine. Now the buttons will increment the counter with their respective values. Another alternative here is to use the bind function and create new functions that remember their arguments. While these solutions work fine, they are not the recommended way to handle this situation. The reason being, when we use the bind method or the function wrapper method, we're actually creating a new function for every rendered button, and that can be avoided. Instead, we'll resurrect this handleClick function. The onClick handler will just call that, this way it's not creating a new function every time. Inside handleClick we'll replace what we had before with the same call to this.props.onClickFunction with the argument of this.props.incrementValue. Although this is a bit more code, it's actually easier to understand and it's more efficient. In fact, in this simple example, the need to invoke a function handler that uses a prop on the component is enough reason to use the class component syntax over the function component syntax. This is why I kept the Button component on the class syntax, although it does not have a state object on its own.
-
Summary
This module was a basic introduction to React. We used the jsComplete web-based REPL playground to build a small example project on React. This playground has React support built in, which allowed us to focus on React components instead of configuring and tuning a React environment. A React app is a set of reusable components, which are just like functions, they take input and produce a description of a user interface in the form of a React element. The ReactDOM library enabled us to render those React elements in the browser efficiently. The input for a component is a set of properties you can access inside the component with the props object, and also a set of state elements that can be accessed with the state object. The latter is only available in class-based components. A component state can be changed inside that component using React's setState method, and every time a component changes its state, React re-renders it. The props of a component cannot be changed by the component, but the whole component can be re-rendered with different props by the component's parent. With React and ReactDOM, we describe our application's HTML in JavaScript, and that produces virtual DOM nodes. Writing HTML in JavaScript is a lot different than what we are used to, but luckily React has a way to write the virtual DOM in a syntax very close to the HTML syntax we're used to. This special React syntax is called JSX. Once we have the virtual DOM described in JSX, we can pre-transform it before shipping it to the browser. React has two types of components, function and class components. Function components can be defined with simple functions that receives a props object and return a React element. Class components can be defined by extending the React.Component class and defining a render function inside of that. The render function returns a React element. The syntax to mount a React component in a browser is ReactDOM.render, and that takes two arguments, the component to render and an HTML element to hold the React-rendered markup. React also comes with normalized events that work across all browsers in a standard way. We've seen the onClick event handler, and there are other onSomething events, like onChange, onSubmit, and many others. In the next module, we're going to work with data using React. We will read that data from the GitHub public API. Here's a preview of the final product. It's a simple GitHub cards app where you have a form to add a card using a GitHub username. The card will show the avatar and the name of that user, and you can add multiple cards like this. The users I am testing with here on the screen are the top committers to the React project. Our thanks goes to them and to the rest of the React Team.
-
Working with Data
Introduction
We've seen simple components, we've worked with multiple components, and we've seen how and when to use state and properties of components. However, we haven't really worked with any real data yet, so we're going to be doing exactly that in this module, and we'll use the GitHub public REST API for it. We're going to build a simple GitHub card component that displays information about a GitHub user. Once we have a card component, we're going to explore how to render multiple cards for different users and have that driven from an array of objects. Then we'll add a simple input form to add a new GitHub card to the UI for a username that we read from the input using the GitHub API. Before we begin, if you don't have the react-devtools extension installed already, find it now and add it to your browser. It adds a tab in your Dev Tools console, which gives us a screen that is super helpful. It allows us to inspect and interact with the React application. You can actually go to any site that uses React and see how they structure their components here with this tool. For example, the netflix.com app can be inspected here, you can see their components and you can see every component's props and state, you can even change the state of any component directly here. For example, let's see what would happen if we change this showDisclosure state element to true. React re-rendered this component and showed a component that was not previously part of the app. Familiarize yourself with this react-devtools extension. It's going to be your best friend when you start writing React applications.
-
Build a Github Card Component
The first decision you need to make in a React application is the component structure. You need to decide how many components to use and what each component should describe. This is often easy if you have the full picture of the application you're building, but practically you don't. I usually start with what makes sense for me at the beginning and keep an open mind about it while making progress. I rename components a lot, and sometimes I remove them if I find no reason for them to stick around. There isn't a right or wrong answer here, but there are good and better answers, and you'll only get better with experience. So just start with something and see where that takes you. Our application will eventually be a list of GitHub cards. That's your first clue that you need a component to represent a single card and another component to represent the list itself. You can start coding either one of these components. I like to start from the bottom up or from the deepest point in the tree, but it's sometimes useful to start with a top-to-bottom approach, especially when you know most of the components in the tree. One other piece of advice. If you know that the component will not have any interactivity with the user, for example the users are not going to be clicking on that component, and if that component is not a top-level component where a state needs to be managed, start that component as a function component. Function components have a lot more advantages over class components, so you should always use them when you can. Only use class-based components when you need to manage a state object, which is usually done on a top-level component, and also when you need personalized event handlers. The card component should be a presentational one. We don't have any events on that card component, so we'll use a function component here. Card is a function that takes the standard props argument and returns a div, which will be the box that displays both the avatar and the name of the user. So we need an image element, and we'll put the person's name and their company's name in divs. Let's add some placeholder data here to test this component. We can use placehold.it for the image. Another quick piece of advice: try to test your code as often as you can. Don't write a lot of code and test it all at once. Instead, test the smallest increment that you can find. For example, what we wrote so far is totally testable by itself. Before we make this official, test that the placeholder data shows up as you want it. To see a component in the preview area here, we need to mount it with the ReactDOM.render method, the first argument is the Card component, and we need to render to the mountNode element. The placeholder data shows up as expected. This concludes the test of what we've written so far. Now we can go and find the next increment to code and test, or test and code, which is actually a lot better in most cases, but of course you need to write code to test your code in that case. To keep things simple here, we'll focus on the actual code and we'll just keep testing things manually. Let's actually style the Card component before we move on. I'll tell you about a few options for styling a React component. We can include a CSS file as normal and use the element class to select and style any element. For example, let's give this information div a class of info. In React, we need to use the className property instead of just class. This is to match the JavaScript DOM API for elements. Then we can use a normal CSS file to style that element. But React is all about writing HTML in JavaScript, so you also have the option of writing CSS in JavaScript as well. This method was popularized by the React Native project, and it solves many of the problems of CSS today. Let me show you how to do that. Let's scratch the global CSS sheet. I don't need a className here anymore, instead I'll include a style property. Now this is a special React property, just like events. Instead of passing it a string, we pass it a JavaScript object. In that object, we can specify any styles we want this element to have using the JavaScript API for styles. I'll use a display inline property, and I'll also give it a marginLeft of 10. Similarly, let's make the style of the div here a little bit bigger and bolder, and let's give every card a margin of 1 em. And now we can test. There is a lot of debate around this method. It feels like using inline styles, and some people call it that, but it's quite different. The difference being this is JavaScript, not strings, so we can generate it and reuse it, and we have the complete power of JavaScript to do that. But this method also has its disadvantages. I personally use a mix of JavaScript styles and global styles in my projects. Since we want to render multiple cards, we need another component to hold those different cards. We'll name this component CardList. We can potentially manage the state of this application in this CardList component, but until we make that decision, we'll start this component as a function component and change it if and when we need to. CardList is a function taking the standard props argument and returning a div that will hold our list of cards, so we use the card component in there, and change the render method to render the CardList component instead, and make sure what we did so far is working without any problems. Let's put some real data in our card component. We'll grab some actual data from the GitHub API directly. If we go to api.github.com/users and provide any GitHub username there, we will see their data as a JSON object. Go ahead and copy the avatar_url and the name, and use them in the Card component. The default size for the GitHub avatar is too big, let's force a width of 75 on it. Looks good. So now that we have an idea of how a card is going to look like, let's make it reusable. If we want to render multiple cards right now, they will all render Paul because we hardcoded those values in the card component. So instead of doing that, we'll make the card component receive the data through its props, like this: avatar_url, name, and company with the actual value and in the Card component. We'll then just use props.avatar_url, props.name, and props.company to display those values. This makes the Card component reusable, which means we can now render different cards by passing different props to the component. However, we're still hardcoding the values in the CardList component. We're going to have multiple cards and we want the CardList component to render all of them dynamically. To accomplish that, we'll assume that we have the data in an array, we'll make that array hold an object to represent the data for each card, and I'll prepare this array with actual values from the API. Now we want the CardList component to render a card for each object in this array. We start by passing this data array to the CardList component as a prop. Inside the CardList component, we can access the card's data with props.cards, and what we need here is to map this array of objects into an array of Card components. React will join the array of Card components automatically and render them all. Every card component renders data through its props, so we want to pass the data as we have it from the original array object. Basically, we want to do something like name=card.name and do the same for all the card properties. React supports a simple operator to do exactly that for us; it's called the spread operator. We basically spread the card object with these three dots. This makes all the properties of the card object available as props in the Card component, and our data will render exactly as we want it. Now that we have a tested data array to work with, we can build the form that will trigger us to append to it actual data from the GitHub API. This is what we'll do in the next clip.
-
Taking Input from the User
To take input from the user, we can use a simple HTML form with an input and a button. Let's create a new React component, a class-based one this time, as we're going to eventually manage the forms state in there. It renders a simple HTML form, text input for the GitHub username to add to the list, and a Submit button to trigger the add. To make this component show up in the browser, we need to include it somewhere in the ReactDOM render function, which currently renders the cardList component. For example, we can include the Form component inside the CardList component itself, and that will make it show up, but it would be completely wrong to do that. Just because it works does not make it right. The Form component is not part of a card list. Every component has its own separate responsibility. The CardList component renders a list of cards and the Form component renders an input form, so we should not mix them together. Instead, let's create a parent component to render them both as siblings, we'll call it App. The App component is going to be a class-based one because it will handle the connection between the CardList and the Form components. It will, for now, just render a div that includes both the CardList component and the Form component. And we need to change the ReactDOM render method to render App instead of CardList. Testing that, both components now show up and we did not mix their logic. The data array is still a global variable here, which is bad. It should be part of the app itself to make the whole app here reusable. Otherwise, multiple instances of the App component will use the same global data, and that would be wrong. We can either maintain the data on an instance property of the App component or use React's internal state object. Since we want React to re-render our App component every time a new record is added to this data array, we need to put it on the state of a component. This way, all we need to do is add a record to that array and React will reflect that in the UI. The question now is, which component should hold that state? So far, the data array is only used by the CardList component, so we can put the data array on the state object of the CardList component. But remember that the Form component will need to append a record to this data array, and it can't do so if the owner of the array is its sibling, the CardList component. To allow both the CardList and the Form component to access the data array, we should put it on the state of the App component itself. Let me name this cards instead of data because that reads much better. Now we can give the CardList component access to this new cards array using this.state.cards. Test and make sure we did not break anything yet. All good. Let's now implement the logic of the Form component. We want to read the value of the text box every time we click on that button. Simple. We can define an onClick event on that button, but I prefer to handle this with an onSubmit event on the form element itself. This way I can utilize native form submission features, like for example I can make the input required here and the onSubmit event will honor that in modern browsers. Let's define an instance function on the Form component to handle the submission, call it handleSubmit and use it on submit. Every React event function receives an event argument. This event object is a wrapper around the native JavaScript event object. All the methods available on the native event object are available here. For example, since we want to take over the HTML submit logic, we should prevent the default form submission behavior here using event.preventDefault. This is actually very important here as without it the form will refresh the page and you might lose the code that you have here, but this editor will actually remember the last code that you executed. Let's test what we did so far with a console.log line here and make sure that shows up when we submit the form. Next increment, we want to be able to read what the user types. We can simply read that value using the DOM API. We can give the input an id attribute and use a getElementById to read its value. React has a special property called ref that we can use to get a reference to this element. This property takes in a function that will be executed when the input element is mounted to the DOM. That function receives a reference to the element, so we can store that reference on the Form component instance, just like this. Now, in the handleSubmit function we can access the value that the user typed using this.userNameInput.value. Let's test that. Type something and when you click Add Card button it should log it. React has another method to work with the input elements, which is to control their values directly through React itself rather than reading it from the DOM. This method is often labeled as controlled components and it has more advantages over the simple ref property. However, it does require a little bit more code, but it's simple. Let me remove the ref attribute here as we're not going to need it. Instead, we introduce a state element on this component for the input value of the userName field. Then we use this state element as the value of the input element. This immediately creates a controlled element. To complete this controlled element we define an onChange event for it to customize its now controlled behavior. I'll just use an inline function for the handler here, the function receives the event object, and we'll make it modify this component's state to change the userName value to match the value coming from the event's target element that we're working with. This way when we type into the text box, React will be aware of this element state change and it will reflect that change back to the element itself because it's a regular React state change. You can see this more clearly if you open up the React Dev Tools tab. You'll see two applications here because this REPL playground is itself a React app, but the other App is the one we're coding here. If you navigate to the Form component inside App, you'll see its state object here. When you type something in the userName input, React is now aware of that change and we can read it directly from the state element. Next increment. We want to fetch this card information from GitHub. To read what the user types, inside handleSubmit we can simply use this.state.userName now. We can use the native fetch method here, but this editor also includes the axios library, which is an excellent one for AJAX requests. In the handleSubmit function, we'll do axios.get, the GitHub URL for a single user using the value of the userName that we now control on the state. Axios gives us a promise that we can now use to see the response coming from GitHub. Let's just console.log it. Test with an actual GitHub username here, and we're getting back an object, this is the axios response itself. And on that object there is a data property that has the GitHub data for profile here. All we need to do now is to append this data object that we get from the API to the cards array that we have on the App state. If we do that, React will re-render the App component and show the new card. However, the form component can't access the card's state element directly here. React components have a one-way flow of data. Components can't change the state of its parent, but the App component can pass properties to the Form component. Those properties can be simple primitive values or function references. So if the App component passes a function reference to the Form component, we can change the state of the App component in that function and the Form component will be able to invoke that function because it will be part of its props object. That's usually what we do in scenarios like this. Let's define a function on the App component and call it addNewCard. This function receives the card information and it will do something with it. For now, console.log it. Then pass the Form component a new prop and call it onSubmit with its value set to the addNewCard reference. Note how this is just a reference to the function, we're not invoking the function here. This reference becomes a prop inside the Form component. We can access it with this.props.onSubmit. This prop now holds a function, so we can invoke it and we'll pass the data that we get from the GitHub API as an argument here. If we test now, the Form component will fetch the card information and it will invoke the function reference that we passed to it, which in turn executes the console log line in the App component. Now all we need to do is make this addNewCard function use this cardInfo object and append it to the state's cards array. We simply use the function contract of setState, which gives as access to the previous state, and we can simply return the previous state with the new cardInfo object concatenated to it. I think we can test. We start with our fake data, then we add a new user using their username here. Let's add Jordan, and now we have an official card for Jordan using his profile information directly from GitHub. We can now remove the fake data that we've been using so far and test one more time. Add Paul, Ben, and Jordan. All good. By the way, Jordan is the original brain behind the React library. React was his crazy idea and he was the one who convinced Facebook engineers to take it seriously, which they did. One small improvement to make here is to reset the username input after a successful add. Since we're controlling this input with a component state element, all we have to do is to set the state back to an empty string. Test that, the input resets now after the card is added. One other small improvement. You might have noticed that we have a warning in the console about not having a key field here when we dynamically render a Card component from an array of cards. Whenever you do so, you should provide a unique attribute as the key prop. React uses that to identify the child correctly in its algorithms instead of relying on its position in the list. Since we're fetching the entire data of a user profile from Github, we also get the Github id of that user, and that is unique. So we can use it here as the key for every card. Now if you test, you'll see that the warning about keys is gone.
-
Summary
We've built a few simple React components in this module, a Card component to render information about a GitHub user, a CardList component to convert an array of records into an array of the Card components, a Form component to read input from the user, and an App component to manage the relation between all the other components. We've managed the records array as a state element on the top-level App component, which allowed us to share data between multiple components, and it allowed us to append new cards to the UI by simply appending the GitHub API response to that state element. In the Form component, we explored how to access an element in the DOM from React directly using the special ref attribute, and we explored how to read from an input element using React's state itself with the help of the onChange event. Components written with this latter method are known as controlled components. It's important that you understand the concept of passing properties from the parent to the child, and that those properties can be regular primitive values or they can be function references that the child can invoke up the chain on the parent component itself. This is what we did to pass the information in the Form component to its parent, the App component. In the next few modules, we'll build a simple game for kids. Here's a preview of it. The player gets a random number of stars here between 1 and 9 and a set of numbers from 1 to 9. They select numbers that sum up to the number of stars, and keep doing so until all the numbers are used.
-
Building the Game Interface
Introduction
Over the next few modules, we're going to build an in-browser math game for kids. I am calling it Play Nine. You can see the final product here. When the game starts, you get a random number of stars between 1 and 9, and you have the set of numbers in the bottom frame that you can use. You can select one or more numbers that would sum up to the value of the random stars. The objective is to correctly use all the numbers here in the bottom frame. If you end up with a number of stars that has no possible correct combination, you get to redraw, which you can do five times. After that, if you still end up with a number of stars that has no possible correct combination out of all the remaining numbers, then you lose the game. Let's play a few rounds. We have 3 stars here, so we can do either 3 or 2 and 1. Check the answer. We get an indication that the answer is correct, and when we can click again to accept the answer, both 1 and 2 will be marked as used, and we'll get a new number of stars. So let me show you how a wrong answer will look like; you get a wrong button here. So undo this. Let's pick 9, pick 7, 3 and 6 is 9, 5 and 4, and now we have 3 stars, but we can't select any correct answer here, so this is the case where we can redraw and see if we can get a correct answer. And with luck, we get 8 stars, so we can select the last number and win this game, and we can play again. So if I didn't have any redraws, if I had 0 redraws and I tried to win this game, that would be hard. Let's try it real quick. Seven, 8, we can do that, 1 we can do that, 9 we can do that, and 1, we can't do that anymore because we've selected 1 already, so I got Game Over! We're going to build the interface of our game in this module. It will be mostly be markup and CSS, but we're going to do those in React, and we'll also render dynamic sections with data collections instead of hardcoding values. We'll build the whole game in jsComplete's REPL here because this course is not about configuring React. Using the REPL will allow us to focus on just the React API and keep the course short. We're going to need to style the game a bit though. Since this is not a course about styling, I'm going to just use the Bootstrap framework for most of the styling. This editor has a CSS mode, which we can use to apply any CSS to our markup. We'll use that briefly to simplify our game logic. I'll also use Font Awesome for all the icons we'll need in the game. You don't need to include either Bootstrap or Font Awesome, they're both used by this editor and are available to the playground as well.
-
The Main Game Component
Let's get started right away by thinking about the top-level components that we need for this game. We render a component with ReactDOM.render, something in the playground's mountNode element. I like to always start with an App component and then create whatever components I come up with inside of that App component. This is sometimes not needed, and I do sometimes get rid of this App component eventually, but having that helps me test my actual application if I need to with more flexibility. For example, let's name the game's main component Game. This is the component that will manage all the state of a single game. Let's add a header for the game, Play Nine, use this Game component inside the App component, which is the one that will render in mountNode, test that this structure is working so far without problems. Now during the development of this game, to test that the state of the game is isolated, I might render two copies of this Game component in my App component. This is especially useful when you start working with a third-party state container like Redux for example. There is one other benefit to having a top-level container component like the App component here, and that's when we need to completely reset the game. The easiest way to do so is to un-mount and re-mount it. The App component can help us there.
-
The Stars, Button, and Answer Frames
Let's create the subcomponents for this game. The first one would be the Stars component. This is where we're going to render the random number of stars, but for now, let's just build the markup and make sure things look good. So this is going to return a div, let's put placeholder dots in there, and go include this component under the Game component. Make sure the dots got rendered. Now copy this Stars component, and let's create two other components, one for the button that's going to show up in the middle, and another for the Answer box that will be on the right side of the button. Include all components under Game, and make sure they all render. Let's render some actual data now. We can use the awesome Font Awesome to render a star icon inside the Star component, we just use an i element with classes of fa and fa-star. Let's render a few stars here. In the Button component, we'll render a simple HTML button element with a label of =. The Answer component starts empty, and then it will get numbers as the user plays. Let's keep it as is for now and change the layout of what we have so far. I am going to do some Bootstrap and CSS next, and this course is not about that, so you can skip the rest of this clip if you want and use the next GitHub gist at the beginning of the next clip. Alright, instead of wasting a lot of time on CSS, we'll simply implement the main layout using Bootstrap 4, which is already included in this playground. All we need to do to make these 3 components show up in a single row, is to put them in a div that has a class of row, then make every component's top level div a class of col, and we can define sizes here on a grid of 12 cells. So let's give the Stars component 5 cells, the Button component 2 cells, and the Answer component 5 cells, and give the Game component itself a class of container. Let's add an hr element here below the title. The last section we want to add markup to is the bottom frame that holds the list of numbers to play with. Let's create a new component for that, Numbers. This will return a div, I'll give a Bootstrap class of card to make it into a box, and let's also align the content of this div to center. This card div will have one div that holds the numbers in one line; we'll use a span element for every number. This is going to hold all the numbers between 1 and 9, eventually. Render this Numbers component under everything, test, numbers are showing up. Let me add a line break here to give it some space. This is all the markup we need so far and it does look ugly, but we're not going to spend a lot of time styling this game, but we are going to use some CSS to help us simplify the logic of the game. So I'm going to use a global style sheet for all the CSS that we need, just to keep things simple, because this is going to be a big example, and I want you to focus on the main React API itself. So we can edit the global style sheet for this application by clicking this icon. I am not a CSS expert, but let me try to quickly make this markup look a little bit better. The stars are too close to each other. We can simply style the .fa-star class to style them here in the global style sheet. Let's give them a little bit of a margin and increase their font size. The numbers are also very close to each other. To style them, I'm just going to style the span element directly here. Make the display inline-block, also some margin, align the text center, give it a background, and I'd like to make the numbers into circles instead of squares, which we can do simply by giving it a width and a 50% border radius. Looks good. One last thing here, since these spans are going to be clickable, we should make the UI indicate so. We can simply add a cursor pointer property to show the familiar pointer cursor when we hover over them. Every number will have three different states; it can be not selected, or selected for the current answer, or already used and can't be selected again. Let's define the visual differences between these three states with CSS. I'm going to give the second number a class of selected, and the third number a class of used, and style those differently. For the selected class, let's give it a lighter shade of gray, for both background and color. For the used numbers, since this is a successful pick, let's give them a shade of green. Also when you select or use a number, you cannot click it again, so I'm going to change the cursor here to indicate that. This not-allowed value makes the cursor show up like this. This is good enough for now; let me stop pretending that I know CSS and let's write some code.
-
Random Number of Stars
Let's make the Stars component render the actual number of stars as it should do in this game. For starters, let's make it render the stars from a variable. I'm going to call this variable numberOfStars and start it with a value of 5. So to render a dynamic number of stars from a variable like this, we need to iterate over that variable's value, and for every iteration, add a star. There are a few ways to do that; the simplest is to just use a for loop, from 0 to our variables value, and inside that loop, push one star to the stars array. In the component's render method, we can just use the stars array directly in curly brackets like this. When React sees this array, it will just join it and render it correctly. Even if the array is an array of values, like numbers, React will just render every number in a text node, which is pretty cool. Now that we're rendering the stars from a variable number, we can go ahead and switch the test value with an actual random number. We can use Math.random here, which gives us a random number between 0 and 1, and if we multiply that by 9, that will make it between 0 and 9, it would never really produce a 9, the max here is 8.99999, but since we should not have a 0 in the result any way, we can just add one to this expression and take the floor of the random value, which makes it between 1 and 9. We can test this value, and we see that we're getting different random numbers of stars in the UI every time we refresh.
-
The Numbers Frame
Let's now render the numbers that the player can use, which are just 1 through 9, but instead of hardcoding that, let's make that into a loop. Actually, let me show you another way of doing this. We've rendered the dynamic number of stars here using a simple loop. Nothing is wrong with that. Loops are so common and they're easy to understand. However, React embraces the declarative school of thought and it encourages using array methods like map, filter, reduce, instead of using loops. So instead of using another loop for the numbers here, let's create an arrayOfNumbers constant and make it into the array of numbers from 1 to 9. We can simply just list them here, but this playground also comes with the excellent Lodash library available as well. So in Lodash, to create an array of a sequence of numbers, we can simply use the range function. Modern JavaScript actually has many new ways to initialize an array like this, but I wanted to let you know that you can use Lodash here as well if you want to. Since we have an array of numbers, we can now declaratively map it into an array of span elements that hold them. This is shorter than the for loop version, and I think it reads much better. I actually need a range from 1 to 10 to list all 9 numbers, it's not inclusive. Let me also make sure that when we select numbers and put them into the Answer component, they'll show up with the right style there, and they do. Before we conclude this markup module, let me refactor the original for loop we used for the list of stars to also use a declarative range map, just like the numbers one. We can use a range from 0 here. And one last thing. This arrayOfNumbers constant is not something I want to create every time I render the Numbers component. Every Numbers component will use the exact same constant, and never change that. This is a good candidate to place as a property on the Numbers object itself. Every function is an object, and we can store data on that object to be used by all instances of that object. I'll call it list, and use the same range expression there, and use Numbers.list in the Numbers component. Do this whenever the variable that you're defining will be shared exactly as is with all instances of the component, and it's not related to any logic inside that component. We're done with the markup here. In the next module, we'll write the bulk of the behavior of the game, what happens when you click the number, and how you can get an answer right or wrong, and a few other features.
-
Summary
We finished most of the markup of this game in this module with the help of Bootstrap, Font Awesome, and some CSS. We explored how to render a random number of stars in the game's top-left corner. The Button component and the Answer component are both just static markup so far, and in the bottom card, we rendered the 9 numbers and made them show up in clickable circles. Now it's time to add the interactive actions of the game, which is what we will be doing next.
-
Numbers Selection
Introduction
We're going to implement the numbers selection in this module, which is the core of how a user would start playing the game. They're going to see the random numbers of stars, and select one or more numbers that would sum up to that number of stars, and they will do that by clicking on the circles in the bottom Numbers component. When a player selects a number, we want that number to show up in the Answer component, and we want it to be disabled in the Numbers component so that the player can't select it again. When they accept an answer, we will reset this Answer component. We will be managing a state of selected numbers for that, and we're going to write a function to add a number to that state, and also remove a number from that state. This state element is going to be a regular array, and resetting that is as easy as reinitializing it with an empty array.
-
The State of Selected Numbers
Let's figure out how to select a number to be considered for an answer to the current stars challenge. When we click a number we want it to be selected in the Answer component, and we want to display it as selected in the Numbers component, so we want React to trigger a re-render for both the Answer and the Numbers components. To trigger a re-render, we need to place something on the state and modify it with an action. Since both Answer and Numbers components need to be re-rendered, it makes sense to place the state elements on the parent Game component itself. We'll initialize a state object, and the first element I need on the state right now is a list of selectedNumbers. I'll just put them in an array for simplicity. I usually use objects to hold list, because they're faster for lookup, but arrays are okay as well for very small data structures. I'm going to push a number to this array whenever the player clicks on a number in the Numbers component to select that number. So I have two main different tasks from this point. Task 1 is to figure out how to push a number to this array on click, and task 2 is to figure out what the UI will do based on numbers stored in this array. Those two tasks are actually totally separate in a React application, and we can code them independently from each other. And more importantly, we can test them independently from each other. I always like to make the UI respect the data we have or will have on a state element, task 2, and I just fake the data element manually. Then, once the UI is behaving exactly like I wanted it to based on that fake data, I'll remove the fake data and implement the task to update the array with actual data based on events, task 1. I find this approach much easier to test. So I'll consider the numbers 2 and 4 fakely selected here. This state element is needed by two components; the Answer component needs to display all the selected numbers, and the Numbers component needs to disable a selected number so that the player cannot select it one more time. For a component to access a state of its parent component, the parent needs to pass the state into the child component as a property. Let's use the same name here for the property. Pass to the Answer component selectedNumbers prop that's set to the value of this.state.selectedNumbers. Inside the Answer component, we now have a prop called selectedNumbers, and it will have the value of the game's selectedNumbers array. So we map this array into an array of span elements to represent those numbers in the Answer component here, and don't forget the key prop here because this is a dynamic list of children. We can either use the number itself here as a unique key, but I like to always keep the id and the actual data element in the UI different. That's why I used the index of the element. Ideally, instead of relying on the position of the item here to identify it, we should use objects and have a unique id attribute for every object. But to keep things simple, I'll stick with a simple array here. Test now, 2 and 4 should now show up here. And just like we passed the selectedNumbers state down to the Answer component, we also need to pass it to the Numbers component. We can use the same syntax for that. Inside the Numbers component, we need to check every number while we render it. If that number is in the selectedNumbers array, we need to render it with a different class, the selected class if you remember from the previous module. We just gave this Numbers component access to the selectedNumbers array, so it can implement that logic on its own. We'll add a className attribute here, and make it compute its value with a function to which we simply pass the value of the current number. We can define this function within the Numbers component itself to keep things simple. It will receive a number and check if that number is included in the selectedNumbers array that's now part of the component's props. If so, this function will return the proper className for the case, which is simply selected. The props syntax here is just props, not this.props. If only I had a dollar every time I made this mistake. Now the UI completely respects the state of selectedNumbers. They'll show up in the Answer component, and they'll change to the disabled state that we styled for them here in the Numbers component. Now we can remove the fake data and implement the original task to click a number to select it, which is what we'll do next.
-
Selecting a Number
We're ready to click on a number to select it. Let's define a new function to handle that. I'll call it selectNumber. This needs to be on the top-level Game component as it will work with the selectedNumbers state. However, we need to invoke it from the Numbers component, so we'll need to pass it there as a prop. I'm going to assume this function is going to receive the clickedNumber as an argument. The selectNumber function implementation is straightforward; it will change the state of selectedNumbers, and add the newly-clicked numbers to that state, and we can use array and concat here to combine the new clicked number with the current array of selected numbers. And that's it. Note how I again used the function argument here for setState because my update operation depends on the previous state. To make the Numbers component able to invoke this function, we pass a reference to it as a prop. Now inside the Numbers component, we have access to this selectNumber function through the props of the component. We'll need to invoke this function every time we click on a number span element here, so we need to define an onClick handler, and what we really want to do here is call props.selectNumber, passing in the current number here, but we can't do that directly. Remember that the onClick handler needs a function reference, not a function call. The easiest way to do that for our case is to wrap what we want to do with an inline function reference like this. Arrow functions made this so much easier. This is good enough for our case here, but remember that we're creating a new function here for every number. When that becomes a problem, the right way is to upgrade this span element here into its own class-based component, and use an instance function instead. But this will do for our simple case. Test now. When we select a number, it gets correctly presented in both the Numbers component and the Answer component. But we do have a problem to solve next. We've actually introduced two problems so far. Can you identify them? First, every time we select a number, the Stars component renders a new random number of stars. Now, imagine that you're being interviewed for a React job, at this point I'd be asking you why is this happening? I'll let you think about that for a while. The second problem that we have so far is that we haven't really blocked the selection of a selected number, we can keep selecting them over and over. Let's fix those two problems right away. Quick advice first. Whenever I identify a problem like this here in the game, pause the video and try to solve it yourself, then compare your solution with mine. Your solution might actually be better than mine, ask me if you're not sure why I did something a certain way. Alright, problem number 1, the changing number of random stars. We get a new random number of stars every time we select a number because React re-renders the Game component, which re-renders all of its children. This means the Stars component also gets re-rendered every time we select a number, and every time we re-render the Stars component, the whole function is invoked, and the numberOfStars variable will get a new value. To solve this, we need to move this numberOfStars variable up one level and maintain it on the Game component itself. So we can use it here in the Stars component as a prop instead of a local variable. Now later on in the Game implementation, when we're ready for a new set of random stars, we do want React to re-render the Stars component. So we'll put the randomNumberOfStars as a value on the state of the Game component itself, initialize it with the same random expression, and pass it to the Stars component as a prop to match how we used it. This way, the number of stars will change in the UI when we update the state of the Game component with a different value of randomNumberOfStars. Test now and make sure that when you select a number, the number of stars does not change. Let's now fix problem number 2. We have many choices here, the simplest is to introduce an if statement before we mark the number as selected. If the number is already selected, then we want to do nothing and return. That's it. I broke the game though because I cannot select numbers any more. Moments like these is when you'll wish that you've written some automated tests for your components. We've introduced only one line though, so it's easy to find the typo here and see if that was the problem, and it was. Now we can select and we cannot re-select a selected number. So far so good. Let's now give the user an option to change their current answer.
-
Changing The Answer
How about we implement a way for the players to change their answer. Let's say they clicked a wrong number, or they tried a combination that's not correct and they want to fix it. So let's make them roll back a selected number, if the player clicks on the number in the Answer component. So we're going to need an onClick handler here on every number in the Answer component. This should be quick and easy because it's going to be mostly like the selectNumber function. Let's call this one unselectNumber. You can probably tell that I'm not the best at naming things, but sometimes the simplest name is the better choice. We'll make unselectNumber receive an argument of clickedNumber as well. To remove an element from an array of elements, which is the selectedNumbers state in this case, we'll need to use a setState call. The declarative way to remove an element from an array in JavaScript is to simply use the filter function. We'll filter out the number that matches the clickedNumber. It's that simple. Then, we'll pass the unselectNumber reference to the Answer component. And now back in the Answer component, our onClick handler will simply be an inline function that invokes the props.unselectNumber function using the current number value. And I think we can test now. Select a few numbers, and now we can unselect them and re-select them as we wish. The game is starting to shape up now. Before we check and accept a number, let me do a few enhancements first in the next clip.
-
Enhancing the UI
I'm going to do a little bit of refactoring here before we proceed. We're reading selectedNumbers out of the state here multiple times; I am going to reduce that into a const, along with the randomNumberOfStars property as well. Both of these can be de-structured out of the state object, and then we can use them directly in the code without repeating this.state every time. I want to do one minor enhancement to the UI before we implement the logic to win or lose the game. The check button should start as disabled and only gets enabled when we select a number. This means the Button component will need access to the selectedNumbers state, so we'll give it that. In the Button component, we can define a disabled attribute here. The value of that would be when the length of the passed in selectedNumbers array is 0, in that case the button should be disabled. And React is smart here, it will just not include the disabled property at all if the condition is false, which is what you would expect. Testing now, the button got rendered as disabled, and it gets enabled when we select a number. Let me actually give this button the Bootstrap class of btn to make it render bigger, and now the features for interacting with the numbers in the game are in good shape. What we need next is to implement a way to check and accept a correct answer, and reach a winning or losing state. This is what we'll do in the next module.
-
Summary
We've made good progress on implementing the first few functions for a player to play the game, they can now select an answer, and that would update the visuals in the UI to represent the selection and disable the selected numbers. Players can also change their answers, just by clicking on those selected numbers in the Answers component. We've also made few enhancements to the code and the UI. For example, if we don't have any selected numbers, then the Check Answer button has nothing to do, so we disabled it for that case. In the next module, we're going to finish this game. The features we need to are, to verify an answer, accept an answer, and add states to represent winning or losing the game.
-
Game State
Introduction
We're going to finish the game in this module, and to do so we need a few important features. We need a way for the users to check an answer, accept an answer, and figure out if there are no more possible solutions left. We're going to also implement a way for the player to redraw the numbers of random stars, and we'll see how many redraws we should give the players. The final component that we want to add to the game is the one that would display the done status, and give the players a way to play again.
-
Verifying an Answer
So far we've implemented a way for the player to select an answer, and they can also change the answer. Now is the time to check the answer, which is what will happen when the player clicks the equal sign button. For an answer to be correct, the sum of all selected numbers should equal the current numbers of random stars. Both of these values depend on the main state, so let's create a checkAnswer function in the Game component, and inside that function we need to do a calculation to see if the current number of stars equal the sum of all selected numbers. Now usually we do not place any values that can be calculated on the state, but to keep things simple, I'm going to store this calculated value on the state because I need it to trigger a UI change. When this calculated value is true or false, I want to indicate that in the UI. So we need a setState call. This will depend on what we have in the state, so function argument. The answerIsCorrect if the current randomNumberOfStars equal to the sum of the current selectedNumbers, which we can calculate with a simple reduce call with a sum callback, and we should start that with 0 in case the selectedNumbers array was empty. We'll need to initialize this new answerIsCorrect state with a null value, with null here meaning there is no answer being checked for correctness yet. It might be better to actually define an answerState here instead of relying on null for logic, but I'll keep that as a challenge for you to do among many other opportunities to make the code of this game more readable. Next, we need to pass both the checkAnswer function reference and the answerIsCorrect state element to the Button component as it's going to be the one who actually uses them. We'll de-structure the answerIsCorrect value from the state as we do other values here. Inside the Button component, depending on this new answerIsCorrect property, we need to render a different button. Let's create a button variable to hold the UI to render and simply use a switch statement over the answerIsCorrect prop. We have three cases here, when answerIsCorrect is true, false, and the default case, which means the button is in its normal initial state. So for the default case, we want to render the button exactly as we have it now; I'll copy this into the default case, assign it to the button variable. Now for the case when answerIsCorrect is true. This means we clicked the equal sign button and our answer was correct. Let's render a different button for that case. I'll remove the disabled attribute for now, make this a success green button, and render a proper Font Awesome icon here; let's go with a checkmark. For the false case, we'll render another button variance. We'll make it red with a btn-danger class, and render an x icon with fa-times. Finally, we need to replace the original markup in the return section to use our now dynamic button variable. To complete this feature, we need to make the equal sign button invoke the checkAnswer function that we wrote, which we passed already to this component, so we can just define an onClick handler here with a value of props.checkAnswer. We can now test. Select the 1, check answer, correct. One more time, select something wrong and check answer, all good. The next feature would be to accept an answer. We could have actually combined verify answer with accept answer, but keeping them separate allows for some more flexibility while playing this game.
-
Accepting an Answer
Now we have a way to verify an answer, however, the game goes on after that, so if we have 5 stars, select 3 and 2, check that answer, correct, we can still select other numbers. This also is a problem when we check a wrong answer. What we should do here is if the player selects a number is to reset the answerIsCorrect state, and we can do that inside the selectNumber set call. Set answerIsCorrect to null, and we should also do the same inside the unselect number function. Let's test that. Wrong answer. You can change the answer, and the answerIsCorrect state resets now, and even on a correct answer, you can still choose something else. Let's now create a way for the players to accept an answer. This will be another top-level function on the Game component, but for such a feature to exist, we need to introduce a new state, because the action for accepting an answer should mark the accepted numbers as used, and that's what eventually will determine if the game ends successfully or if the player should get a game over. The UI will need to be re-rendered when we accept an answer to show those accepted numbers as used. Let's initialize a usedNumbers array on the state. Start it with some fake data for testing the UI first. In the render method, let's read usedNumbers into a constant, and pass that constant into the Numbers component. Now inside the Numbers component, we need to change the logic of the numberClassName function. If the number is used, we want this function to return the string used instead of selected. We'll simply add another if statement for that, same logic, but before the selected if statement, if the number is included in the usedNumbers prop that we just passed to this component, then make this function return used. We can test what we have so far. Our fake test data is showing up as used now. We can reset the testing data and start the usedNumbers state with an empty array. And now we're ready to implement the acceptAnswer function. This function is going to change a few things on the state, so it will be a simple State function call. In there, we first need to change the usedNumbers array to basically add to them the currently selected numbers, which we can read from the previous state here. Once we accept a list of selected numbers, we need to reset the selected numbers back to an empty array, and we should also reset the answerIsCorrect state, and at that point, the game is ready for a new random number of stars, so let's use the same expression to give the random number of stars a new value. The Button component needs access to this new acceptAnswer function because we want to click a success button to accept an answer. So we'll pass it here as a property. And within the Button component, the success button needs an onClick handler, within which we're going to invoke the acceptAnswer function that we now have on the props of this component. And we can test now. Select an 8 and 1, check, and hit the button one more time to accept, and now both 8 and 1 are marked used.
-
Redraws
We have a way to check and accept an answer now; let's try to win this game. Select good answers and accept them, keep going, we reach a game over pretty quickly, there are no possible answers in this case for 2 stars. It's pretty hard to win this game as is, so I'm going to introduce a new feature of redrawing the number of stars. Let's add a new button under the main button, and inside that button let's use an icon of fa-refresh. Let's use a Bootstrap warning class for this button for the orange color, make it a smaller one, align those buttons center, add a couple of line breaks, and it looks good enough. So what should happen when we click this button? Let's wish there was a property on this component that we can call, with the name of redraw, and let's go up the chain and pass that property downward assigning it to a function on the Game component. And now we can define that function, all it needs is to change the state and generate a new random number of stars using the same expression, and it should also reset both the answerIsCorrect state and the selectedNumbers state if we have any. And now we can test. Select a number, hit redraw, and get a new random value for the number of stars. Alright, now that we have this feature, let's try to win this game one more time. Select all correct answers. Keep going, keep going, and we get to this state where it doesn't really have any answers, so now we can redraw, and we can keep redrawing until we hit a random number of stars that we can use, and win this game by selecting all numbers. However, the game is now too easy, you can just keep redrawing and you'll eventually win, so we need a balance between no redraws and too many redraws. I'm going to test a limit of 5 on the redraws, so let's implement that. This would be something that we need to keep on the state, let's call it redraws, and I want to display it right there on the same orange button. So let's go ahead and de-structure it out of the state in the render method, pass it down to the Button component as a property. Within the Button component, we can display this new property on the new redraw button by reading it directly from the props object. Alright, looks good, but it doesn't work yet. What we need to do to make it work is, when we click that redraw and invoke the redraw function, we need to decrement the values of redraws, straightforward. Inside the redraw function when we're updating the state, we should also update the redraws, but since this is going to depend on the previous state now, we should switch this setState call to use a function argument, and use the prevState.redraws - 1 to decrement the current value of redraws. Now we can test, and it works. It won't stop the action though, if we have 0 redraws, we should not be able to redraw any more. so let's write an if statement in the redraw function to guard against that. When the redraws are 0, do nothing. Now if we test, 0 redraws is where the button stops. This redraw button should be disabled when it reaches 0, the player shouldn't be able to click it. Implementing that is easy. In the Button component and on the redraw button element, we can add a disabled property, and make its value true when the redraws are 0, and if we test now, when we have 0 redraws we cannot click that button.
-
The Done Status
Let me refactor something that's been bothering me real quick. We've used this Math.random expression in a few places in the code, and that's a bit of duplication of logic, what we should do here is extract that expression into a function. We can actually use a class-level function here because this function does not depend on the state of the game, it will always generate a random number. So use Game.randomNumber here, and we can define this class-level function using a static property inside this class object. This is another modern feature that's coming to JavaScript classes, and we can used it here thanks to Babel JS. The function will just return the exact same expression. Test and make sure we did not break anything so far. Now it's time to implement an end to this game, which is going to be one of two things, either the game is over, or a successful run where all the numbers got selected. Let's manage this game state with a new element on the state, call it doneStatus, and for testing we'll use fake data as usual. I'll start it with a Game Over message. For displaying this doneStatus, let's create a new DoneFrame component which will need to render under everything we have so far. It'll need access to the doneStatus property, so we'll pass it here as a prop. We need to de-structure that property from the state as well. Now we can implement the DoneFrame component. It can be a function component that just returns a div. I'll put the done message in an h2 element here. Let me center the text of this frame and add a line break here to give it some space. It looks okay. So if we have a game over, or even if we have a successful run, basically if we have any doneStatus, we shouldn't really display the Numbers component, we should just display the DoneFrame, and if we don't have a doneStatus, we should not display the DoneFrame. So it's going to be one or the other based on the doneStatus value. We can implement this in a few ways, my favorite way for this case since we have two very simple components is to just use a ternary statement with the condition being the doneStatus constant itself. If that constant has any value, we want to render the DoneFrame here, otherwise, we want to render the Numbers component. Simple and powerful. Now in the UI we only see the DoneFrame, and if we reset the doneStatus to null, we're back to seeing the Numbers component.
-
Winning and Losing the Game
Now that we have a doneStatus and we're displaying it whenever it changes, we now need to update it according to the games rules. Let's create an updateDoneStatus function on the Game component. Inside this function we'll need two conditions, one for a successful run, and one for the game over case. This updateDoneStatus function will conditionally update the state based on the previous state, so we'll need a setState call with a function argument syntax, and inside the function we'll write our two conditions. The successful run condition is easy. If at any moment we have exactly nine used numbers, for this case, we'll just update the doneStatus to indicate so. If that was not true, then we should check if the game is over, and the game is over if there are no more possible solutions. Assuming we have such a function, I am going to just use it here, and for that case, we're going to update the doneStatus with the Game Over message. We should only do this check though if we don't have any redraws left, because if we do have redraws left, then it doesn't matter if we don't have any possible solutions, we can still redraw and continue. Alright, let's now implement this possibleSolutions function. This function also needs access to the information on the state, but instead of reading the state directly, since we're inside the function argument of a setState call, we should use this prevState variable to read the state properly. We'll just pass it to the possibleSolutions function. So possibleSolutions will receive a state object and use that directly. This function actually only needs two things from the state to operate, so let's just de-structure them here in the argument. We need the current random number of stars and the array of usedNumbers. We need the array of usedNumbers to calculate what numbers are left that are available for a solution; I'll put those in a possibleNumbers array, it's the original range of numbers from 1 to 9 filtered out to remove all currently used numbers. So if the number is in the usedNumbers array, we want to exclude it here. So now we have a list of possible numbers in an array, and we have the value of the random number of stars, and what we want to answer here is the following question. Does the array of possible numbers have any combination of numbers that sum up to equal the value of the stars? This is a popular array logic problem which has some obvious cases, but the general approach is to calculate all the combinations in the array, and loop over them checking their sum against the value of the stars. The exact solution to this problem is a bit involved and it's beyond the scope of this course, so I did prepare a version of it and I'm just going to use that here. This will be a general function that receives an array of numbers, and a number to compute the possible combination sums for from the first argument array of numbers. To get this function, I saved it under bit.ly/s-pcs, possible combination sum, just copy the function as is and include it in the editor. Read over this function and try to understand what it's doing, there are a few advanced techniques here, but it's always fun to decipher. So now that we have this updateDoneStatus function, we need to invoke it, and we should do that in two places, whenever we accept an answer, and whenever we update the redraws. These actions can lead to either a game over or a game success. However, we need to update the done status after we're done updating everything else in the state, because the updateDoneStatus function depends on the component state itself, but simply calling the updateDoneStatus function after this setState function call might not work correctly, because the setState call is an asynchronous call. So do not assume that multiple setState calls will do their thing in order. React gives us a second argument in the setState function, which is a callback function we can use to run any command when React is done updating the state. Doing it this way guarantees the order of the operations here. Since updateDoneStatus itself is a function, we can just pass the reference to it here directly, and we need to do that in both acceptAnswer and in redraw. Test and make sure we did not break anything, make sure you're not seeing any errors in the console, and let's try to get to a no possible solution state. Drain all the redraws first, and accept the few possible answers that you can, and there you go, 1 star has only 1 possible answer which we used already, so it's not available and we got a Game Over message. Let me try to win this game real quick. Don't redraw this time and try to play it smart, and when you have all the numbers used, you should get this Done message. When the user loses or wins this game, there is nothing we can do in the UI, so we should give the player a way to reset the whole game and play again. Let's add a button under the done status message, call it Play Again, give it some Bootstrap classes, and let's update the doneStatus property to force this button to show up so that we can see it. Let's assume that the DoneFrame component will receive a function reference as a prop that it will use in its onClick handler; I'll call this function resetGame and pass it down to the DoneFrame component from the Game component. Inside this function's definition on the Game component, what we want here is to simply reset the whole state of the game and start over, so we need to set the state to the same initial state that we used here, but to avoid duplication here, let's create a function on the class level, and make it return the initial state object. We can now use this function both for the actual initial state and for the resetGame setState call. And since this is just a one-liner function here, we can use the arrow function shorthand syntax like this. So testing that now, play the game, and when you get to either a win or lose state, the Play Again button will reset the whole game. I think we have a game in good shape now. There are a lot more features we can add, but I'll give you one challenge here to run with. Add a timeout for the game. For example, make it so that the user only has 1 minute to win the game, and it will be game over if they take longer than that. This particular challenge is important because it will make resetting the game a bit harder. I will write a follow up blog post about how I'd solve this challenge, and I'll publish the solution on edgecoders.com. I hope you've enjoyed this course. I'd love to hear feedback about what you liked and what you didn't so I can make the next course better. Have fun playing the game you just wrote with React.
-
Summary
We finished the most important features of the game in this module. We started by implementing a way for the user to check an answer, and accept it afterwards. These features allowed us at that point to officially play the game. However, it was too hard to win the game, so we introduced the feature of five redraws to balance the level of the game. The last component we wrote for this game was to display whether a user won or lost the game whenever they reached one of these states, but for that, we needed to calculate the state of no possible solutions. And we used that state to figure out the state of winning or losing the game, and we displayed that to the user. This course was an introduction to the basics of React.js; there is a lot more to learn. There are a lot of resources you can now use to continue learning React. I recommend checking the official documentation next. It has a lot of great resources, and it has gotten a lot better lately. I also recommend that you continue adding features for this game, try to at least add the play timer that I mentioned in the previous clip, but you can also think about managing points based on how fast you can win this game and how many redraws you've used. You can also get the player's information and when they win, store their points somewhere, and then create a leaderboard widget for all players. If you're planning to use React with REST APIs, you should take a look at GraphQL because I think it's a much better solution to working with data. And finally, if you're really serious about React, you should learn Node. Node is the platform you're going to be using to compile your React applications, and it's also the easiest option to render server-side HTML from the same React applications that you write for your clients. My Node.js course on Pluralsight is an advanced one, so if you're just starting with Node you should probably take a more introductory course first, but make sure that you get really comfortable with Node itself because that would make your React experience much more valuable.