简体   繁体   中英

Why is my onClick not working in my React Project

At the moment I am trying to do a website on cruise ships using React in my spare time. This is a link to my repository ad branch that I am currently working on https://github.com/RobertWSON/Personal-ship-project/tree/robs-shipslist-under-cruiselines .

The problem that I am currently having, is that when a Cruise Line Heading is clicked it should expand to show a List of Ships for that Cruise Line and if you click again, it collapses to show just the Cruise Line Heading. At the moment I am a bit confused, as to how I can make this work and I have not got it working just yet.

I have managed so far to get the Cruise Lines displaying as heading buttons on http://localhost:3000/#/cruiselines down the page (I want it across the page though and I believe you can use CSS Flexbox for that)

CruiselistHeaders component:

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 Heading and shipsList and initialize cruiseHeaders as an empty array
        this.state = {
            cruiseHeaders: [],
            shipsList: {isOpen: false}
        } 

        //binding methods for Cruise Line Headers and Handle Click Function
        this.setUpCruiseLines = this.setUpCruiseLines.bind(this),
        this.handleClick = this.handleClick.bind(this)
    }  

    componentDidMount() {
        console.log('cdm')
        this.setUpCruiseLines()
    }

    setUpCruiseLines()  {
        console.log('getcruiselines')
        getCruiseLines()
            .then(res   =>  {

                this.setState({
                    cruiseHeaders: res
                })
            })
    }

    /* There will be Headings for all the Cruise Lines.
        When a Cruise Line Heading is clicked, it goes to ListofShips Component and the Ships List opens up for that Heading.
        When user clicks on a Cruise Line Heading, when a Ships List is open, the Ships List Collapses.*/
        // If we click on Cruise Line Heading, then a shipsList is Opened and we go to ListofShips Component, 
        // else if we click on Cruise Line Heading when shipsList is Open then just the Cruise Line Heading is shown. 

    handleClick(event)   {
        //This handleClick sorts out the changing of state, when Cruise Heading is clicked

        //this handleClick function should only handle the 'isOpen' value in the state.
        //any renders supposedly to be made on the 'render' method instead.
        this.setState(prevState =>  ({
            shipsList:  {
                //will reverse prevState of isOpen.
                isOpen: !prevState.shipsList.isOpen,
            }
        }))
    }

    // This renders at the start when the page loads and also when you close a list

    //We can handle the way you render the component that depends on the this.state.shipsList.isOpen this way.  
    render()    {
        //destructive declaration for isOpen from inside shipsList in the state.
        const {shipsList: {isOpen}} = this.state

        return  (

            <React.Fragment>

                {/* Map the Cruiseline Headings for each Cruise Line to display them on the page
                I am currently get all Cruise Lines for each Ship. I only want each Cruise Line appear once and
                I want to map ship because I need each ship displayed in a List for a Cruise Line when isOpen is True. */}
                {this.state.cruiseHeaders.map (ship =>  {

                return  (
                    <div>
                        {
                            //Line below has Conditonal Operator that displays ShipsList when isOpen is True. 
                            //isOpen && <ListofShips />
                            isOpen == true && <ListofShips/>
                        }

                        <h3 key = {ship.cruise_line}><button onClick = {this.handleClick}>{ship.cruise_line}</button></h3>

                    </div>

                    )

                    })
                }  

            </React.Fragment> 
        )
    }
} 

export default CruiseListHeader

ListofShips component:

import React from 'react'
// import {Link} from 'react-router-dom'
import {getListofShips} from '../api/api'
import { HashLink as Link} from 'react-router-hash-link'


class ListofShips extends React.Component {
    constructor(props){
      super(props)

      this.state = {
        //initialize shipsList as an empty array
        shipsList: []

      }
      this.setUpShips = this.setUpShips.bind(this)
      //this.onHover = this.onHover.bind(this)
    }

    componentDidMount(){
      console.log('cdm')
      this.setUpShips()
    }

    setUpShips() {
      //console.log('getShips')
      console.log('getListofShips')
      //getShips()
      getListofShips()

      .then(res =>  {

        this.setState({
          shipsList: res  
        })
      })
    }

    //This is an onMouseOver event for when you run your mouse over ship in it's list
    // onHover(event)  {

    //   getInitialState

    //   this.setState = {}
    // }

