LoF10
LoF10

Reputation: 2127

Accessing Props to Map over Array in React Child?

Been working with react and still trying to grasp all the concepts. Could use help to understand how I can get this to work. I want to pass an array as props to another react component to map over. Can someone point out what I am doing wrong here? I can map through the array as a function but not inside render:

App.js

import React, { Component } from 'react';
import Leaf from './components/Leaf';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      viewport: {
        height: "100vh",
        width: "100vw",
        latitude: 40.7128,
        longitude: -74.0060,
        zoom: 10
      },
      latitude: 40.7128,
      longitude: -74.0060,
      zoom: 10,
      stations: [],
      selectedStation: null,
      userLocation: {}
    };
  }

  componentDidMount() {
    fetch('https://gbfs.citibikenyc.com/gbfs/en/station_information.json')
    .then(res => res.json())
    .then(res=>
      this.setState({stations: res}))
  }

  checkData=()=>{
    console.log(this.state.stations)
    this.state.stations.data.stations.map(e=>{
      console.log(e)
    })
  }

  render() {

    return (
      <div>
          <button onClick={this.checkData}>click me</button>   
          <Leaf 
            viewport={this.state.viewport}
            stations={this.state.stations}/>
      </div>
    );
  }
}

export default App;

leaf.js

import React, {Component} from 'react';
import { Map, TileLayer, Marker, Popup } from 'react-leaflet';
import { Button } from 'react-bootstrap';

class leaf extends Component {

    checkData=()=>{
        this.props.stations.data.stations.map(e=>{
          console.log(e)
        })
      }



    render() {

        const markers = this.props.stations.data.stations.map((station) =>
        <Marker 
          position={[station.lat, station.lon]}
          onClick={this.markerClick.bind(this,station)}>
          <Popup>
          </Popup>
        </Marker>
              );


        const position = [this.props.viewport.latitude, this.props.viewport.longitude]
        //const position = [40.7484, -73.9857]
        return (
            <div>
                <button onClick={this.checkData}>check props</button>
                <Map center={position} zoom={14}>
                    <TileLayer
                        attribution='&amp;copy <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
                        url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
                    />
                    {markers}
                    <Marker position={position}>
                        <Popup>
                            A pretty CSS3 popup. <br /> Easily customizable.
                        </Popup>
                    </Marker>
                </Map>  
            </div>
        );
    }
}

export default leaf;

In the past I've done something like :

const Search = ( props ) => {
return (
)
}

But I am looking to understand how to make this work using class leaf extends Component . Appreciate the help.

Data Structure for Reference

    {
    last_updated: 1572631066,
    ttl: 10,
    data: {
    stations: [
    {
    station_id: "237",
    external_id: "66db3c29-0aca-11e7-82f6-3863bb44ef7c",
    name: "E 11 St & 2 Ave",
    short_name: "5746.04",
    lat: 40.73047309,
    lon: -73.98672378,
    region_id: 71,
    rental_methods: [
    "CREDITCARD",
    "KEY"
    ],
    capacity: 39,
    rental_url: "http://app.citibikenyc.com/S6Lr/IBV092JufD?station_id=237",
    electric_bike_surcharge_waiver: false,
    eightd_has_key_dispenser: false,
    eightd_station_services: [
    {
    id: "e73b6bfb-961f-432c-a61b-8e94c42a1fba",
    service_type: "ATTENDED_SERVICE",
    bikes_availability: "UNLIMITED",
    docks_availability: "NONE",
    name: "Valet Service",
    description: "Citi Bike Station Valet attendant service available",
    schedule_description: "",
    link_for_more_info: "https://www.citibikenyc.com/valet"
    }
    ],
    has_kiosk: true
    },
    {
    station_id: "281",
    external_id: "66db5fae-0aca-11e7-82f6-3863bb44ef7c",
    name: "Grand Army Plaza & Central Park S",
    short_name: "6839.10",
    lat: 40.7643971,
    lon: -73.97371465,
    region_id: 71,
    rental_methods: [
    "CREDITCARD",
    "KEY"
    ],
    capacity: 66,
    rental_url: "http://app.citibikenyc.com/S6Lr/IBV092JufD?station_id=281",
    electric_bike_surcharge_waiver: false,
    eightd_has_key_dispenser: true,
    eightd_station_services: [
    {
    id: "32461582-cd1e-4ecf-a5ea-563593fa7009",
    service_type: "ATTENDED_SERVICE",
    bikes_availability: "UNLIMITED",
    docks_availability: "NONE",
    name: "Valet Service",
    description: "Citi Bike Valet Attendant Service Available",
    schedule_description: "",
    link_for_more_info: "https://www.citibikenyc.com/valet"
    }
    ],
    has_kiosk: true
    }
]
}
}

