Functional Components

A stateless component has no state variables. Stateless components never change state. Stateless components are also referred to as presentational or dumb components. Because they do not have any state variables, stateless components do not need to have a constructor() method.

Functional components:

The features listed above are shown in the code below:

// Class component

class DropDownRegionsList extends React.Component 
{
    constructor(props)
    {
        super(props) 
    }            
    
    
    render()
    {           
        return ( 
                <div>

                    {this.props.region}

                </div>               
        )
    }
}





// Functional component

const DropDownRegionsList = props =>
{        
    return ( 
            <div>

                {props.region} 

            </div>               
    )
}

In this countries example, the DropDownRegionsList component is stateless. Change the code so that this becomes a functional component, as shown here.

State

A hook is a function that allows us to access a React feature. Functional components use the useState() hook to allow the use of state variables. The useState() hook uses array destructering to associate a state variable with a function. The function is used to update the state variable. Each state variable will have its own useState() hook. The useState() hook allows us to set the state variable with an initial value. React ensures useState() is only called once for each functional component. React also ensures that the value saved in a state variable is preserved between calls to a functional component. The useState() hook syntax is shown below:

const [stateVariableName, functionName] = useState(initialValue)

 

useState() hooks replace the this.state variable that is used in class components. As there is no consturctor() method, useState() hooks are declared to be global to a functional component. Each state variable is assigned its own useState() hook, as shown below:

// Class component

class CountriesForm extends React.Component 
{
    constructor(props)
    {
        super(props)
        
        this.state = {selectedCountries:this.props.countries,
                      selectedRegion:"All Regions"}
    } 


    ... 

}



// Functional component

const CountriesForm = props => 
{
    const [selectedCountries, setSelectedCountries] = useState(props.countries) 
    const [selectedRegion, setSelectedRegion] = useState("All Regions")


    ...

}

 

State variables are global to all of the methods inside a functional component (just as the this.state variable is with class components). When using state variables in functional components, we do not need to include the this.state as part of the variable, as shown below:

// Class component

class CountriesForm extends React.Component 
{
    constructor(props)
    {
        super(props)
        
        this.state = {selectedCountries:this.props.countries,        
                      selectedRegion:"All Regions"}
    } 


    ... 


    render()
    {           
        return (  
            <div>
              <h1>{this.state.selectedRegion}</h1>                          
            </div>
        )
    }
}



// Functional component

const CountriesForm = props => 
{
    const [selectedCountries, setSelectedCountries] = useState(this.props.countries) 
    const [selectedRegion, setSelectedRegion] = useState("All Regions")


    ...


    return (  
        <div>
            <h1>{selectedRegion}</h1>                          
        </div>
    )
}

 

A state variable can be modified by calling its associated function. For example, in the code below , setSelectedRegion() is called to change the value of selectedRegion.

// Class component

class CountriesForm extends React.Component 
{
    constructor(props)
    {
        super(props)
        
        this.state = {selectedCountries:this.props.countries,        
                      selectedRegion:"All Regions"}
    } 


    handleRegionsChange = e => 
    {
        this.setState({selectedRegion: e.target.value})
		
        ...

    }

    ... 

}



// Functional component

const CountriesForm = props => 
{
    const [selectedCountries, setSelectedCountries] = useState(props.countries) 
    const [selectedRegion, setSelectedRegion] = useState("All Regions")


    handleRegionsChange = e => 
    {
        setSelectedRegion(e.target.value)
		
        ...

    }


    ...

}

useEffect()

The useEffect() hook allows functional components to access React lifecycle functions, such as componentDidMount() and getDerivedStateFromProps().

The useEffect() hook below will be called every time a functional component is called.

    useEffect(() => 
    {
    
        ...

    })

 

 

The useEffect() method below will be called once. This one call will happen the first time that a functional component is called. This does the same role as the class component's ComponentDidMount() method.

    useEffect(() => 
    {
    
        ...

    }, [])

 

 

The useEffect() method below will be called when props.countries has changed. This does the same role as the class component's GetDerivedStateFromProps() method.

    useEffect(() => 
    {
    
        ...

   }, [props.countries])

Functional Component Web Apps

