Sample Project: Star-Rating (React)

This is part one of a multi-part series about React and Vue.
Part 1: React
Part 2: Vue

Lately, I’ve been learning React, a JavaScript front-end framework. As of now, it is one of the most popular JS frameworks in existence. To help myself learn the framework, I built a sample project: a Star-Rating app. It functions similarly to what you might see on Amazon, where you have a rating represented as a line of stars. You can change the parameters used to calculate the star rating via an inputs component.

To see the final result, click this link.

I think it turned out rather nicely, and today I’d like to show how I built it, and in the process explain how React does things, as best as I currently understand it.

This tutorial presumes a basic knowledge of HTML, CSS, and JS, along with some basic knowledge of how to develop using a command line interface (or CLI) and how to use Node Package Manager (or NPM). If you’re unfamiliar with any of these things, feel free to make an online search for whatever you don’t know.

Table Of Contents

Setting Up
Looking Around
Creating Our First Component
Rendering Stars with FontAwesome
Calculating How Many Stars to Render
Rendering the Stars
Adding Our New Component
Introducing a Second Component
State and React
Passing State Between Components
Wiring Everything Else Up
Better Validation
Bug Hunt
Final Touches
Conclusion

Setting Up #

To start the process, I chose to use create-react-app to handle creating the base project structure. You can get it through NPM. Using the command line:

# Install create-react-app. You only need to do this if you don't already have it installed.
npm i -g create-react-app

# Create a new React single-page app.
create-react-app react-star-rating

# Navigate to the app directory and fire up the development server.
cd react-star-rating
npm start

After performing the above instructions, a tab should automatically open up in your default browser to localhost:3000, which should be a page containing a spinning React logo and some additional text.

It should be noted that you don’t need to use create-react-app to make a React project; instead, you could just include CDN links of these two scripts in an HTML file:

<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

I find create-react-app offers conveniences that are useful, such as hot reloading (changes are updated as you save your files) and a development server (so I don’t have to set one up myself), so I usually prefer to use that. This tutorial will assume that you are also using create-react-app.

Looking Around #

Now that we have the barebones project set up, let’s open the project directory in a code editor. You can use whatever editor you wish; I personally use the free editor Visual Studio Code, but there are plenty of other excellent options, such as Atom and (if you’re willing to spend money) Sublime.

Once you have the project open in the code editor, you should see there’s a bunch of files already set up. Let’s look in the src directory and open up the file called App.js. It should look something like this:

One thing you’ll notice right away is the use of import and export statements, as well as the use of classes. These are all part of the ES6 syntax. React makes heavy use of ES6 features; if you’re not familiar with ES6, here is a quick guide of basic features. Not all ES6 features are yet included in modern browsers (as of this writing), so one of the things create-react-app provides is a “transpiler”, which automatically converts ES6 syntax to its ES5 equivalent, so browsers know how to read the code. If you want to work with React, I’d recommend becoming familiar with ES6 features and how to use them; once you know how it works, ES6 lets you do really powerful things.

I’ll occassionally point out instances where I’m writing ES6 and compare it to the ES5 version so you can see how much simpler ES6 can make things look.

So, what is the code for App.js actually doing? Let’s do a brief examination.

These import statements are including external files into our file. We can even use imports to add specific parts of a file to our app, which is what { Component } is doing. Notice that we can import more than just JavaScript; we’re also including a logo image (logo.svg) and our app’s CSS file.

Here, we are using an ES6 feature — classes — to extend the Component class, which means we are getting access to all the functionality that a Component class has. If we were to use ES5 syntax, we wouldn’t be using class syntax at all. Instead, we’d be calling a React function called createClass, like so:

Both this snippet and the one previous do exactly the same thing: create a new React Component class.

So, what is the “render” function for? Primarily, it is responsible for rendering our component’s HTML. You might have noticed the HTML elements being returned by the render function:

Strictly speaking, this isn’t actually “HTML”. It’s something called “JSX”. The React site gives a good explanation of what JSX is, but to summarize, it’s a way to write HTML markup using JavaScript. This is one of React’s biggest features: the idea that components return their own HTML to render on the page. Some people really love the idea, as it allows you to keep the logic and markup of a component in the same file, which can make it easier to reason about an app’s logic. The JSX makes it easier to understand what the component’s markup looks like. React takes care of converting JSX to rendered HTML for you.

Some say that you shouldn’t mix JavaScript and HTML/JSX together. If you belong to this camp, then you don’t have to use JSX at all. Everything you can do in React using JSX, you can also do through pure JavaScript. The following code does exactly the same thing as the previous snippet, but just using JavaScript:

As you can see, under the hood JSX is just a series of JavaScript functions and objects, which end up creating our HTML elements. Personally, I find this syntax more difficult to read, and thus harder to reason with when I’m building an app, so I like to use JSX. That’s what I’ll be using for the remainder of this tutorial.

There’s another point of interest we should look at in the JSX code: the {logo} which is being set as the image tag’s source. In JSX, if you want to use JavaScript variables, you can do so by adding that variable directly to the JSX code, enclosing it in curly brackets. This is what will allow us to show various bits of data that we calculate with JavaScript.

Finally, at the end of the file, we have one final line:

As we use import to pull stuff in from other files, we use export to send stuff to other files that want to import our code. In this case, we are exporting the App component class.

Now that we’ve analyzed the App.js file provided by the boilerplate code created with create-react-app, and in the process familiarized ourselves a little bit with how React works, let’s start actually creating our app!

Creating Our First Component #

