React - TypeError: jokes.map is not a function

Viewed 246

I am relatively new to React and I am trying to build a simple app that queries a Chuck Norris API and returns a random joke. When I run my code in the browser I get the following error message:

TypeError: jokes.map is not a function

My theory is 'map' expects an array - which my json data object is not - however I am not positive and unsure as to what / how to tweak my code if that even is the case.

Here is my code:

This is App.js:

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


class App extends Component {
 render() {
    return (
        <Norris jokes={this.state.jokes} />
    )
 }

 state = {
    jokes: []
 };

 componentDidMount() {
    fetch('https://api.chucknorris.io/jokes/random')
        .then(res => res.json())
        .then((data) => {
            this.setState({ jokes: data})
        })
        .catch(console.log)
}

}

export default App;

Here is norris.js:

import React from 'react'

const Norris = ({jokes}) => {
 return (
    <div>
        <center><h1>Norris Jokes</h1></center>
        {jokes.map((joke) => (
            <div class="card">
                <div class="card-body">
                    <h5 class="card-title">{joke.value}</h5>
                </div>
            </div>
        ))}
    </div>
 )
};

export default Norris
4 Answers

The response of https://api.chucknorris.io/jokes/random is not an array

If you want jokes as an array you can change your code like the following:

componentDidMount() {
    fetch('https://api.chucknorris.io/jokes/random')
        .then(res => res.json())
        .then((data) => {
            this.setState({ jokes: [data]})
        })
        .catch(console.log)
}

I checked you API end point https://api.chucknorris.io/jokes/random it does not return any array at all, neither is there any data variable within the object that it returns.

The output is an object with some properties:

fetch('https://api.chucknorris.io/jokes/random')
        .then(res => res.json())
        .then((data) => {
           console.log(data);
        })
        .catch(console.log)

enter image description here

Well, the endpoint doesn't provide an array, but a single joke, wrapped with a JSON like this:

{
categories: [ ],
created_at: "2020-01-05 13:42:20.841843",
icon_url: "https://assets.chucknorris.host/img/avatar/chuck-norris.png",
id: "cFdQaTXXRJS_tIKa_xuqOg",
updated_at: "2020-01-05 13:42:20.841843",
url: "https://api.chucknorris.io/jokes/cFdQaTXXRJS_tIKa_xuqOg",
value: "Chuck Norris can ride a BMX bike while wearing bigass MC Hammer parachute pants."
}

(Kinda lame, this one). So instead of jokes.map(//..., first, rename the prop to joke, and second, leave just

            <div class="card">
                <div class="card-body">
                    <h5 class="card-title">{joke.value}</h5>
                </div>
            </div>

The API you've called is just for ONE random joke, not a list of jokes (Array of Objects).

You can check this code sandbox with a working code.

Or here if you want a list with multiple jokes.

Cheers!

App.js

import React, { useEffect, useState } from "react";    
import Norris from "./Norris";
    
    export default function App() {
      const [state, setState] = useState([]);
    
      useEffect(() => {
        for (let i = 0; i < 5; i++) {
          fetch("https://api.chucknorris.io/jokes/random")
            .then((result) => result.json())
            .then((data) => setState((s) => [...s, data.value]))
            .catch((error) => console.log(error));
        }
      }, []);
    
      return (
        <div style={{ fontFamily: "sans-serif" }}>
          <Norris jokes={state} />
        </div>
      );
    }

Norris.js

import React from "react";

export default function Norris({ jokes }) {

      return (
        <>
          <h1>Norris Jokes</h1>
          <ul>
            {jokes.map((joke, index) => (
              <li key={index}>{joke}</li>
            ))}
          </ul>
        </>
      );
    }
Related