Functional & class components in React 🤔 🤓
React is a framework that allows us to encapsulate code so to make it more reusable. These encapsulated code snippets are called components. They can hold their own logic and state without interfering with what’s going on in the Document Object Model (DOM).
Splitting the website into smaller bite-size components to pass data around allows the code to become reusable and more DRY (Don’t Repeat Yourself). There are two main types of components that you will encounter in React are Functional and Class Components.
At a very high level, React components are basically JavaScript functions that accept props as a parameter and return some React elements that basically describe what should be on the screen:
🛑
import React from 'react'
import ReactDOM from 'react-dom'
const Greeting = (props) => {
return <div>Hello, {props.name}</div>
}
const element = <Greeting name="Irene" />
ReactDOM.render(element, document.getElementById('root'))
The React element in this case here is a <div>
with some text in it. That text uses a props object that has been passed into the component. That props object will pass data down to children components from their parents.
In this instance, the prop that has been passed down is name. Element represents the parent.
Any property you pass into the <Greeting />
component will make it to the props object and be available to use inside Greeting. This makes Greeting super reusable since we can pass in any name we would like into the component.
Functional Components 🤔
Functional components, are basically JavaScript functions. You can use EcmaScript 5 (ES5) or EcmaScript a6 (ES6) syntax when creating React components.
👉 As a rule, React components must be Capitalized to indicate they are indeed components.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Basic React Component</title>
</head>
<body>
<!-- App is inserted at thr root node -->
<div id="root"></div>
<!-- React CDN -->
<script
src="https://unpkg.com/react@16/umd/react.development.js"
crossorigin
></script>
<!-- React-DOM CDN -->
<script
src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"
crossorigin
></script>
<!-- React Components can be rendered here -->
<script async>
'use strict'
const element = React.createElement
function Greeting() {
return element('h4', null, `Hello, World!`)
}
function App() {
return element(Greeting)
}
const domContainer = document.querySelector('#root')
ReactDOM.render(element(App), domContainer)
</script>
</body>
</html>
The code snippet above is an example of how an ES5 functional component looks like when we are not using JSX, but plain vanilla JavaScript. Don’t worry too much about what’s going on in the HTML. Focus on the JavaScript logic: we have an element that’s being created, a function that’s returning something and then that function is being rendered to some sort of ReactDOM.
What JSX – short for JavaScript XML Extension – basically allows us to do is to write HTML in our JavaScript to make it more akin to what we are used to seeing in HTML. In the code example above, we are purely using JavaScript to create our HTML element and then using React’s createElement method to insert an <h4>
into the DOM at the <div id=”root />.
If we were to scale our application – make our application much bigger than it is – this type of writing would become cumbersome fairly quickly. JSX was created basically as syntactic sugar for the React.createElement method and allows us to scale our apps much quicker.
Here is an example bellow of how both an ES5 and an ES6 functional component looks like using JSX:
ES5 Functional Component
import React from 'react'
import ReactDOM from 'react-dom'
function Animal(props) {
const element = <h1>My dogs name is {props.name}</h1>
return element
}
function App() {
return <Animal name="Blackflash" />
}
const domContainer = document.querySelector('#root')
ReactDOM.render(<App />, domContainer)
ES6 Functional Component
const Animal = (props) => {
const element = <h1>My dog name is {props.name}</h1>
return element
}
const App = () => {
return <Animal name="Blackflash" />
}
const domContainer = document.querySelector('#root')
ReactDOM.render(<App />, domContainer)
Functional components were known as stateless components. This means that the main purpose of the component was to be presentational – to look good on the page. Quite often, data was passed to these functional components so that they could display something on the user interface.
With the advent of React v.16 that all changed. We will get into that in a little bit. For right now, just know that functional components exist to receive some sort of data from a parent or from some global object to present something to the client.
Class Components 🤔
Class components are a little more complicated than “just” a JavaScript function. Class components in React borrow the concept of the ES6 JavaScript class. The structure of a class in JavaScript is pretty much an object that has attributes and methods associated with it. We use the this
keyword to access an instance of that object and interact with it.
In React, for the most part, the structure is the same. React class components are an instance of an object and this object has what we call state. At a high level, state is just data that the component holds – think of it just as another way to set up attributes and bind it to the class. As a developer you can then do whatever you would like with that data: present it on screen, pass it around to other components, use it to do other logic, etc.
An ES6 class component is always capitalized, just like with functional components – that is a React rule so that the transpiler knows it’s a component. Because we are inheriting the component structure from React itself, we have to extend React’s Component class. Inside that block of code is where we will put our state:
import React from 'react'
import ReactDOM from 'react-dom'
import Name from './Name'
// the 'this' keyword needs to be used when we are talking about an instance of a class. So it will go in front of the methods and state when referring to it in the render method.
class Animal extends React.Component {
constructor(props) {
super(props)
this.state = {
species: [
'wolf',
'leopard',
'elephant',
'monkey',
'dolphin',
'horse',
'dog',
'squirrel',
],
}
}
render() {
return (
<div>
{this.state.species.map((animal) => {
return <Name animal={animal} />
})}
</div>
)
}
}
ReactDOM.render(<Animal />, document.getElementById('root'))
🛑 State is an object full of properties and values. The state in the snippet has a property of species and its value is an array full of animals. To refer to or interact with this array at all, we use this.state.species
.
The purpose of the class component above is to pass the name of the animal down to the <Name />
component. The <Name />
component’s job is to do something with that data when it gets it.
👉 The Animal’s state will become part of a props object when it is passed to a functional or other class component. It will be able to be accessed in child components as long as it keeps getting passed down.
🛑 Remember:
👉 ⚠️ In React, data flows down from parent component to child component only one level. You must pass it down another level if you need the data in the parent’s grandchild component.
Another feature of class components in React is that we have access to and use lifecycle methods to keep track of the state.
🛑 React lifecycles typically have three phases:
👉 They are created (Mounting), they live (Update) and they die (Unmounting).
There are methods to access and/or change state at each of the stages of the lifecycle method:
👉 ComponentDidMount() – this is the lifecycle method where we would make AJAX requests/network requests to initialize state. Use this.setState() to load your retrieved data into state. ComponentDidUpdate() – any updates to state occur here after a user has interacted with the application.
👉 ComponentDidUnmount() – this is a cleanup function that occurs when the component unmounts. It’ll take care of timers, AJAX requests, etc. There are more lifecycle methods than these – those listed are just the main ones. Please see React documentation for more information about these methods.
👉 Finally, in contrast to the functional component’s return statement, class components use a render() method
. This method is invoked after ReactDOM.render()
passes the Animal component and React calls its constructor.
👉 State is then initialized and then the render method is called to actually put content on the screen.
remember that functional components prior to React v. 16 were primarily presentational – they didn’t handle state – they just displayed it. Class components detailed all the state logic for us and passed the information down to other components as props.
Functional Components and useState()
In 2018, React introduced the idea of React Hooks. Hooks are a clean and concise way of utilizing lifecycle methods and state inside a Functional Component.
Most everything is very similar to everything we have covered so far. We have some state, we need to do stuff with it, and we need to pass it somewhere else. The main objectives are the same. The syntax is much cleaner – it just requires a little bit of getting used to.
❗️ My advice is to practice, and get some repetitions for class components, really understand how the data flow works in React.
Let’s start out with the code that we had before:
import React from 'react'
import ReactDOM from 'react-dom'
const Animal = (props) => {
const element = <h1>My dogs name is {props.name}</h1>
return element
}
const App = () => {
const [state, setState] = useState('Blackflash')
return <Animal name={state} />
}
const domContainer = document.querySelector('#root')
ReactDOM.render(<App />, domContainer)
We have two components, one Animal
and one App
. It looks like App is returning Animal and passing in a prop called name with a value of “Blackflash”.
Believe it or not, there isn’t much we have to do to convert this into some stateful logic. Let’s take a look at the <App>
component. We’re are going to follow these steps:
import React, { useState } from 'react'
👉 this line will import the hook, useState
.
const [state, setState] = useState('Blackflash')
👉 this line initializes our state. State and setState
here are arbitrary words. You can name these whatever you’d like. It’s customary to name them after what the value is.
👉 state is the actual state. The initial state is inside the parentheses in useState()
.
👉 setState
is similar to this.setState()
.
ℹ️ ❗️This is the method that will change the state as you journey through your application.
return <Animal name={state} />
👉 Replace “Blackflash” with {state}
. When writing JavaScript in JSX, use curly braces.
👉 Curly braces allows to pass in the variable.
One of the features that defined class components, the React Lifecycle, with its various methods, is skimmed down to one basic hook that encapsulates all the methods.
🛑 The useEffect()
hook can mount, update and unmount the React component it’s in.
Let's Sum Up:
In React v. 16, functional components were purely used as a presentational view layer with no use of state except as props that were passed down from class components.
- Class components held all of the state of the application and passed the data around.
- Class components utilized life cycle methods that mounted, updated and unmounted our React component.
- And about also React Hooks, a new pattern released by React in version 16, allows for functional components to be stateful and have it’s own version of lifecycle methods.