How to fetch data from api and pass it as props

I'm in the learning phase of react and trying to figure out how to fetch api data and pass it as props, so i created my own api file in github and tried to fetch the api data from it, here is the link below:

https://raw.githubusercontent.com/faizalsharn/jokes_api/master/jokesData.js

for some reason the data is not being fetched from the api and not being passed as props could someone, please explain me where im doing wrong, forgive me if there is any obvious mistakes here im still in beginner level

App.js

import React, {Component} from "react"
import Joke from "./joke"

class App extends Component {
    constructor() {
        super()
        this.state = {
            loading: false,
            jokeComponents: []
        }
    }

    componentDidMount() {
        this.setState({loading: true})
        fetch("https://raw.githubusercontent.com/faizalsharn/jokes_api/master/jokesData.js")
            .then(response => response.json())
            .then(data => {
                this.setState({
                    loading: false,
                    jokeComponents: data.jokesData.map(joke => <Joke key={joke.id} question={joke.question} punchLine={joke.punchLine} />)  
                })
            })
    }

    render() {
        const text = this.state.loading ? "loading..." : this.state.jokeComponents
        return (
            <div>
                {text}   
            </div>
        )
    }
}

export default App

joke.js

import React from "react"

function Joke(props) {
    return (
        <div>
            <h3 style={{display: !props.question && "none"}}>Question: {props.question}</h3>
            <h3 style={{color: !props.question && "#888888"}}>Answer: {props.punchLine}</h3>
            <hr/>
        </div>
    )
}

export default Joke

Upvotes: 4

Views: 5441

Answers (3)

Abhinav Kinagi
Abhinav Kinagi

Reputation: 3801

To show content after it is being loaded and hide the loading indicator, use a function that simulates an async action and after that the data will be shown. I've shown this example with another API, as there is a problem with your API. I hope you fix that. Also set headers to allow cross domain data access.

App.js

import React, { Component } from "react";
import ReactDOM from "react-dom";
import Joke from "./Joke";

class App extends Component {
  constructor() {
    super();
    this.state = {
      loading: true,
      jokeComponents: []
    };
  }

  componentDidMount() {
    fetch("https://jsonplaceholder.typicode.com/posts",{
      headers: { crossDomain: true, "Content-Type": "application/json" }
    }).then(response=>response.json())
      .then(data => {
        console.log(data);
        this.setState({
          jokeComponents: data.map(joke => (
            <Joke
              key={joke.id}
              question={joke.title}
              punchLine={joke.body}
            />
          ))
        });
      });
      demoAsyncCall().then(() => this.setState({ loading: false }));
  }

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

    if(loading) { 
      return "loading...";
    }
    return <div>{this.state.jokeComponents}</div>;
  }
}

function demoAsyncCall() {
  return new Promise((resolve) => setTimeout(() => resolve(), 2500));
}


ReactDOM.render(<App />, document.getElementById('root'));

The working code of the same is set up in CodeSandbox below:

Edit React example

Upvotes: 1

Mark
Mark

Reputation: 989

Gideon Arces correctly explained your first bug, but there's more to do:

  • You need to format your .json file as json, which is not the same as javascript. For example, while this is javascript {id: 1, question: "?"} it's not json. Json must be formatted like this: {"id": "1", "question":"?"} with quotes around the property names.

  • You need to do your data fetching in your componentDidMount and call setState there

  • You need to pull data from the state and render your components in render(). Typically this is done by creating an array of components and then putting them into the return inside {}. See more on that here: Lists and Keys

  • It's always a good idea to start with dummy data hardcoded into your component before you try to combine your ui with your api. See below in the componentDidMount() where I hardcoded some jokes. This way you can isolate bugs in your ui code from those in your network/api code.

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

    componentDidMount() {
        // this.setState({loading: true})
        //     fetch("https://raw.githubusercontent.com/faizalsharn/jokes_api/master/jokesData.js")
        //         .then(response => response.json())
        //         .then(data => {
        //             console.log(data);
        //             this.setState({
        //                 loading: false,
        //                 jokes: data
        //             })
        //         })
        const json = `[
      {
          "id": "1",
          "question": "?",
          "punchLine": "It’s hard to explain puns to kleptomaniacs because they always take things literally."
      },
      {
          "id": "2",
          "question": "What's the best thing about Switzerland?",
          "punchLine": "I don't know, but the flag is a big plus!"
      }
  ]`;

        const jokes = JSON.parse(json);
        this.setState({ jokes });
    }

    render() {
        const jokeComponents = this.state.jokes.map(joke => (
            <Joke key={joke.id} question={joke.question} punchLine={joke.punchLine} />
        ));
        console.log(jokeComponents);
        const text = this.state.loading ? "loading..." : jokeComponents;
        return <div>Text: {text}</div>;
    }
}

function Joke(props) {
    console.log("joke props:", props);
    return (
        <div>
            <h3 style={{ display: !props.question && "none" }}>
                Question: {props.question}
            </h3>
            <h3 style={{ color: !props.question && "#888888" }}>
                Answer: {props.punchLine}
            </h3>
            <hr />
        </div>
    );
}

Upvotes: 0

Gideon
Gideon

Reputation: 71

I check the API, and found out that it is not working properly when the response.json() is being invoke in the fetch API.

And this is due to the error in the response of the API. You just need to return a bare array, and not return the API with a variable.

enter image description here

For reference, please check the return json of the Jsonplaceholder Fake API. https://jsonplaceholder.typicode.com/posts

Hope this fix your error.

Also, for the state of the jokeComponents, please just have the array passed in the response, and not manipulate the data. Just use the .map for the jokeArray in the render() function if the state is changed. :)

Upvotes: 2

Related Questions