Convert React Class to a Functional Component

Viewed 96

On my older repository, I have used Class Components with the following CruiseListHeader component code, as an example (used for showing Cruise Buttons).

    import React from 'react';
    import {getCruiseLines} from '../api/api'
    import ListofShips from './ListofShips'
    
    
    class CruiseListHeader extends React.Component {
        constructor(props)  {
            super(props)
    
            //setting intial state for Cruise Headings and initialize cruiseHeaders as an empty array
            this.state = {
                cruiseHeaders: []
            } 
            
            //binding methods for setting up Cruise Line Headings
            this.setUpCruiseLines = this.setUpCruiseLines.bind(this)
        }  
    
        componentDidMount() {
            console.log('cdm')
            this.setUpCruiseLines()
        }
    
        setUpCruiseLines()  {
            console.log('getcruiselines')
            getCruiseLines()
                .then(res   =>  {
    
                    this.setState({
                        cruiseHeaders: res
                    })
                })
        }
         
        render()    {
            return  (

            <React.Fragment>

                {/* There will be Headings for all the Cruise Lines. */}
                {/* Map the Cruiseline Headings for each Ship to display them on the page. I want to map ship, because I need each ship displayed in a List, when Cruise Line Heading is clicked. */}

            <div className = "cruiseContainer">

                {this.state.cruiseHeaders.map (ship =>  {
                
                    return (

                    <div key={ship.cruise_line}>

                        {/* ListofShips component needs cruise_line, because when user clicks on Cruise Line Heading button,
                            we need to fetch ships that belongs to that particular cruiseline. */}
                        {/* We need to render multiple ListofShips components, with one for each cruiseline */}
                        <ListofShips cruise_line={ship.cruise_line}></ListofShips>

                    </div>
                    )

                })}

            </div>      

            </React.Fragment> 
        )
    }
} 

export default CruiseListHeader

Please note that this is all related to a Cruise Lines Page shown below, that has a main CruiseLines.jsx component with the CruiselistHeader.jsx mentioned above imported into it.

Cruise Buttons on Cruise lines Page

Now I want to start the change by converting this React Class Component into a Functional one.

This is what I have for my CruiseListHeader as a Functional Component, so far.

Please note that ListofShips is now called ShipsList in this new repository.

import React, { useEffect, useState} from 'react'
import {getCruiseLines } from '../api/api'
import ShipsList from './ShipsList'

function CruiseListHeader() {

    // Declare cruiseHeaders State variable 
    const [cruiseHeaders] = useState({
        
    })

    useEffect (() =>    {

    // Note: This was the original ComponentDidMount that took Binding this.setUpCruiseLines()
    // Now it is coming from the CruiseListHeader.js useEffect to the DOM    
    }
    )
    

    return  (
        <>
        <div>    
            <div key={ship.cruise_line}>
                <ShipsList cruise_line={ShipsList.cruise_line}></ShipsList>            
            </div>
        </div>    
        </>
    )
}

export default CruiseListHeader

What I am wanting to understand, is how does the Functional Component handle the state from my api, the binding and the mapping that I was previously using in my Class Component ?

If anyone has any ideas of how I can handle this, then that would be of great valuable help thanks.

2 Answers

You're ignoring the setter for the state, your useState line should be:

const [cruiseHeaders, setCruiseHeaders] = useState({});

Then you'd use that setCruiseHeaders function to set the state:

useEffect (() => {
  getCruiseLines()
    .then(res => {
      setCruiseHeaders({
        cruiseHeaders: res
      })
    });
}, []); // Make sure to also pass an array here, or you'll be triggering this effect on every render

As an aside, you probably meant to initialize your state value to an array instead of an object:

const [cruiseHeaders, setCruiseHeaders] = useState([]);

Since the "cruise headers" data in your original code was an array.

import React, { useEffect, useState} from 'react'
import {getCruiseLines } from '../api/api'
import ShipsList from './ShipsList'

function CruiseListHeader() {

    // Declare cruiseHeaders variable and set it's array using useState 
    const [cruiseHeaders, setCruiseHeaders] = useState([]);    
    
    // Note: This was the original ComponentDidMount that took Binding this.setUpCruiseLines()
    // Now it is coming from CruiseListHeader.jsx useEffect to the DOM    

    useEffect (() => {
        getCruiseLines()
          .then(res => {
            setCruiseHeaders({
              cruiseHeaders: res
            })
          });
      }, []); // Make sure to also pass an array here, or you'll be triggering this effect on every render

    return  (
        <>

        {/* <div className = "cruiseContainer"> I don't think I need this because I am using Tailwind CSS*/}
        {/* When I use Sass in my next final repo I may not need a div either */}

            {cruiseHeaders.map (ship => {

                // return  ( Do not need return here, I think

                    <div key = {ship.cruise_line}>

                        {/* ListofShips component needs cruise_line, because when user clicks on 
                        Cruise Line Heading button, we need to fetch ships that belongs to that particular 
                        cruiseline. */}
                        {/* We need to render multiple ShipsList components, with one for each cruiseline */}
                        <ShipsList cruise_line={ship.cruise_line}/>

                        {/* </ShipsList>  I think I don't I need this closing tag*/}
                    </div>

                // ) I think, I do not need return closing bracket here
            })}
        {/* </div> I think, I do not need closing div from cruiseContainer here*/}

        </>
    )
}

export default CruiseListHeader
Related