Attempts

So here I changed the const markers to mirror the conosle.log from checkData:

const markers =  this.props.stations.data.stations.map((station) =>
        <Marker 
          position={[station.lat, station.lon]}
          onClick={this.markerClick.bind(this,station)}>
          <Popup>
          </Popup>
        </Marker>
              );

I get the following error:

TypeError: Cannot read property 'stations' of undefined

When I remove the markers variable and click checkData, notice that it has not issue mapping over and console logging the object:

enter image description here

Upvotes: 2

Views: 458

Answers (2)

LoF10
LoF10

Reputation: 2127

Camilo was correct and I needed to make sure data was available for render. Below is the working code:

App.js

//import logo from './logo.svg';
//import './App.css';

import React, { Component } from 'react';
import Leaf from './components/Leaf';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      viewport: {
        height: "100vh",
        width: "100vw",
        latitude: 40.7128,
        longitude: -74.0060,
        zoom: 10
      },
      latitude: 40.7128,
      longitude: -74.0060,
      zoom: 10,
      stations: [],
      showStations: false,
      selectedStation: null,
      userLocation: {}
    };
  }

  componentDidMount() {
    const request = async()=> {
      await fetch('https://gbfs.citibikenyc.com/gbfs/en/station_information.json')
      .then(res => res.json())
      .then(res=>
        this.setState({stations: res, showStations: true}))
    }
    request();
  }

  checkData=()=>{
    console.log(this.state.stations)
    this.state.stations.data.stations.map(e=>{
      console.log(e)
    })
  }

  render() {

    return (
      <div>
          <button onClick={this.checkData}>click me</button>   
          <Leaf 
            viewport={this.state.viewport}
            stations={this.state.stations}
            showStations={this.state.showStations}/>
      </div>
    );
  }
}

export default App;

Leaf.js

import React, {Component} from 'react';
import { Map, TileLayer, Marker, Popup } from 'react-leaflet';
//import './leaf.css'
//import InfoBox from './InfoBox';
import { Button } from 'react-bootstrap';
//import Search from './Search';
//import Match from './Match';
//import Modal from './Modal';
//import L from 'leaflet';
//import Routing from "./RoutingMachine";
//import { Row, Col, Grid, Container } from 'react-bootstrap';
//import ErrorBoundary from '../ErrorBoundary/ErrorBoundary'



class Leaf extends Component {


    checkData=()=>{
        this.props.stations.data.stations.map(e=>{
          console.log(e)
        })
      }

    render() {

        let markers =null;
        if(this.props.showStations) {
        markers = (
            <div>
            {
            this.props.stations.data.stations.map((station) =>
                <Marker 
                    position={[station.lat, station.lon]}>
                </Marker>
                )
            }
            </div>
        )
        }


        const position = [this.props.viewport.latitude, this.props.viewport.longitude]
        //const position = [40.7484, -73.9857]
        return (
            <div>
                <button onClick={this.checkData}>check props</button>
                <Map center={position} zoom={14}>
                    <TileLayer
                        attribution='&amp;copy <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
                        url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
                    />
                      {markers}
                    <Marker position={position}>
                        <Popup>
                            A pretty CSS3 popup. <br /> Easily customizable.
                        </Popup>
                    </Marker>
                </Map>  
            </div>
        );
    }
}

export default Leaf;

Upvotes: 0

Chris B.
Chris B.

Reputation: 5763

I'd doublecheck that you're accessing the correct data properties, as in your function you're calling this.props.stations.data.stations.map, but in your render you're calling this.props.stations.data.map, so one must be incorrect.

Also, class components should be PascalCase, i.e. capitalized.

Upvotes: 2

Related Questions