To convert a web app's class component into a functional component, we need to:

  1. Make the changes described above. In summary, these are:
    1. Replace the React Component inport with the useState imports, as shown below:
      REPLACE
      import React, {Component} from "react"
      
      
      WITH one of the lines of code below (depending on whether the Component has a state)
      import React, {useState} from "react"
      
      
      import React from "react"
      
    2. Replace each state variable in the this.state JSON variable with a useState() hook
    3. Replace componentDidMount() and getDerivedStateFromProps() with useEffect()
    4. Replace all instances of this.setState() with a call to the state variable's associated function that was declared using the useState() hook
    5. Remove the constructor() method
    6. Remove the render() method
    7. Remove all instances of this.state. from the code
    8. Replace all instances of this this.props with props in the code
  2. Export the component, as shown below:
    export const MyComponent = props =>
    {
        ...
    } 
  3. Import component into another file, sourround it with {}, as shown below:
    export const MyComponent = props =>
    {
        ...
    } 
    
    
    To import this into another file
    import {MyComponent} from "./MyComponent"
  4. If the component has PropTypes, these need to be declared outside the functional component's scope, but in the same file, as shown below:
    export const MyComponent = props =>
    {
        ...
    }
    
    
    MyComponent.propTypes = {
        name:PropTypes.string,
        age:PropTypes.number 
    } 
  5. All of the functional component's methods need be declared as const, as shown below:
    export const MyComponent = props =>
    {
        const handleHeaderClick = e =>
        {     
    
            ...
    
        }
    }
  6. Event handlers (such as onClick() for key input) cannot be shared. For example, if we have an email and password input, then:
    Class components allow the same method can be used for all inputs
    handleChange = e =>
    {
        this.setState({[e.target.name]: e.target.value})
    }
    
    
    
    
    Functional components need to use different method for each input
    const handleEmailChange = e =>
    {
        setEmail(e.target.value)
    }
    
    
    const handlePasswordChange = e =>
    {
        setPassword(e.target.value)
    }
  7. When the render() method is removed, any code in the render() method that preceeds the return() should be left where it is, as shown below:
    // Class component
    
    const MyComponent = props =>
    {
    
        ...
    
    
        render()
        {
            const flag = `https://flagcdn.com/256x192/${(props.country.alpha2Code).toLowerCase()}.png`		
            return ( 
                
    ...
    ) } } // Functional component const MyComponent = props => { ... const flag = `https://flagcdn.com/256x192/${(props.country.alpha2Code).toLowerCase()}.png` return (
    ...
    ) }

Modify the login example so that it uses functional components, as shown here.

Modify the countries example so that it uses functional components, as shown here.

Change the code in the react_app from the previous section so that the components are functional components.

"Cars" Worked Example

The full project code for the "Cars" Worked Example that is described below can be downloaded from this link.

In the code below, the class components from the React as a Nodejs app section of these notes are converted into functional components.

Client-Side

client/src/index.js

import React from "react"
import ReactDOM from "react-dom"
import {App} from "./App"


ReactDOM.render(<App/>, document.getElementById(`root`))


// Register the service worker
window.addEventListener('load', () => 
{
    if ("serviceWorker" in navigator) 
    {
        navigator.serviceWorker.register("/serviceWorker.js")
        .then(() => console.log("Service Worker Registered"))
        .catch(err => console.error("Service Worker Registration Failed", err))
    }
}

client/src/App.js

import React from "react"
import {DisplayAllCars} from "./components/DisplayAllCars"


import "bootstrap/dist/css/bootstrap.css"
import "./css/App.css"


export const App = props =>
{
    return (
        <DisplayAllCars/>
    )
}

client/components/DisplayAllCars

import React, {useState, useEffect} from "react"
import {CarTable} from "./CarTable"


export const DisplayAllCars = props =>
{
    const [cars, setCars] = useState([])


    useEffect(() =>
    {
        const cars = [{_id: 0, model: "Avensis", colour: "Red", year: 2020, price: 30000},
            {_id: 1, model: "Yaris", colour: "Green", year: 2010, price: 2000},
            {_id: 2, model: "Corolla", colour: "Red", year: 2019, price: 20000},
            {_id: 3, model: "Avensis", colour: "Silver", year: 2018, price: 20000},
            {_id: 4, model: "Camry", colour: "White", year: 2020, price: 50000}]
        
        setCars(cars)
    }, [])


    return (
    <div className="form-container">
        <div className="table-container">
            <CarTable cars={cars} /> 
        </div>
    </div>
    )
}

client/components/CarTable

import React from "react"
import {CarTableRow} from "./CarTableRow"


export const CarTable = props =>
{
    return (
    <table>
        <thead>
            <tr>
                <th>Model</th>
                <th>Colour</th>
                <th>Year</th>
                <th>Price</th>
            </tr>
        </thead>
    
        <tbody>
            {props.cars.map((car) => <CarTableRow key={car._id} car={car}/>)}
    </tbody>
    </table>
    )
}

client/components/CarTableRow

import React from "react"


export const CarTableRow = props =>
{
    return (
    <tr>
        <td>{props.car.model}</td>
        <td>{props.car.colour}</td>
        <td>{props.car.year}</td>
        <td>{props.car.price}</td>
    </tr>
    )
}

Server-Side

The server-side code does not change, as functional components only apply to the client-side.

 
<div align="center"><a href="../versionC/index.html" title="DKIT Lecture notes homepage for Derek O&#39; Reilly, Dundalk Institute of Technology (DKIT), Dundalk, County Louth, Ireland. Copyright Derek O&#39; Reilly, DKIT." target="_parent" style='font-size:0;color:white;background-color:white'>&nbsp;</a></div>