    findShipNames(ship) {

      ship.cruise_line.id = ship.ship_name.id

      return ship.ship_name

      // This function helps display the Cruise Ships based on the Cruise Line

      // Example Code
      //   For Cruise_line === "Carnival"

      //   Ships Displayed =

      //   You have to group the Cruise Lines by their corresponding Ships, somehow

      //   return ship.ship_names

      //   this returns the Ship Names based on there Cruise Lines                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 

    }

  render()  {

    return  (

      <React.Fragment>

      <ol>
        {this.state.shipsList.map (ship => {  

          return   (
              // <li className = "shipsList" key = {ship.cruise_line} onMouseOver={this.onHover}><link smooth to = {`/cruiselines/${ship.cruise_line}#${ship.ship_name}`}>{this.findShipName(ship)}</link></li>
              <li className = "shipsList" key = {ship.cruise_line}><Link smooth to = {`/cruiselines/${ship.cruise_line}#${ship.ship_name}`}>{this.findShipName(ship)}</Link></li>

          )

          // I have to find a key that can display each ship name for each Headings Ships List.
          // The Link will be an Anchor Link to that specific ship, on it's Cruise Line Page.
          // Note: The Cruise Line Page, can have a Review for more than one ship and will be a Review Component.   

            // I would like to do a onMouse Hover function, so that when you run your mouse over ship in list a corresponding image appears

        })
        }
      </ol>

      </React.Fragment>
    )  
  }

}

export default ListofShips

ListofShips component is not working when I click on a Cruise Line and the result I am expecting is for onClick to work in the way that I have described above.

I wish I could put a screenshot here to make it easier to show the errors, here they are below

ListofShips.jsx:75 Uncaught TypeError: Cannot read property 'map' of null
at ListofShips.render (ListofShips.jsx:75)
at finishClassComponent (react-dom.development.js:7873)
at updateClassComponent (react-dom.development.js:7850)
at beginWork (react-dom.development.js:8225)
at performUnitOfWork (react-dom.development.js:10224)
at workLoop (react-dom.development.js:10288)
at HTMLUnknownElement.callCallback (react-dom.development.js:542)
at Object.invokeGuardedCallbackDev (react-dom.development.js:581)
at invokeGuardedCallback (react-dom.development.js:438)
at renderRoot (react-dom.development.js:10366)
react-dom.development.js:9747 The above error occurred in the <ListofShips> component:
    in ListofShips (created by CruiseListHeader)
    in div (created by CruiseListHeader)
    in CruiseListHeader (created by CruiseLines)
    in div (created by CruiseLines)
    in div (created by CruiseLines)
    in CruiseLines (created by Route)
    in Route (created by App)
    in div (created by App)
    in Router (created by HashRouter)
    in HashRouter (created by App)
    in App

Consider adding an error boundary to your tree to customize error handling behavior.

logCapturedError @ react-dom.development.js:9747
warning.js:33 Warning: Can only update a mounted or mounting component. This usually means you called setState, replaceState, or forceUpdate on an unmounted component. This is a no-op.

Please check the code for the ListofShips component.
printWarning @ warning.js:33
ListofShips.jsx:75 Uncaught (in promise) TypeError: Cannot read property 'map' of null
    at ListofShips.render (ListofShips.jsx:75)
    at finishClassComponent (react-dom.development.js:7873)
    at updateClassComponent (react-dom.development.js:7850)
    at beginWork (react-dom.development.js:8225)
    at performUnitOfWork (react-dom.development.js:10224)
    at workLoop (react-dom.development.js:10288)
    at HTMLUnknownElement.callCallback (react-dom.development.js:542)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:581)
    at invokeGuardedCallback (react-dom.development.js:438)
    at renderRoot (react-dom.development.js:10366)

You should add the variable validate part in 2 components' render function before mapping the value.

 {this.state.shipsList.length && this.state.shipsList.map (ship => {  
...

and also

{this.state.cruiseHeaders.length && this.state.cruiseHeaders.map (ship =>  {
...

There are two way to fix it.

Solution 1: (Recommended)

Check the value of the API response before you are going to setState .

CruiselistHeaders component:

setUpCruiseLines()  {
  console.log('getcruiselines')
  getCruiseLines()
    .then(res => {

      this.setState({
        cruiseHeaders: Array.isArray(res) ? res : [] // check the API response
      })
    })
}

ListofShips component:

setUpShips() {
  getListofShips()
    .then(res => {
      this.setState({
        shipsList: Array.isArray(res) ? res : [] // check the API response
      })
    })
}

Solution 2:

You have to validate the state variable before the proceed.

{Array.isArray(this.state.shipsList) && this.state.shipsList.map (ship => {
...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM