Prerequisites: some familiarity with HTML, JavaScript, and CSS.
Unfortunately, most of the React tutorials out there have no consideration for best practices and don’t always teach you the “right” way to do React.
In this tutorial, I will go over the basics of React, and the most common bad practices that you might encounter.
This tutorial is going to be long, so make sure to get yourself some coffee!
Before we get started, let’s stop for a moment and see why React indeed is the best choice.
In React, you describe what to render (instead of telling the browser how to do it). This also means that the amount of boilerplate is greatly reduced.
In React you simply start coding, it has no component boilerplate that you have to generate. There’s some setup involved, but when it comes to the components, you can express them as pure functions.
JSX in React feels just like HTML, there’s no special syntax to learn:
Learning curve is very important when picking a UI framework. React has the least abstractions. If you know JavaScript then you can probably start writing React code in a single day. Yes, it takes time to pick up best practices, but you will be able to start very fast.
In my opinion, React’s greatest strength comes from the fact that you aren’t even forced to use classes. Classes over-complicate codebase without providing any benefits.
In React, all of the UI can be expressed as a set of pure functions, and using pure functions to render the UI feels like a breath of fresh air.
“turned on MacBook Pro beside space gray iPhone 6s, black ballpoint pen, and black Asus cordless optical mouse on top of table” by Fabian Grohs on Unsplash
Now that I’ve hopefully convinced you to go with React, let’s write some code!
Node.js is a JavaScript runtime environment which enables us to compile cool React code!
First of all, let’s make sure that you have Node.js installed. If not, you can download it from here: https://nodejs.org/en/download
We’ll be using create-react-app from Facebook to scaffold our application. This is the most popular way to set up your environment and start coding. It comes with many required tools built-in, which helps us to eliminate many decisions.
To install create-react-app globally:
npm i -g create-react-app
Then to scaffold your first project run:
create-react-app react-intro
That’s all! Now, to start the application:
cd react-intronpm start
This will launch a development server and will allow you to open the new and shiny React application by going to http://localhost:3000/ in your browser.
Now, let’s see how things are working under the hood. Using your IDE of choice (I recommend Visual Studio Code) open the newly-created project.
Within the project go to the public/index.html
file. This is what you’ll see:
The part that interests us is the <div id="root"></div>
. This is where our React application will go. The entire root div will simply be replaced with the contents of our React application. Everything else will remain unchanged.
Now let’s open src/index.js
. This is the file that bootstraps the entire React application. And by the way, all of our React source code will go into the src
directory.
The line that does the magic is:
ReactDOM.render(<App />, document.getElementById('root'));
This line is a way of telling React to take our App component (we’ll discuss it in a bit), and place it within the root
div that was defined above within the index.html
file.
Let’s now focus on the <App />
part. This looks a lot like HTML, doesn’t it? This is called JSX, and is a special JavaScript syntax that React uses to do its magic. Note that it starts with a capital A
— it is <App />
, not <app />
. This is a convention used by React, which allows it to disambiguate between regular HTML tags and React components that we’ve created. If you don’t start your components with a capital letter, then React won’t be able to render your components.
Whenever using JSX, we always have to import React by adding the following line within our .js
file:
import React from 'react';
Now we’re ready to take a look at our first component. Let’s open src/app.js
:
In order to create a React component, we have to create a class that inherits from React.Component
. That is exactly what the line class App extends Component
does. All React components should implement a render
method — as you may have guessed, all of the rendering is happening within this method. The render
method has to return the markup to be rendered.
A small side-note: the
className
attribute is equivalent to theclass
attribute in HTML, and is used to assign CSS classes for styling.class
is a reserved keyword in JavaScript, and cannot be used for an attribute name.
Let’s recap:
React.Component
classrender
method, which returns the markup.There are two ways to create components in React — Class Components and Functional Components. As you may have noticed, the example above uses a Class Component. And, unfortunately, most beginner React tutorials encourage the use of Class Components.
What’s wrong with Class Components? They are hard to test, tend to grow really big, prone to poor separation of concerns, couple logic with presentation (which makes debugging and testing harder). In general, you will shoot yourself in the foot by using Class Components. Especially if you’re a beginner, I would recommend staying away from them altogether.
Ok, Class Components are bad, I get it. But what are the alternatives? Functional Components. If a component has nothing but the render
method, then it is a great candidate for refactoring into a functional component. Let’s see how the App component created by create-react-app can be improved:
See what we have done here? We’ve removed the class and replaced the render
method with function App() {...}
. And if we make use of ES6 arrow functions, it is going to look even better:
We’ve turned the class component into a function that returns the markup to be rendered.
Think about it for a moment… A function that returns the markup, there’s no unnecessary boilerplate code, just pure markup! Isn’t it beautiful?
The functional component reads much better and has a higher signal-to-noise ratio.
In this article, we’ll stick with Class Components, because they involve fewer abstractions, and are easier to demonstrate the core React concepts. Once you’re comfortable with React basics, I strongly recommend you to read my more in-depth article — Mastering React Functional Components with Recompose.
Props is a concept central to React. What props are exactly? Think for a second about parameters passed to a function. Props are just that — parameters passed down to a component.
Here we have created a Greetings
component, and we’re using it to greet John Smith from within the App
component. This will result in the following markup:
<div><div>Hey you! John Smith!</div></div>
The curly brackets in {props.name}
denote JavaScript code. The Greetings
component was passed firstName
and lastName
as parameters, and we simply retrieve them by accessing the props
object.
Note that the component got passed a single props
object, not two values for firstName
and lastName
.
We can further simplify the code by making use of the ES6 object destructuring syntax:
Note that (props)
was replaced with ({ firstName, lastName })
. This means that we’re only interested in those two properties of the props
object. And this, in turn, allows us accessing the firstName
and lastName
values directly, without having to explicitly specify props.firstName
.
What if we’ve been using class components instead?
I don’t know about you, but to me, this looks much more bloated! We always have to explicitly use this.props
.
“black camera, round silver-colored analog watch, black Swiss Gear pocketknife, and black flashlight” by Alexander Andrews on Unsplash
Single-Responsibility Principle is the most important programming principle to follow. It states that a module should do one thing, and it should do it well. Not following this principle alone can turn any codebase into a nightmare that is impossible to maintain.
How can we violate this principle? The most common way is placing unrelated things in the same file.
I’ll refer to the Single Responsibility Principle multiple times in this tutorial.
Beginners usually place multiple components in the same file. Here we’ve placed the Greetings and App components within the same file. This is a bad practice because this violates the Single Responsibility Principle.
Even the smallest components (like the Greetings component above) should be placed in a separate file.
Let’s place the Greetings component into its own file:
And then to use it within the App
component:
import Greetings from "./Greetings";
const App = () => (...);
Make sure that the filename matches the component name. App
component should be placed in App.js
, Greetings
component should be placed in Greetings.js
, and so on.
State is another concept central to React. This is where you want to keep your data — things that may change. Storing the value typed into a form element? Use state. Keeping track of score within your game? Use state.
Let’s build a simple form that takes in user’s first name. Note that I’m purposefully using a class component to demonstrate the concept. I demonstrate refactoring of a class component into a functional component in my other article Mastering React Functional Components with Recompose.
Ok, the user can type his email into the form, which is great! If you’ve been paying attention, then you’ve noticed that no matter what, the name John will be used in the greeting. What if not all of our users’ names are John? We’d place ourselves in a very uncomfortable situation.
How can we use the value typed into the input? In React we aren’t supposed to query the DOM directly. This is where input handlers and state come in.
State is basically a plain JavaScript object that is stored as a property within the SimpleForm
class component. Here we’re adding value firstName
to the class.
Our firstName
input now has the onChange
event handler. It fires every time when the user types a key into the input. And the propertythis.onFirstNameChange
in our class handles the onChange events.
Let’s take a look at the onFirstNameChange
property:
this.setState(...)
This is how we update the state of our components. We’re not supposed to update the component state directly, only via the setState
method. And to update the value of the firstName
state value we simply pass an object with the updated values to the setState
method:
{ firstName: event.target.value }
In this case, event.target.value
is the value that was typed into the form input. In this case, this is the user’s name.
A side note: we haven’t defined
onFirstNameChange
as a method. This is extremely important to define it as an arrow function property on the class, and not a method. If we had defined it as a method instead, thenthis
would be bound to the form input that called the method, not to the class as we would have expected. This small detail often trips up beginners. This is another reason to avoid classes in JavaScript.
“silver iMac turned on” by Brennan Burling on Unsplash
Now let’s implement simple form validation using regular expressions — let’s ensure that the first name is at least three characters long, and contains only letters.
We will add another event handler for the onBlur
event —it will fire whenever the user leaves the input. We will also add another property to the state — firstNameError
. And then we’ll display the validation error right under the input (if errors are present).
First, we’ve added a firstNameError
property to the state:
state = {...firstNameError: "",};
The validation itself is happening in the validateName
arrow function above. It simply tests the input name against the regular expression:
validateName = name => {const regex = /[A-Za-z]{3,}/;return !regex.test(name)? "The name must contain at least three letters...": "";}
If the validation fails, we return the validation error. If the validation succeeds, then we return an empty string (which signifies lack of error). We’re using JavaScript ternary expressions here to make the code terser.
Let’s take a look at the onBlur
event handler (fires whenever the user leaves the input):
onFirstNameBlur = () => {const { firstName } = this.state;
const firstNameError = this.validateName( firstName );
return this.setState({ firstNameError });};
Here we extract the firstName
from the state by using ES6 object destructuring syntax. The first line is equivalent to:
const firstName = this.state.firstName;
Then we run the validation function defined above with the firstName
, and then we set the firstNameError
state property with the error returned. If the validation failed, the firstNameError
will be set. If it succeeds, then it will be set to an empty string.
render
methodAnd now let’s take a look at the render()
method:
render() {const { firstNameError, firstName} = this.state;...}
Here we’re once again using ES6 object destructuring to extract values from the state.
<input...onBlur={this.onFirstNameBlur}/>
This line assigns the onFirstNameBlur
function as the event handler for the onBlur
event.
{firstNameError && <div>{firstNameError}</div>}
Here we’re using short circuit evaluation feature of JavaScript. The div containing the firstNameError
will be rendered only if the value itself is truthy.
“empty spiral stairs on low-angle photograph” by Maxime Lebrun on Unsplash
If you’ve been following along, then you might have noticed that our form isn’t particularly pretty… Let’s change that by adding some inline styling!
Styles in React are added simply by passing the styles in the style
attribute.
I will admit that I’m not a designer, but my programmer art is looking much better now. Here’s the form with validation error:
Here we’ve encountered another bad practice, that unfortunately is too common — placing the styles within the render
method of our components. Why is this bad? This violates the Single Responsibility Principle. It also clutters our component with styles, which significantly impairs readability.
What are the remedies? Create a special style
object that will contain all of our styles. It is considered a good practice to place the styles
in a separate file:
And then to use it within our SimpleForm
component:
This looks much cleaner!
Takeaway: place your styles in a separate file.
Let’s make the form a little more interesting by adding a field to enter the last name:
Not much has changed here — we’ve simply duplicated the code for the firstName
input, and also duplicated its event handlers.
Did I just say duplicated? Duplicate code is a big NO in software development and should be avoided at all costs.
This bad practice once again comes back to the violation of Single-Responsibility Principle. Good written code should read like a poem, and I bet that the render method of our component doesn’t read like a poem. Let’s change that.
The inputs are almost identical, and both require some sort of validation. Let’s apply some refactor-fu to our component, and create a reusable TextField
component:
I’ve simply extracted one of the components from the render
method, converted it into a functional component, and passed it things that change as props:
Nice, this reads much better now! We can even go one step further, and create dedicated TextField
components for the first and last name:
Here we’re simply returning a pre-made component to display the first name. The ({...rest})
is the new Object Rest syntax — this means that whatever is passed as props will be saved into the rest
object. Then to pass the props down to the TextField
we’re using the Object Spread syntax {...rest}
. This takes the rest
object, spreads its properties, and passes them down to the TextField
component.
In other words: we take whatever was passed into FirstNameField
, and pass it unchanged to TextField
.
And similarly, the LastNameField
:
This is what our form is looking like now:
Much better!
this
keyword, which has always been a major source of confusion.The accompanying source code can be found at GitHub
This tutorial has gotten a little longer than I was expecting. There’s still a lot to cover, especially relating to code organization, and a few other best practices. Let me know if you’re interested in hearing more in the comments.
If you truly want to master React, I would strongly recommend you to check out my other article: Mastering React Functional Components with Recompose.