First, let’s make a folder called “components”, which will house all of our component files. With our components folder created, let’s add a new file to it: StarRating.jsx. Note the .jsx extension. It signifies that we are using JSX in this file. You could, technically, just make it a regular .js file (and, if you noticed, the file we looked at earlier is App.js), but I prefer to use the .jsx extension because it helps me know which components are rendering HTML.

Now that we have our first component’s file, let’s write some code.

This is just adding some code to get us started. We import React and the Component class, and we export a new class, StarRating. I’m defining the class as part of the export, instead of creating the class first and then exporting it, as we saw in App.js. Either way works; I prefer to use this method. We also create a render function and return a single <div>, with a class called star-rating. Note that, instead of class, we use className. Since JavaScript itself uses the word “class”, when writing JSX we need to distinguish between an HTML class and a JavaScript class, and this is done by using className when writing an element’s classes.

You may notice that I’m not using semicolons when writing my JavaScript. In many cases, the browser takes care of adding the semicolons you need, so you don’t actually need to write them yourself. This is just a personal preference I have; you can choose to write or not write semicolons, as per your own tastes.

The other thing we’ve added is some default props, which React requires you store in an object assigned to the static property defaultProps. What are props? They are the data that gets passed in by a parent component, and the child component can then take that data and use it as it sees fit. In a bit, we’re going to modify the App component to do just that, but until then, we’re going to use these default values. It’s also good practice to include default values, as it helps show what props your component is expecting.

Here is a quick explanation of what each prop is for:

  • minRating: This controls the lowerbound value of our rating. Defaults to 0.
  • maxRating: This controls the upperbound value of our rating. Defaults to 10.
  • rating: The actual value of our rating. Defaults to 5.
  • starRatio: This controls how many rating points it takes to render a full star. Defaults to 2, which means it takes a rating of 2 to render a single star. With a default rating of 5, this will result in rendering two and a half stars.
  • limit: We’ll use this to set a limit for how large the min/max rating values can be. This is purely to improve app performance.

Rendering Stars with FontAwesome #

Next, we need to add some code that will allow us to render stars. To do this, we’re going to install a font library called FontAwesome, which has thousands of custom icons stored as scalable SVGs. This way, we can style our stars however we want. Specifically, we will be using the react-fontawesome package, which integrates FontAwesome with React.

First, we’re going to install a few libraries via NPM and save them to our package.json:

npm i --save @fortawesome/fontawesome @fortawesome/fontawesome-free-solid @fortawesome/fontawesome-free-regular @fortawesome/react-fontawesome

To summarize, we’re installing Fontawesome, two sets of Fontawesome icons, and a package which provides FontAwesome React components. Once the packages have finished installing, we need to import them into our project.

First, we need to go back to App.js and add a few lines:

The imports work just like our previous import statements. The line fontawesome.library.add(solid, regular) is a function of fontawesome which will take the function arguments and, if they are valid fontawesome icons or sets, make them globally available to all our other components (so we don’t have to individually import the icons in each component file). In this case, we’re importing all of the “solid” and “regular” icons and making them globally available. We could also, if we wish, pull individual icons from each set (like we did with React’s Component) and add each one to our library. This results in slightly improved performance; however, since this is just a simple project example, I’m going to add the entire solid and regular sets of icons to the library.

Next, let’s go back to our StarRating component and add one more import:

Here, we are importing the FontAwesomeIcon component. We will be using it to render our star icons. (Confused? Just keep reading, for now; there will be examples that should hopefully make things clearer.)

Calculating How Many Stars To Render #

If you played around with the final result at the beginning of the tutorial, you may have noticed three different types of stars: “full” stars, “half” stars, and “empty” stars. This is how I’ve chosen to represent the star ratings for this project.

Before we can render any of that, though, we need to calculate four things:

  • What is the maximum number of stars we should render?
  • How many full stars should we render?
  • How many half stars should we render?
  • How many empty stars should we render?

You may be wondering why it’s necessary to calculate a “maxStars” value. Sure, we could simply calculate the full, half, and empty star values without calculating a separate max value, but I’ve found that it makes the math simpler to just figure out the maximum number ahead of time and use it in calculations later, so that is what I’ve chosen to do.

The following is the StarRating component, with the addition of the code which will calculate the four values we want:

To briefly explain each calculation:

  • maxStars(): We take the maxRating and divide it by the starRatio, then round the answer up to the next integer.
  • fullStars(): We take the rating and divide it by the starRatio, then round the answer down to the next integer.
  • halfStars(): We take the modulus (or remainder) of rating and starRatio, get half the value of starRatio, and compare the two using a ternary operator. If `x` is greater than or equal to `i`, we return one half-star; otherwise, we return no half-stars.
  • emptyStars(): We take maxStars and subtract from it fullStars and halfStars.

ES6: When you are declaring methods inside of an object or class, you can use the shorthand notation yourFunction () {} instead of explicitly specifying yourFunction: function () {}. Also, I’m assigning the values of the props to variables within the calculation functions using ES6 destructuring, which is a convenient way to get values off of objects; in ES5, you’d have to use objectName.dataValue.

Rendering the Stars #

Now that we can calculate how many of each type of star we need to render, let’s get to actually rendering them! First, let’s render the fullStars. In our render function:

The first thing we do is assign fullStars to equal the result of our fullStars() function. Next, we create a new function within the render method, called renderFullStars(). Since we’re defining this within a class method, and not the class itself, we can’t use the shorthand notation from before; we have to explicitly assign the function.

