When writing this course, React was on version 17.0.2 and has evolved over the years, which means that the way we write components today looks nothing like the way they were in the beginning. Unfortunately, that means some codebases contain components written using other syntaxes. Time to get back in our time machine to take a look over the main stages in React’s evolution. 🚀
Discover the Evolutionary Stages of React
In the Beginning: createClass
When React became open source in 2013, components were created with React.createClass
. To give you an example, a MyComponent
component that receives a title
prop would be written as:
React.createClass({
displayName: "MyComponent",
render() {
return (
{this.props.title}
)
}
})
These days, though, this syntax is considered to be deprecated, and you won’t find it anywhere in React’s documentation.
However, you’re likely to stumble across class components.
Discover Class Components
With the addition of classes in ES 2015 (here is Mozilla’s documentation on classes), React wanted to align itself by creating class components. It was a time of major change. In this and the following chapters, we will go over their syntax, how to make API calls, and manage state and props in class components.
Learn about the syntax of class components in the screencast below. 👇
If you’d like to see the full code for this whole component, you can find it here, on the GitHub repository.
Discover the Syntax of Class Components
As you’ll have seen from the screencast above, class components have different syntax. We’re going to create a new EmailInput
component and put it in the footer.
Start by declaring the component:
import { Component } from 'react'
class EmailInput extends Component {
constructor() {
}
render() {
return (
)
}
}
export default EmailInput
You declare your components with class
and extends Component
. If you’ve ever worked with classes, you’ll have already seen extends
and constructor
. It’s important to know that constructor
(documentation here) is a method for creating and initializing an object when you’re using the keyword class
.
What’s is render
?!
It's one of your component’s methods. In fact, it’s the only method that you are obliged to call in your component (so it needs to be there). For certain events, your component will have to re-render. Each time, this method will recalculate what is declared and display what is returned.
A new render will happen every time an update takes place:
When initializing the
constructor
.Whenever a prop is updated.
For every change to state (more on this later).
render
is not a feature of function components.
Note that render
must return JSX (which will be in the return
). But if you have nothing to return in your render
, you can also return null
.
Access Props
We’re now going to add a bit of style to the component that we’ve just created, creating a version for the darkMode
and one for the lightMode
. In a previous chapter, you learned that using context is much easier with hooks. Unfortunately, you can’t use hooks from class components. Therefore, you have to pass the theme
as props.
But we don’t have any parameters in which we can get props, so how do we get them?
Don't worry, there’s a way to get your props in class components by using this.props
throughout your component – whether it’s in the return
,render
, or a method. Start by initializing them in the constructor
, and then pass the initial state
, even if you don’t need it for now (it keeps our linter happy).
Declare the following in your component:
class EmailInput extends Component {
constructor(props) {
super(props)
this.state = {
inputValue: '',
}
}
render() {
// Here we get theme by destructuring this.props
const { theme } = this.props
return (
)
}
}
With this, you can apply a different style depending on your theme
. You can find this style on P4C1-exercise.
Then what’s this
?
If you’re familiar with JavaScript, you’ll have come across this
, which refers to the object it belongs to. If you need a reminder, you can see how to define a class in JavaScript in the chapter “Define objects and their attributes with classes” of the course Learn Programming With JavaScript.
Here, this
refers to the component: it is the props
and the state
of EmailInput
.
Careful, though – when you declare the methods (which are kind of functions) in your components, you need to pay attention to the bind
.
The what? The "bind"?
To put it simply, you use bind to bind your methods to your class component: they need to be correctly bound to the this
. You’ll understand better once you see the state
. ✨
Manage State With setState
Let’s now manage the value entered into the EmailInput
with setState
.
Isn’t setState
more or less the same as useState
?
Not really. First of all, useState
is a hook: it’s how you manage state for function components. setState
is for class components. Although both functions deal with state, they do not work in the same way:
useState
lets you declare your state variable, initialize its value, and get a function to update it.setState
only lets you update the state in your component. Remember, the initial state is declared in theconstructor
.
You have to go step by step to manage the state of the value entered into EmailInput
. If you’re struggling to follow, all of the code you need is on the GitHub repository.
In the section just above, you initialized your state in the constructor
with:
this.state = {
inputValue: '',
}
You’re now able to declare a function to update the value of your state:
updateInputValue = (value) => {
this.setState({ inputValue: value })
}
There is no need to have const =
in front of your function. Usually, you’d have to initialize it, but given that you’re in a class
, updateInputValue
is a method of your EmailInput
class.
Could we have put updateInputValue
in the render
?
Yes, but everything within the render
is run every time a prop or the state is updated. That means that your function would be declared again, which wouldn’t be very efficient, would it?
This method is called in onChange
from input
:
onChange={(e) => this.updateInputValue(e.target.value)}
Here, every time a setState
is run, it will trigger a new render
in your component.
There are certain rules to follow for setState
(the fact that setState
is asynchronous, that state updates are merged, etc.). Learn these rules in React's documentation.
And just like you access props
with this.props
, you use this.state
to access the current state.
Do the following to display the input:
render() {
return (
{this.state.inputValue}
onChange={(e) => this.updateInputValue(e.target.value)}
)
}
Back to this
Usually, when you declare a new function, you use the syntax function myFunction()
. So why are we using an arrow function here?
To access your method in your class, you need to bind your function to your class. Arrow functions let you do this implicitly. Problem solved.
Without an arrow function, you’d have to do it explicitly like this: 👇
constructor(props) {
super(props)
this.updateInputValue = this.updateInputValue.bind(this)
this.state = {
inputValue: '',
}
}
updateInputValue (value) {
this.setState({ inputValue: value })
}
this
can be a bit tricky to grasp. That’s one of the reasons why React chose to promote function components, to make things easier at the learning stage. The Medium article “What is ‘this’ in React?” goes back over the key ideas.
Give It a Go!
It’s time to put everything you’ve just learned into practice. To do this, I’ve turned the Card
component into a class component and created an EmptyList
component that lets you signal to the user that the skills list is empty (if they have answered ‘no’ to all questions). Your task is to:
Recreate the favorites system, which adds "⭐️" around the name of the freelancer that we’ve clicked on in
Card
, sticking with a class component.Transform the
EmailInput
component into a function component.Transform the
EmptyList
component into a function component.
As usual, you’ll find the start of the exercise on the branch P4C1-begin, and the solution on P4C1-solution. Over to you! 🚀
Let’s Recap!
Class components appeared at the same time as
class
in JavaScript.A class component is declared with
class ComponentName extends Component
.render
is called every time an update occurs.State is updated with
setState
, to which you pass an object as a parameter.Props and state are accessed with
this.props
andthis.state
.
In the next chapter, we’ll look into another essential aspect of class components: lifecycle methods. You’ll also learn how to call an API in a class component. See you there! 🚀