Darren
Darren

Reputation: 2290

Map API data with this.state in ReactJS

How can I get the below API to display each date and rate from the JSON in a list? I have gone through the JSON and from what I can see I have mapped it correctly, yet {this.state.calendarDay} is undefined in the console, causing the .map to be undefined.

What am I missing?

import React, { Component } from 'react';

class Rates extends Component {
    constructor(props) {
        super(props);
        this.state = {
            fetched: false,
            isLoaded: false,
            calendarDay: []
        }
    }

    componentWillMount(){
        this.setState({
          loading : true
        });

        const proxyurl = "https://cors-anywhere.herokuapp.com/";
        const url = `//apac.littlehotelier.com/api/v1/properties/chatelainevalleydirect/rates.json`;
        fetch(proxyurl + url).then(res => res.json())
        .then(res =>{
            this.setState({
                calendarDay : res.rate_plans,
                loading : true,
                fetched : true
            });
        });
    }

    render() {
        const {fetched, loading, calendarDay} = this.state;
        let content;
        if(fetched){
          content =
          <ul>
            {calendarDay.rate_plan_dates.map((dailyRates,index) => <li key={index+1}>{dailyRates.date} - ${dailyRates.rate}</li>)}
          </ul>;
        }
        else if(loading && !fetched){
            content = <p> Loading ...</p>;
        }
        else{
          content = <div/>;
        }
        return (
          <div>
            {content}
          </div>
        );
    }
}

export default Rates;

Upvotes: 2

Views: 592

Answers (3)

Subhanshu
Subhanshu

Reputation: 2146

Call the api in ComponentDidMount and also I have split the code based on plan name.Hope this helps.

class Rates extends React.Component {
constructor(props) {
    super(props);
    this.state = {
        loading: false,
        calendarDay: []
    }
}

componentDidMount(){
    this.setState({
      loading : true
    });
            var self = this;
    const proxyurl = "https://cors-anywhere.herokuapp.com/";
    const url = `//apac.littlehotelier.com/api/v1/properties/chatelainevalleydirect/rates.json`;
    fetch(proxyurl + url).then(res => res.json())
    .then(res =>{
     console.log(res)
        self.setState({
            calendarDay : res,
            loading : false,
        });
    });
}

render() {

    return (
      <div>
        {this.state.loading &&
            <p>Loading...</p>
        }
        {(this.state.calendarDay.length == 0 && !this.state.loading) && 
            <p>No data Available</p>
        }
        {this.state.calendarDay.length > 0 &&
            <ul>
            {this.state.calendarDay.map(function(i,index){
                return(
                <li key={index}>
                {i.rate_plans.map(function(j,index){
                  return(
                    <ul key={index}> <p>{j.name}</p>
                      {j.rate_plan_dates.map(function(dailyRates,index){
                          return(
                            <li key={index+1}>{dailyRates.date} - ${dailyRates.rate}</li>
                        )
                      })}
                    </ul>
                  )
                })}
                </li>
                )
          })
            }
          </ul>
        }
      </div>
    );
}
}

Upvotes: 0

Manoz
Manoz

Reputation: 6587

Couple of problems I see

First you specified that calendarDay: [] is an array and at the same time you are mapping through its property which makes no sense

{calendarDay.rate_plan_dates.map...

So what it is currently looks like array[].property is always undefined.

Since calendarDay is an array so it will not have a direct property rate_plans.

one of your comments

when console.log(this.state.calendarDay) is placed after setState I get calendarDay is undefined. Cheers again for you help - any suggestions?

if you put log immediately after setting up the state it will not work as expected because state in react works in asynchronous mode.

verify by the callback -

this.setState({
      calendarDay: res,
      loading: true,
      fetched: true
    }, () => console.log(this.state.calendarDay));

if it is still undefined then res.rate_plans is coming undefined from the server.

else do the followings as @Kamran Nazir already mentioned in his answer.

{calendarDay.map((item, k) => <div key={k}>
            {item
              .rate_plans
              .map((ratePlans, ratePlanKey) => <ul key={ratePlanKey}>
                {ratePlans
                  .rate_plan_dates
                  .map((dailyRates, index) => <li key={index + 1}>{dailyRates.date}
                    - ${dailyRates.rate}</li>)}
              </ul>)}
          </div>)}

Upvotes: 0

Kamran Nazir
Kamran Nazir

Reputation: 692

You are using nested array of objects. Here's a rough solution that I made.

this.setState({
            calendarDay : res, // change this
            loading : true,
            fetched : true
        });

inside the render function

if(fetched){
      content =
        <div>
            {calendarDay.map((item, k) =>
          <div key={k} >
            {item.rate_plans.map((ratePlans, ratePlanKey)=>
                <ul key={ratePlanKey}>
                    {ratePlans.rate_plan_dates.map((dailyRates,index) => <li key={index+1}>{dailyRates.date} - ${dailyRates.rate}</li>)}
                </ul>
            )}
          </div>
            )}
        </div>
    }

Upvotes: 1

Related Questions