ES6: I’m using an arrow function, but I could also have simply declared function renderFullStars() {}. Either way works; I prefer the arrow function syntax here because it looks cleaner to me.

The code in renderFullStars() may seem confusing, at first glance, especially if you’re not familiar with the various methods associated with arrays. However, once you know how it works, I think it’s actually easier to read.

First, we check to see if the number of full stars is 0. If it isn’t, then we proceed to create an empty array with the length set to the number of full stars. Using chaining, we then call fill() on the resulting array, which will fill the entire array with whatever value we want; in this case, null. Finally, we use the map() method to create an entirely new array, with exactly the same number of items, but instead of being null these items equal the result of the callback function we pass in (note I’m again using an arrow function). This final array is what gets returned. What if there are no full stars? In that case, we return an empty string; you’ll see why in a moment.

Let’s take a brief moment to examine the callback we’re passing into the map method:

Remember the FontAwesomeIcon component we imported earlier? We are now using that component as the return for our callback. We’ve given it a class of “star” (via className), which our CSS can target later. The next two things we are setting are props which we are passing to the FontAwesomeIcon component. I’ll go over each one.

The first prop we’re setting is key. This is something React uses to help keep track of lists of items, and while it is technically possible to exclude the key property from a list, it is strongly discouraged. The key should be a unique value, and to accomplish this I’m using the string “fs” plus the i argument, which is equal to the index number of the current array item. Note that, since we need to use a JavaScript expression to set the key value, we enclose it in curly brackets.

