0

Edit//

I suppose my question isn’t so clear. I’m trying to get one park returned when my url points to http://localhost:1233/details/‘${parkcode}’. I’ve defined the param for the url in my results.js file. But I’m having trouble in defining the this.setState in my details.js to render just one result of the park based on the id which also happens to be the park code.

I'm new to React (and possibly to JavaScript, I don't know anymore). I am following a tutorial - instead of using an npm package for an API I decided to branch out and use axios.get() to fetch data from an API. I am able to render the results from a component into the browser, however after adding on reach-router (I assume it's similar to React Router), I am having troubles rendering just one result of my API call as the page I am attempting to build is supposed to only show ONE result based on the ID I have defined.

In my main file, which is Results.js here, I am able to get the data with no problem and include them in my file using JSX and render them. I'm attempting to use the same logic as I did in that page in my Details.js page (which is the page that is supposed to show only one result to the ID in the route).

How I'm using axios in Results.js

componentDidMount() {
    axios
      .get(
        "https://developer.nps.gov/api/v1/parks?stateCode=wa&fields=images&api_key=" +
          `${nps}`
      )

      // https://css-tricks.com/using-data-in-react-with-the-fetch-api-and-axios/
      .then(res =>
        res.data.data.map(park => ({
          description: `${park.description}`,
          fullname: `${park.fullName}`,
          states: `${park.states}`,
          parkcode: `${park.parkCode}`,
          image: `${park.images[0] ? park.images[0].url : "No Image"}`,
          designation: `${park.designation}`
        }))
      )
      .then(parks => {
        this.setState({
          parks
        });
        console.log(parks);
      });
  }

How I'm attempting to use the same logic in Details.js

It's not recognizing park.name even though I did the API call. However, if I hard code park[0].name it works. I have no idea what I'm doing wrong here. It might be an obvious problem but help me.

class Details extends React.Component {
  constructor (props) {
    super(props);

    this.state = {
      loading: true,
    }
  }

  componentDidMount() {
    axios
      .get(
        "https://developer.nps.gov/api/v1/parks?stateCode=wa&fields=images&api_key=" +
          `${nps}`, 
          { id: this.props.id }

      ).then(res => {
        const park = res.data.data.map(park => ({
          description: `${park.description}`,
          fullname: `${park.fullName}`,
          states: `${park.states}`,
          parkcode: `${park.parkCode}`,
          image: `${park.images[0] ? park.images[0].url : "No Image"}`,
          designation: `${park.designation}`
        }))

        console.log(park.name);

        this.setState({
          name: park.name;
          loading: false
        })
      }).catch(err => {
        this.setState({error: err});
      })
  }

I'm expecting the page to recognize the id as defined in the GET request along with the axios, and render the park details in relation to the id. But now, it's doing none of it and I've been stuck on this for forever :(

  • so u trying to show detail of single item when its clicked?? – adel Jun 07 '19 at 07:30
  • Yes! Basically from my Results.js, which I click a certain park after all results are rendered, it will link to the Details page to show details on one park. – Cynthia Wong Jun 07 '19 at 07:34
  • It's look like your API return array of parks. Some thing like this: `[{ name: "park1" }, { name: "park2" }]`. If you want them to return only single park your response data should be look like this `{ name: "park1" }` – vedsmith92 Jun 07 '19 at 07:35
  • here a tutorial same as you are trying to do https://codingthesmartway.com/the-mern-stack-tutorial-building-a-react-crud-application-from-start-to-finish-part-4/ – adel Jun 07 '19 at 07:37
  • @vedsmith92 I'm struggling to figure out how to write that part into the .then() It returns an array when I use axios, but I'm struggling for it to return just one... – Cynthia Wong Jun 07 '19 at 07:38
  • I'm suspicious about the `axios` part where you use to get the `park` details. The second part of `axios.get` is for config, but you are trying to define `id` there. I don't know the API you are trying to use, but if the `id` part is defined at URL then you can use `params` in the config part of `axios.get`. – devserkan Jun 07 '19 at 07:46
  • Also, you don't need `${park.fullName}` where you construct your data at the fetch response. You can use as `park.fullName`. Also, you can use your get url as `https://developer.nps.gov/api/v1/parks?stateCode=wa&fields=images&api_key=${nps}` and if you want to add `id` of the park `https://developer.nps.gov/api/v1/parks?stateCode=wa&fields=images&api_key=${nps}?${this.props.id}` (if API works like that, as I said I'm not sure) and drop the params part at the `axios` config. – devserkan Jun 07 '19 at 07:51
  • @devserkan Thanks for the pointers. The API itself doesn’t come an actual id so I’ve defined the id as the park code, which is part of the api. I’m unable to simply use park.fullName for some reason as it returns undefined when I do a console log on it. – Cynthia Wong Jun 07 '19 at 07:59
  • Sorry, I don't get it. First, you request a park list right? Then you get a list and there is `parkCode` for the items? So, for the details part how should you use the API? What is the endpoint example for this? – devserkan Jun 07 '19 at 08:25
  • I’m using reach-router so the url will look like localhost:1234/details/(whatever the park code is), which is defined in my Results.js page. The API itself doesn’t have an endpoint for id. – Cynthia Wong Jun 07 '19 at 08:30
  • Ok, I think it is something like this: "https://developer.nps.gov/api/v1/parks?parkCode=acad&api_key=INSERT-API-KEY-HERE". So you can use the get request as `https://developer.nps.gov/api/v1/parks?parkCode=${parkCode}&api_key=${nps}` The `parkCode` part should be coming from the parent (Results) component. – devserkan Jun 07 '19 at 08:30
  • Ok, so you are creating a route with `parkCode` right? Then get this data from reach router params, then define your get part as I described above. – devserkan Jun 07 '19 at 08:31

4 Answers4

0

I believe this is the part you are getting wrong

const parks = res.data.data.map(park => ({
  description: `${park.description}`,
  fullname: `${park.fullName}`,
  states: `${park.states}`,
  parkcode: `${park.parkCode}`,
  image: `${park.images[0] ? park.images[0].url : "No Image"}`,
  designation: `${park.designation}`
}))

console.log(parks) // this should display your array of all parks

this.setState({
  parks,
  loading: false
})

displayParks(parks) {
 const allParks = parks.map((park, index) => {
   return <div key={park.parkCode}>{park.fullname}<div>
 })
}


render() {
const { parks } = this.state;

const displayParks = parks && parks.length > 0 ? this.displayParks(parks) : <div>Loading parks</div>
  return (
      <div>{ displayParks }</div>
    );
  }

When you do a .map on an array you are basically creating another array and that is what is returned to your park variable.

So in your render method, you can then loop over every item in parks

Mark
  • 146
  • 7
  • I’ve also tried that. When I do name: park.name in this.setState, it doesn’t return anything. When I bring it down to my render method, I can’t access the state. When I had code it to name: park[0].fullname, it works. – Cynthia Wong Jun 07 '19 at 08:10
  • Yes. You should get that cause you are creating an array when you use `.map`. So ordinarily, if you have only one item in the array then it makes sense to target the index position in the array to get the value – Mark Jun 07 '19 at 09:34
  • @CynthiaWong I just modified the code. I hope this should help – Mark Jun 07 '19 at 09:57
0

You can try this in .then()

let park = res.data.data.map(park => ({
    description: `${park.description}`,
    fullname:    `${park.fullName}`,
    states:      `${park.states}`,
    parkcode:    `${park.parkCode}`,
    image:       `${park.images[0] ? park.images[0].url : "No Image"}`,
    designation: `${park.designation}`
}))
park = park[0]; // convert arrays of parks to single park
console.log(park.fullname); // now you can use `park.fullname`

or this

const park = {
    description: `${res.data.data[0].description}`,
    fullname:    `${res.data.data[0].fullName}`,
    states:      `${res.data.data[0].states}`,
    parkcode:    `${res.data.data[0].parkCode}`,
    image:       `${res.data.data[0].images[0] ? park.images[0].url : "No Image"}`,
    designation: `${res.data.data[0].designation}`
}
console.log(park.fullname); // now you can use `park.fullname`

otherwise do it in API

vedsmith92
  • 579
  • 3
  • 5
  • Thanks! But I suppose what I’m trying to do is that let’s say my data returns 10 items. If I code the [0] when accessing one single element, would it not work on others? I’m trying to find a way where it automatically links to the correct park when clicking on one park on Results. The API doesn’t include a id param so I assume that’s why it’s difficult. – Cynthia Wong Jun 07 '19 at 08:15
  • Okay try this `let park = res.data.data.find(park => park.id === this.props.id).map(park => ({` then remove `park = park[0];` – vedsmith92 Jun 07 '19 at 08:44
0

I think you can first set a state for your responses and then try to show them

same this :

state = {
  result: []
}

componentDidMount() {
  axios
    .get("https://developer.nps.gov/api/v1/parks?stateCode=wa&fields=images&api_key=" +`${nps}`).then((res) => {
      this.setState({result: res.data.data})
  })
}

render(){
  const result = this.state.result.map((el, index) => {
    return(
      //data
    )
  })
  return(
     <div>
       {result}
     </div>
  )
}
Mahdi
  • 1,355
  • 1
  • 12
  • 28
0

There are some unnecessary parts in your code. You don't need to construct your data as you do in your setState part. You are getting park list and it is already a structured data. So, just set your state with the data you get back.

After that, you can map over this data and render the parks with links for React Router. You can use parkCode as your URL param for Link. In Details component you can extract this parkCode and make a new request for park details, then set this to your state.

I'm providing an example.

index.js

import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Results from "./Results";
import Details from "./Details";

const Routes = () => (
  <Router>
    <Switch>
      <Route exact path="/" component={Results} />
      <Route path="/details/:parkCode" component={Details} />
    </Switch>
  </Router>
);

ReactDOM.render(<Routes />, document.getElementById("root"));

Results

import React from "react";
import axios from "axios";
import { Link } from "react-router-dom";

class Results extends React.Component {
  state = {
    parks: [],
    loading: true,
  };

  componentDidMount() {
    axios(
      "https://developer.nps.gov/api/v1/parks?stateCode=wa&fields=images&api_key=LbqZVj21QMimfJyAHbPAWabFaBmfaTZtseq5Yc6t"
    ).then(res => this.setState({ parks: res.data.data, loading: false }));
  }

  renderParks = () =>
    this.state.parks.map(park => (
      <Link to={`/details/${park.parkCode}`} key={park.parkCode}>
        <div>{park.fullName}</div>
      </Link>
    ));

  render() {
    return (
      <div>{this.state.loading ? <p>Loading...</p> : this.renderParks()}</div>
    );
  }
}

export default Results;

Details

import React from "react";
import axios from "axios";

class Details extends React.Component {
  state = { park: "", loading: true };

  componentDidMount() {
    const { match } = this.props;
    axios(
      `https://developer.nps.gov/api/v1/parks?parkCode=${match.params.parkCode}&api_key=${nps}`
    ).then(res => this.setState({ park: res.data.data[0], loading: false }));
  }

  render() {
    return (
      <div>
        {this.state.loading ? <p>Loading...</p> : this.state.park.description}
      </div>
    );
  }
}

export default Details;
devserkan
  • 16,870
  • 4
  • 31
  • 47