React error : Unhandled Rejection (TypeError): this.state.categories.map is not a function

Viewed 2687

**I'm trying to learn react from some courses/videos and i got this error couple times and figure it out and solved but right now i can't figure it out. Can someone help me please about this? And also I tried not using "this" but it didn't work This is the error: **

   Unhandled Rejection (TypeError): this.state.categories.map is not a function
    CategoryList.render
    C:/Users/Casper/Desktop/Programlar/React-Dersleri/intro/src/CategoryList.js:31
      28 | <div>
      29 |     <h3>{this.props.info.title}</h3>
      30 |     
    > 31 |     <ListGroup>
         | ^  32 |         {
      33 |             this.state.categories.map(category => (
      34 |                 <ListGroupItem active={category.categoryName===this.props.currentCategory?true:false}
    View compiled
    ▶ 18 stack frames were collapsed.
    (anonymous function)
    C:/Users/Casper/Desktop/Programlar/React-Dersleri/intro/src/CategoryList.js:21
      18 |    }
      19 | 
      20 |    getCategories = ()=>{
    > 21 |        fetch("http://localhost:3000/categories")
         | ^  22 |        .then(response=>response.json())
      23 |        .then(data=>this.setState({categories:data}));;
      24 |    }

here is my code:

import React, { Component } from 'react'
import Navi from './Navi' //bulunduğum klasörden naviiyi import et
import CartSummary from './CartSummary'

import ProductList from './ProductList'
import {ListGroup,ListGroupItem} from 'reactstrap'



export default class CategoryList extends Component {
   
    state = {
        categories: []
    };

    componentDidMount(){
        this.getCategories();
    }

    getCategories = ()=>{
        fetch("http://localhost:3000/categories")
        .then(response=>response.json())
        .then(data=>this.setState({categories:data}));;
    }
    
    render() {
        return (
            <div>
                <h3>{this.props.info.title}</h3>
                
                <ListGroup>
                    {
                        this.state.categories.map(category => (
                            <ListGroupItem active={category.categoryName===this.props.currentCategory?true:false}
                            onClick={()=>this.props.changeCategory(category)} 
                            key={category.id}>
                                {category.categoryName}
                            </ListGroupItem>
                        ))

                    }
                    
                </ListGroup>
                { <h4>{this.props.currentCategory}</h4> }
            </div>
        )
    }
}
1 Answers

Since we can see that you correctly initialize categories with [], we know that the initial state isn't the problem. So we look at the state updates.

The only place you're updating categories is here:

getCategories = ()=>{
    fetch("http://localhost:3000/categories")
    .then(response=>response.json())
    .then(data=>this.setState({categories:data}));;
}

So that means data in that second .then callback isn't an array (because if it were an array, it would have a map method).

Two possible reasons for that:

  1. Whatever JSON your endpoint is returning isn't just an array of categories. You'll need to look in the Network tab of devtools, or put a breakpoint in that .then callback, so see what it actually returns — probably an object with a property that provides the array of categories.

  2. You're not seeing the endpoint response at all, but you are still seeing something that can be parsed as JSON. That could be happening because unless the page you're doing this from is on http://localhost:3000, you're doing a cross-origin ajax call, which will be blocked by the Same Origin Policy unless the response contains the necessary Cross-Origin Resource Sharing headers to allow it. (Your server may also need to handle an OPTIONS HTTP call.)

Re #2, you could be missing the fact you're not getting a successful response because your code is falling prey to the fetch API footgun: You need to check for HTTP success before calling json, because fetch only rejects its promise on network error, not HTTP error. More about that on my anemic little blog, but basically you need to change it to:

getCategories = ()=>{
    fetch("http://localhost:3000/categories")
    .then(response => {                                       // ***
        if (!response.ok) {                                   // ***
            throw new Error("HTTP error " + response.status); // ***
        }                                                     // ***
        return response.json();                               // ***
     })                                                       // ***
    .then(data => this.setState({categories:data}))
    .catch(error => {                                         // ***
        // Handle/report error.                               // ***
    });                                                       // ***
}

The check on response.ok is to check for HTTP success. Also note I added a rejection handler. You always need to handle rejection, or return the promise chain to something that will. (In this case, you need to handle it; componentDidMount won't.)

But again, that alone may not be a fix; I'd say it's likely #1 is the cause, it would be slightly odd if an SOP error happened to be something you could parse as JSON.

Related