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:
๐
1import React from 'react'
2import ReactDOM from 'react-dom'
3
4const Greeting = (props) => {
5 return <div>Hello, {props.name}</div>
6}
7
8const element = <Greeting name="Irene" />
9
10ReactDOM.render(element, document.getElementById('root'))
11The 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.
1<!DOCTYPE html>
2<html lang="en">
3 <head>
4 <meta charset="UTF-8" />
5 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6 <title>Basic React Component</title>
7 </head>
8 <body>
9 <!-- App is inserted at thr root node -->
10 <div id="root"></div>
11
12 <!-- React CDN -->
13 <script
14 src="https://unpkg.com/react@16/umd/react.development.js"
15 crossorigin
16 ></script>
17 <!-- React-DOM CDN -->
18 <script
19 src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"
20 crossorigin
21 ></script>
22
23 <!-- React Components can be rendered here -->
24 <script async>
25 'use strict'
26
27 const element = React.createElement
28
29 function Greeting() {
30 return element('h4', null, `Hello, World!`)
31 }
32
33 function App() {
34 return element(Greeting)
35 }
36
37 const domContainer = document.querySelector('#root')
38 ReactDOM.render(element(App), domContainer)
39 </script>
40 </body>
41</html>
42The 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
1import React from 'react'
2import ReactDOM from 'react-dom'
3
4function Animal(props) {
5 const element = <h1>My dogs name is {props.name}</h1>
6 return element
7}
8function App() {
9 return <Animal name="Blackflash" />
10}
11const domContainer = document.querySelector('#root')
12ReactDOM.render(<App />, domContainer)
13ES6 Functional Component
1const Animal = (props) => {
2 const element = <h1>My dog name is {props.name}</h1>
3 return element
4}
5const App = () => {
6 return <Animal name="Blackflash" />
7}
8const domContainer = document.querySelector('#root')
9ReactDOM.render(<App />, domContainer)
10Functional 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:
1import React from 'react'
2import ReactDOM from 'react-dom'
3import Name from './Name'
4
5// 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.
6class Animal extends React.Component {
7 constructor(props) {
8 super(props)
9 this.state = {
10 species: [
11 'wolf',
12 'leopard',
13 'elephant',
14 'monkey',
15 'dolphin',
16 'horse',
17 'dog',
18 'squirrel',
19 ],
20 }
21 }
22
23 render() {
24 return (
25 <div>
26 {this.state.species.map((animal) => {
27 return <Name animal={animal} />
28 })}
29 </div>
30 )
31 }
32}
33ReactDOM.render(<Animal />, document.getElementById('root'))
34๐ 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:
1import React from 'react'
2import ReactDOM from 'react-dom'
3
4const Animal = (props) => {
5 const element = <h1>My dogs name is {props.name}</h1>
6 return element
7}
8const App = () => {
9 const [state, setState] = useState('Blackflash')
10 return <Animal name={state} />
11}
12const domContainer = document.querySelector('#root')
13ReactDOM.render(<App />, domContainer)
14We 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:
1import React, { useState } from 'react'
2๐ this line will import the hook, useState.
1const [state, setState] = useState('Blackflash')
2๐ 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.
1return <Animal name={state} />
2๐ 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.