ES6: I’m using a template literal to set the value of the string, which uses the back-tick character ` instead of regular quotation marks. The ES5 equivalent is "fs" + i.

The second prop is icon. This tells the FontAwesomeIcon which icon we want to use. For full stars, we want to render star from the solid library.

At the end, we self-close the component. Generally, you can treat React components like self-closing tags, but you can also use <YourComponent></YourComponent> instead. There are cases where we’d want to use the opening/closing tag syntax, but this isn’t one of them. Generally, I prefer to use the self-closing tag format because it’s simpler to read.

Finally, in the return…

… we’ve added {renderFullStars()} inside of the <div>. Since curly brackets enclose JavaScript expressions in JSX, that means we’re running the renderFullStars function and outputting the return directly into the JSX. Take a moment and go look at the final result again. The default rating here is set to 5, which means two full stars and a half star are rendered. renderFullStars() creates two FontAwesomeIcon components and outputs them into the star-rating container, which is then rendered into HTML by React. What if there aren’t supposed to be any full stars? Then this function returns an empty string, and nothing is rendered.

Let’s go ahead and add the code to render half stars and empty stars:

As you can see, the process for rendering half stars and empty stars is the same as for full stars: pull in the calculated values, create render functions to render the components, and run the render functions in JavaScript expressions in the JSX. The only differences lie in the FontAwesomeIcon components we are returning.

For empty stars, there are two differences:

  • We’ve changed the key to use “es” instead of “fs”. This is to keep the key unique from the keys we generated for the full star components.
  • Instead of passing in a string for the icon, we’re instead passing in a JavaScript expression. This is because, by default, the FontAwesomeIcon assumes we want an icon from the solid library. Our star icon for full stars was part of the solid library, so we could just pass in the name of the icon. But for empty stars, we want to use the star icon from the regular library, so, as per the react-fontawesome documentation, we need to pass in an array. The first item in the array is the library we want to use, “far” (“FontAwesome Regular”); the second item is the name of the icon itself, “star”.

For half stars, we also use a different key prefix, “es”. But, instead of returning a single FontAwesomeIcon component, we’re returning two different icons, wrapped in a span. The reason for this is because of the way FontAwesome renders the half-star icons. Instead of rendering a single icon of a star half-full and half-empty, FontAwesome has a full half-star and an empty half-star.

To get the half-filled star effect we’re looking for, we need to take advantage of FontAwesome’s power transforms — specifically, Layering. What Layering lets you do is take two icons and combine them together so they appear as a single icon. You do this simply by adding the fa-layers class to an element containing the icons you want to combine.

So we’ll use the two half-star icons — star-half from “solid” and {['far', 'star-half']} from “regular”. We’ll also pass in flip="horizontal" to the regular half-star so it’s reversed. The end result is something looks like a half-filled star. Excellent!

At this point, we’ve finished* the StarRating component! Here’s what the code looks like:

Our component takes in a bunch of props, uses the values of those props to calculate how many star, empty star, and half star icons to render, and then returns the rendered stars in JSX. It’s time to go back to the App component and add our brand-new component!

Did you notice the asterisk on “finished”? There’s one other thing we’re going to address later, but we don’t need to worry about it right now.

Adding Our New Component #

To begin, let’s import the StarRating component and add it to our App component’s render return:

If you don’t have the local development server running yet, do so now by entering npm start into your CLI; if you do have it running, then upon save the page should automatically reload. At this point, you should see a row of black stars appear beneath the default text — two full, one half, and two empty. If you see this, congratulations, you’ve just rendered a React component! If you don’t see this, or if you get an error, try to go back over your code to make sure it matches what I’ve previously wrote; hopefully you’ll find what the problem is and fix it.

Assuming things are working, let’s go ahead and pass a prop down to StarRating:

Upon saving the file, you should see the rendered stars change. Assuming you passed in “7” like I did, you should now see three and a half stars. This is the power of props: a parent component passes the props down to the child, which then takes the props and automatically updates any data that is calculated using those props. In our case, we passed down a rating of 7, and StarRating converted that rating into full-stars, half-stars, and empty-stars and updated the rendered result automatically.

Of course, right now we can only update the rating by manually changing the value of the rating prop being passed to StarRating. That’s not very practical at all. What we want to do is have a way for us to input the values we want and have StarRating update accordingly, as seen in the final result. To do that, we’re going to need another component specifically dedicated to do just that…a RatingInputs component!

Introducing a Second Component #

Let’s start by creating a new component file in our components folder, calling it RatingInputs.jsx. Add the following code:

Just like with StarRating, we start with importing React and the Component class from the “react” package, then export a class, this time called RatingInputs. The class includes a render function which returns some JSX, as well as the same set of default props we used for StarRating.

I took the liberty of going ahead and including an input tag (and corresponding label) for our rating. Notice the use of htmlFor on the label instead of the usual for; like class, for is a reserved word in JavaScript, so JSX provides an alternative for the HTML attribute.

In case you’re wondering why you aren’t seeing this show up on the page, remember that RatingInputs hasn’t yet been imported into the main App component. You could do so now, if you wish. You just might get some errors flashed to the page as you make changes to the code.

As of right now, the rating input isn’t wired up to anything; it’s just a dumb number input element. To remedy that, let’s begin by pulling the rating prop into our render function and setting the input’s value equal to it:

Notice that I’ve added an attribute called “ref” to the input. What’s that for? This is how we’re going to tell React what element our rating input is. I’ve also added an attribute called onChange; this is, in fact, an event handler. The way React/JSX handle event handlers is different from using your typical addEventListener(), and you can read more about it here. The value of onChange is set to a JavaScript expression: this.handleRating. That will call a method on our RatingInputs class called handleRating.

Wait, we don’t have a method called handleRating? Let’s fix that:

Currently, all our handleRating function does is save the value of this.refs.rating.value as a number, but we’ll change that soon. The other change is the addition of a constructor function to our class. In other programming languages, constructor functions are run every time a class is constructed, and here is no different. We call super() to call the parent class’ constructor (Component), and after that we bind the this value of our handleRating function to the this value of our class.

Why are we doing this? Well, when we run a callback function in our JSX, the this value of the callback function will be undefined. Thus, we need to explicitly bind the value of this in the callback function to the this of our class so we can access this.refs.

Now we have a callback function that will run every time we update the value of the rating input. How are we going to send that value from the RatingInputs component to the StarRating component? By passing it up to the parent App component and having it pass the value down to StarRating.

State and React #

Before we implement the code that will transfer our rating data from RatingInputs to StarRating, we need to discuss the concept of state in React. A fundamental concept powering React is the idea that no child component should ever modify any prop passed down to it. Doing so would mean the parent component’s state could be modified in a way the parent doesn’t control. When that happens, you can’t always be sure what is modifying the state.

Instead, it is the responsibility of the parent component to provide the child component with the means to request changes to the parent’s state. That way, you know the only way the parent’s state can be modified is through methods the parent itself provides. The parent then passes the updated state back down to the child component, as well as to any other components receiving props from the parent.

With that in mind, we can have App pass one of its own methods down to the RatingInputs component that, when run, will update the state in App. Since StarRating also receives that same state from App, it will receive the changed data from App as soon as it is updated. Thus, we preserve the sanctity of the state by not allowing RatingInputs to modify it directly; instead, RatingInputs asks App to update its state by calling the specified update request function, and App handles changing its state and passing the updated state down to both RatingInputs and StarRating.

Enough theory, let’s code.

Passing State Between Components #

First, in RatingInputs, we’ll add the following to handleRating():

This will call a function passed to RatingInputs as a prop, called onStarRatingsUpdate.

Next, let’s update App:

Whoa, there’s been quite a lot of code added! Let’s break it down, piece by piece.

First, we added an import statement for our RatingInputs component. The next addition is a constructor for our App class:

Once again, we call super() to run the Component class’ constructor, and we also bind the this value for a function called this.handleStarRatingsUpdate, similarly to what we did for handleRating(). Finally, we set a property called state to be an object, with one member, rating, set to 5. This state property is how components handle state within themselves. We will be passing this state down to both StarRating and RatingInputs via props.

Confused about the difference between “props” and “state”? Props are data that flow from a parent component to a child; state is the data within a component.

Next, we add a function called handleStarRatingsUpdate:

In React, outside of the constructor, the only way state should be modified is by calling a component’s specific setState function, and this is what we’re doing here. We’re passing in an object to setState, and setting the properties of the object using ES6’s spread syntax. Basically, the spread operator will take all the properties of one object and copy them over to a different object. In this case, we’re first taking all of the values in this.state (the App component’s state), and then we’re taking all the values from the data argument and adding them to the object being passed into setState. This results in any properties from data overwriting any similarly-named properties pulled in from this.state.

Still confused? Keep reading; there will be a demonstration of what this accomplishes, and hopefully that should clear things up.

Spread syntax also works with arrays! Read the reference link to learn more.

Lastly, we update our render function:

We’re pulling in rating from this.state. We’ve also replaced the hard-coded prop being passed into StarRating with this rating value. Finally, we’ve added our RatingInputs component below StarRating, and we’re passing two props to it: rating and onStarRatingsUpdate, which we’ve set equal to our function handleStarRatingsUpdate. Why the two different names? It’s just a convention: we say “on” to refer to the prop, and “handle” to refer to the actual function which handles the call from the child component. We could easily have both the prop and the function be the same, if we wanted. My preference is to use the on/handle convention.

Assuming nothing went wrong, once you save App.js you should see the rating label and input appear immediately below the black stars from StarRating. Try modifying the value of the input. If everything was done correctly, as you change the value of the input, the rendered stars should also update right along with it. Our components are talking to each other through the parent!

If you were confused by the contents of handleStarRatingsUpdate(), perhaps now it’ll make more sense. When we update the rating input, it calls handleRating(), which takes the value of our rating and passes it as an argument into onStarRatingsUpdate(). This runs handleStarRatingUpdate(), on App. It calls setState(), which first sets state equal to this.state (in other words, the previous state values). Next, it adds any values passed in via data; any state properties with the same key as the properties passed in by data get overwritten with the value from data. With the state updated, App passes the state back down to both RatingInput, where the value of the rating input is updated, and StarRating, where the updated rating is calculated as stars and rerendered onto the page.

As you play with the rating input, you may find you’ve triggered an error called “invalid array length”. This is because we’ve set rating to a value lower than minRating or higher than maxRating, and our star calculations can’t handle that. To prevent us from setting the rating too low or too high, let’s start by setting the min and max values on our rating input:

Now, when we toggle the rating input up and down, we should be stopped when our rating gets down to 0 or up to 10. That’s good enough, for now; we’ll be implementing a better fix shortly.

Wiring Everything Else Up #

Now that we have our rating wired up, let’s go ahead and wire up minRating, maxRating, and starRatio. When you’re done, your App and RatingInputs components should look something like this:

We’re setting the min values of both minRating and maxRating to 0, and the max values to limit. You technically don’t need to include a limit, but in testing I’ve found that adding min/max ratings resulting in over 1,000 rendered stars introduce the possibility of performance issues. Thus, I made the decision to limit how high those values can be increased.

Now all of the values are dynamic: we can set the maxRating to 20, our starRatio to 1, our minRating to 5…any number of combinations, really! As you experiment with the possible combinations, however, you might notice something: if you directly set the input values to something illegal (instead of using the increment/decrement buttons), you’ll trigger the “invalid array length” error. While setting the min/max attributes of rating stops the input increment/decrement buttons from going out of range, they do not prevent us from entering such an illegal value directly. Also, you could change minRating to be higher than maxRating (or maxRating lower than minRating), and trigger an error that way.

We clearly need better validation than what we can get from HTML. Let’s implement it!

Better Validation #

First, we’ll create a new folder in src and call it lib. We’re going to make a library of functions we can use to validate our inputs. Make a new file in lib and call it validate.js, then place the following code into it:

With ES6 exports, you are not limited to only exporting one function; you can export as many as you want. You just have to refer to them explicitly by name when you pull them out. For example, if you wanted to use the minLessThanMax function, you’d write the import statement as import { minLessThanMax } from 'path/to/validate'. You can specify a single function as the default export; this is what will be returned if you simply do import ThisFunction from 'path/to/validate'. In our case, all we really need is the default function in validate. I just wanted to illustrate what export is capable of, and that you aren’t limited to one export per file.

Let’s look at the default function we’re exporting. We’re taking in arguments for rating, minRating, maxRating, starRatio, and limit, and then passing these arguments into various validating functions. The key is that we’ve strung the return as a chain of functions using the && (and) operator. The moment any one of these validating functions returns false, the entire function returns false; otherwise, if all the validating functions return true, the function returns true.

For a couple of the validating functions, I use a combination of rest parameters in the function argument and the filter array method to validate one or more possible values with just a single line of code. Read up on the two links to figure out how this works!

Now that we have our validation library, let’s start by importing it into RatingInputs:

As you can see, all we needed to do is import the default validate function as inputIsValid, and then wrap our call to onStarRatingsUpdate() in an if statement which checks to see if inputIsValid() returns true. If it doesn’t, then nothing happens. The inputs won’t update, which means you can’t change them to anything which would cause validation to fail.

We also need to import our validation library into StarRating (this is what the asterisk was for):

Here, we are once again importing the validate library as inputIsValid. This time, we’re using a new function called componentWillMount to run the inputIsValid check, and if it fails we throw an error.

What is this for? componentWillMount() is a part of how React components work, one of the so-called “lifecycle methods”. A lifecycle method is just a function that a React component runs at a specific moment, such as before props are passed down, or before/after a component is rendered. In this case, we are using the componentWillMount lifecycle method, which runs right before a component is first rendered. Because we are checking inputIsValid() at this point in time, we can make sure the initial props passed down by App are valid, and if they aren’t we’ll throw an error telling the developer that they need to make sure rating is between minRating and maxRating (as this is what’s causing validation to fail).

If you want to learn more about lifecycle methods, read this.

Bug Hunt #

Now, things seem like they work. If you try to directly input a minRating of 11 when the maxRating is 10, you’ll find yourself unable to do so. Same with if you try to make rating exceed the bounds of the min and max ratings. Yet there is still a bug, which I didn’t notice until I started messing around with the project one final time before submitting this post.

What is the bug? Click on the rating input, then try to backspace. Normally, when you do this, you’d expect the input field to be empty; however, our app will either refuse to delete the value at all, or it will set the value to 0. Peculiar, indeed, and not very user-friendly.

Why is this happening? Well, in our handleChange function in RatingInputs, we automatically convert each ref’s value to a number when we assign it. This is because refs are stored as strings, so we want to make sure we’re working with numbers when we’re performing validation. What happens when an empty string gets converted to a number? It becomes 0. Thus, when we assign the value of an input that was cleared by the user — an empty string — we’re assigning the variable a value of 0. When minRating is 0, this results in rating being set to 0; otherwise, 0 is lower than minRating, so our code doesn’t allow the update to be made.

How do we fix this? We’ll need to refactor the RatingInputs component to have its own state, instead of merely rendering the props App passes down to it. This way, we clear the input fields, but delay sending an update until the inputs have values that are validated. Why wasn’t this done in the first place? When I first designed the component, I didn’t think it needed to have its own state, so I simply wired everything up through props. Now, however, it’s clear that we need the inputs to be able to have values separate from the parent state, so giving RatingInputs its own state is appropriate.

Here is what the changes will look like:

First, in the constructor, we set the initial state of the component to be equal to the props being passed down to it (we also accept props as an argument). Next, we add a new function, anyAreEmpty, which just checks to see if any of the arguments passed into it are empty strings.

We make quite a few changes in handleRating():

  • We change our assignments to get the raw ref values, instead of converting them to numbers.
  • We update the state of our application to match the value of the refs.
  • We use the anyAreEmpty function to see if any of the variables are empty strings, and if so we return.
  • Having verified we have no empty strings, we convert the ref values to numbers.
  • Finally, as before, we pass the numeric values through our validation library, and only update when validation passes.

Lastly, in our render function, we get our default values from the state of RatingInputs, not the props passed down from App.

When these changes are implemented, you’ll notice that you can now set the input fields to illegal values once again. However, no updates are sent to App, so StarRating is not updated. Additionally, because of the min/max attributes set on the rating input, an error highlight appears around the element when an illegal value is set. I consider this “good enough” error notification for a sample project.

Final Touches #

At this point, our Star-Rating app is fully functional and secure against bad data. The only thing left to do (if you want) is to restructure the app and change the styling to make it look like the final result. We’ve already done a lot of hard, good work, so I’ll simply provide you with the JSX you’ll need for App.js and the styles you need to replace within the App.css file to make it match my version of the app:

Of course, if you want to make your Star-Rating app look completely different, then feel free to do your own thing!

Conclusion #

I hope you had fun building this app; I certainly enjoyed doing something different from a Todo list as a sample project. React, once you get used to some of the ways it wants to do things, is a fun front-end framework to build apps with. Even though it may seem like it takes some effort to do things with React, the way it is designed helps you think logically about the structure of your code, helping you avoid writing smelly code and making it (mostly) clear how your app works.

If you want to view the entirety of my source code, look up the project on my Github repo.

Questions? Feedback? Leave a comment!

Star-Rating Part 2: Vue

Hacky Way to Display PHP Object for Debugging

Sometimes, you have to work with…let’s call it “legacy code”. You don’t get to use the nice things to help you debug your code, like Laravel’s dd(), and you’re getting really tired of var_dump-ing all over your web page just to figure out what variables are being used by the particular function you’re debugging.

In cases like that, here’s a hacky way to print data to the javascript console:
echo "<script>console.log(" . json_encode($whatever) . ");</script>";

This does exactly what it looks like: prints a script tag which console.log’s a json-encoded PHP variable of your choice to the inspector console. You can do this with practically any type of variable, but where this is particularly useful is when dealing with arrays and objects. Instead of having to burn your eyes looking through lines and lines of irrelevant data to find what you’re looking for, you can just take advantage of your browser’s object display syntax and only look at the parts which are relevant to you.

Refactoring BandTracker

It’s always a good idea to take time to review old code you’ve written and see how you can refactor it to be cleaner, modern, and easier to understand. This weekend, I’ve been doing just that with one of my old demo projects, BandTracker. It’s a Laravel 5.4 app I wrote about a year ago as a demo project for a company I was applying for. I didn’t get that job, but the project did end up helping me land my current job.

Since then, I’ve grown significantly in my programming knowledge, and I decided it was time to revisit this old project and refactor it. Many things were updated during this process: I upgraded from Laravel 5.4 to Laravel 5.6, I added unit tests for the existing features, and I refactored the code to follow Laravel best practices, including PSR-2 conventions.

This article is about one part of the refactor: the AlbumController. Here is a link to what the file used to look like: OldAlbumController. Take a look. It’s…ok. It works, but it looks cluttered and slightly complicated. Not to mention this isn’t PSR-2 compliant at all, or friendly to Laravel best practices. Let’s fix that!

You’ll see me include <?php tags at the beginning of each gist. That’s to get the syntax highlighting.

First, we start with the index method.

Right away, you can see a typo in the first inline comment. That if statement also looks a little ugly, even though it’s doing its job well enough. Also, 2-space indentation. It’s what I liked at the time, but now we’re trying to follow PSR-2 (and I’ve used 4-space indentation long enough that 2-space indentation now hurts my eyes to look at).

Let’s improve on these things.

That looks cleaner, doesn’t it? Instead of an if statement, we’re now using a ternary operator, which takes up less room and is easier on the eyes. I also added eager loading (the ::with('band') part) so we don’t end up with N+1 queries when our album index view calls $album->band->name or any other property it needs from the parent band. I run my unit tests to make sure my changes didn’t break anything…and nothing broke. Yay!

You can assume from this point on that I’ll keep running unit tests after I make a change.

Next up, the sorting functionality. If you look at it, you’ll notice that it calls $this->sort(), and if you further inspect the old controller you’ll see that there’s no sort method to be found. Where is it? Well…it’s in the parent class. I added it there so both the album controller and the band controller could make use of it. That’s not cool, and it needs to be dealt with. I also want to remove the for loop attaching band_name to the album. That’s what $album->band->name is for!

Let’s make it happen!

Much better. I already changed the band controller to use this same code, so we no longer need to use the crappy old sort method parasitically attached to the parent Controller class. I also used a switch statement instead of an if statement, because in cases where I’m expecting specific values I find this cleaner and easier to read. It also allows me to get rid of the redundant $sortdirection ternary. I normally love ternaries, but that one wasn’t a pretty sight.

Why didn’t I abstract the sorting logic into a service? It’s a valid option, and arguably a “more correct” choice from a DRY standpoint…but at this point, I felt it was more complexity than I needed to deal with. I’d need to set up a service provider, bind it to the app, include another facade, find a new, clean way to change $sortdirection…right now, I just need basic sorting, and the code written is easy to understand. For now, I’m okay with being a little WET.

I could also have used a trait to graft a sorting function to my controllers, but, again, that is more complexity than is needed right now.

When I initially made this change, my unit tests reported an error. Now that band_name, is no longer an actual attribute on the Album model (because we took out the for loop), there’s no longer anything to sort. I changed the parameter being passed in to band.name to allow Eloquent relationship magic to work, and that fixed the issue.

On to the next part. The band grab is alright, but we don’t need every single parameter. Let’s narrow the scope of our grab.

Nothing fancy, just adding a select to only grab the band’s name and id.

The last part doesn’t need much work, either. I’ve taken to prefer the compact() way of passing variables into the view, so I’ll update my return to use that.

Notice that I’m now directly using the variables I assigned earlier, instead of calling $request->variable on some of them, which allows me to compact everything and avoid the ugly-looking ['variable' => $variable].

And that’s the index method! Let’s see the changes in full.

It’s about five lines longer (thanks to the syntax of switch/case), but despite that it’s a lot easier to read. Plus, now we’re PSR-2 compliant! We also optimized our queries and cleaned up the return statement. Overall, a good start to the refactoring process!

I used a linter on my editor — Visual Studio Code — to help me identify where my code was violating PSR-2 standards. I highly recommend using a linter whenever you need to enforce any kind of style guide; it’s much easier than just eyeballing it!

Time to deal with the create method:

We can improve it slightly to make it easier to read.

I added breathing room between the lines so it’s less dense, and thus less of a cognitive burden to read. I also compacted the variables being passed into the view, as I did in the index method.

You can assume I’ll keep adding compact() to my returns from this point forward.

Next up, the store method:

Yeah, it looks gross, doesn’t it? I’m validating directly in the controller, I directly created a new Album model, I’m individually storing every parameter to the model…yuck. Let’s fix this, stat!

First, I enter php artisan make:request AlbumRequest in my terminal to make a Request object, and then move all of my validation rules to it (after making sure the authorize method of the request always returns true — otherwise none of our form submissions will be authorized). A quick update to the typehint for the $request variable (and the corresponding @param in the doc block) and that part’s good to go.

Now, to deal with saving the data. There’s ten lines of steamy code just begging to be put out of their misery. Let’s oblige.

Bam! All that ugliness reduced to a clean one-liner — that never gets old. Any attribute we need to store will get passed into the create method by $request->(), and if we happened to not pass a parameter in, Model::create() ignores it.

However, we have to be careful that we aren’t passing in any parameters which don’t exist on the model; Eloquent doesn’t like that.

Coming off the one-liner high, let’s deal with the flash message. There’s a better way to write that:

We no longer call flash() statically off the Session facade — we’re using the session() helper, instead. We’re also using a localization string for our message and passing in the name of our album. This will result in the same flash message as before, but with the message string in the app language file, which will not only make it easier to find and change the string in the future, should we want to do so, it will allow us to reuse the string in other locations of the application.

The final result for the store method:

Whew! Just extracting the validation and squashing the model creation makes this method much better already! Combined with the session flash improvements, we’ve done good work here!

On with the show (method):

Another simple method. The main things that we need to change here are adding dependency injection (and subsequently removing the find() call), adding eager loading, and adding some spacing to improve readability. Make it so!

Quick and easy. Time for the edit method!

The changes we need to make here are nearly identical to the ones we made for the create method; the only differences are the additions of eager loading and dependency injection. Let’s do it!

It’s a similar story for the update method; we just need to add dependency injection and the changes from the create method. (I’ll spare you the torture of looking at all that bloated code from the old method — if you really feel like seeing it, it’s in the link to the old album controller at the beginning.)

Here’s the result of making those changes:

At last, we’ve come to the final method — the delete method. Here’s the old code:

Not much needed here — just add dependency injection, reduce the deletion to a simple $album->delete(), update how we’re setting the flash message, and reformat for readability. Easy peasy!

We’re almost done! There’s just one thing left to take care of — the use statements before our class declaration.

The obvious one is the newline between Illuminate\Http\Request and the rest of the use statements. We’ve also refactored all of our session calls to use the session helper, so we no longer need to include Session directly. The as statements are redundant, so we can get rid of them. Finally, we’ll add a newline between the opening tag and the namespace declaration.

Whew! This took a fair bit of work, but the end result is not only a lot easier to read, it more efficient and it’s compliant with PSR-2. You can look at the final result here.

Feel free to look over the repo to see what other changes I made!

Sync Your Project Directory To Your VirtualBox VM

One of the things I enjoy about Laravel’s Homestead is the automated syncing that happens between your local project directory and the virtual machine, so any changes you make locally are immediately transferred to the VM on save, thus making changes quick and easy. I wanted to set up the same thing for a VM of a non-Laravel project recently.

I started by firing up the VirtualBox GUI, accessing the settings for my individual VM (let’s call it vm-name) and went to the section labeled Shared Folders. This is where you can set up links between your VM and local directories on your machine. Clicking on the “add new” icon, I proceeded to enter the path to my project directory: /Users/joshanthony/projects/project-name and the name of the folder I wanted to store things in on the VM: project-name. After that, I ticked the “Auto-mount” and “Make Permanent” properties; “Make Permanent” will make the shared directories available on the next boot and “Auto-mount” will handle mounting the directory. I then clicked “OK” to save the changes.

At this point, my project directory will be shared with the machine; however, it will be stored in a location of VirtualBox’s own choosing (the location will depend on what OS the VM is running). I’d like to store it in /var/www/vhosts, the VM’s websites directory. VirtualBox’s GUI doesn’t allow you to change that, so it’s time to get in the console!

I entered the following commands:

VBoxManage guestproperty set vm-name /VirtualBox/GuestAdd/SharedFolders/MountDir /var/www/vhosts
VBoxManage guestproperty set vm-name /VirtualBox/GuestAdd/SharedFolders/MountPrefix /

For both commands, we need to specify the name of the virtual machine we want to change; in this case, vm-name. The first command changes the directory our shared folders mount to. That might seem like enough, but by default VirtualBox also adds a prefix to the paths all mounted directories (sf_, for me), which in this case would result in our shared directory being saved to sf_var/www/vhosts, which isn’t what I want at all. Thus, the second command changes the mount prefix to a simple /; it might seem we can just set it to an empty string, but this just causes the property to be reset to its default value of sf_, so we want to use the slash.

When I was searching for how to do this, I found a number of suggestions that involved running the above commands on the VM itself using VBoxControl instead of VBoxManage; however, for me this resulted in failure due to the properties being marked guest read-only. That’s why I went the VBoxManage route.

Once the commands are run, all that should be needed is to reboot the VM, and things should work! If they don’t the most likely culprit is the web server user not having permission to access the project directory. Running sudo usermod -G vboxsf -a [server-user] (where server-user = the server’s username) should resolve this issue.

Now I have my local project directory synced with its web directory on the VM, and any changes I make locally get instantly reflected on the VM. Real-time changes FTW!

Creating a Data Container in Javascript

While working on an interactive novel demo, I happened to need a container for containing flags (aka values that I could check later on to change bits of flavor depending on what a player chose), but I didn’t want to just use a plain ol’ JavaScript object. I wanted to create a true data container where you could set any property you wanted, but you wouldn’t be able to overwrite the data in the container itself.

After experimentation and some searching, I came across Proxies, which let you define custom behavior for basic object methods. With guidance from a Stack Overflow answer, I came up with this:

const Flags = new Proxy ({data: {}}, {
    get: (target, name) => target.data[name],
    set: (target, name, val) => target.data[name] = val
});

That was a fun little bit to figure out, and I got to learn something new. Woohoo!

Laravel: Validate Fields Only When One Is Present

Sometimes, when setting up validation for Laravel forms, you need to be able to set up validation rules for two fields, but allow for one of them to be ignored if the other is filled. This is how you can set up your validation rules to make that happen:

'email' => 'nullable|required_without:login',
'login' => 'nullable|required_without:email'

You first have to set nullable to allow both fields to have null values, otherwise Laravel rejects them. After that, you set each field to require_without:[otherField]

A potential use case for this (and the one I needed it for) is when your application allows a user to login using an email, a login, or both. It turns out sometimes didn’t work at all for this because it only applies when the field in question sometimes isn’t sent, not cases where it is sent but the value is null. The solution above accounts for this.

Laravel and PHPUnit Error: Supported Ciphers

While setting up unit testing for a PHP project, I ran into this error when I first tried to run the simple example tests to prove the system was set up correctly:

RuntimeException: The only supported ciphers are AES-128-CBC and AES-256-CBC with the correct key lengths.

Searching for this error revealed that, by far, this issue most commonly happened when you had an improper app key, and the solution was simply to regenerate the key — but this was not my problem, because I had a perfectly valid app key. I even regenerated it for good measure, and still I got the error. Nothing else on the first two pages of Google provided any help for my situation, and once I started getting the foreign-language pages I knew I was on my own.

Fortunately, in this case, the solution proved to be simple. In the phpunit.xml file Laravel automatically generates, I found this in the section for setting up environment variables:

<env name="APP_ENV" value="testing"/>

Aha! It was set to look for the testing environment, aka .env.testing! Sure enough, my environment file had the application key set to, literally, the word “key”. I simply copied over my main .env file into .env.testing, ensuring the correct key value was provided, and this solved the issue.

React Error: Expected a string, got object

If you’re working with React, you might run into one or both of these errors:

Warning: React.createElement: type should not be null, undefined, boolean, or number. It should be a string (for DOM elements) or a ReactClass (for composite components). Check the render method of `SomeComponent`.
bundle.js%20line%20242%20%3E%20eval:45:9
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. Check the render method of `SomeComponent`.

One possible problem, which is what happened to me, is that you simply forgot to export one of your modules. In this case, I had a child component which I forgot to export, but the resulting error said nothing about the child component so it took awhile to puzzle this out. Hopefully this helps someone else figure out the problem faster than I!

Laravel Redirect Status Code

Laravel’s documentation doesn’t make this obvious, but when you are using a redirect you can change what the status code of the redirect is.

Route::get('some/route', function(){
    return Redirect::to("another/route", 301);
});
Route::get('some/route', function(){
    return Redirect::route("another.route", null, 301);
});

The tricky part is knowing which parameter is the status code. To check which parameter it is, refer to the Redirector Class.