-1

I try to fetch data by url with react. Server gives to url the list of objects in String in json ( String jsonBooks = new Gson().toJson(books);). I try to get this in react app and render. But it is error: TypeError: Cannot read property 'map' of undefined

import React, { Component } from 'react';
import './App.css';

class App extends Component {

        state = {
            books: []
        }

        componentDidMount() {
            fetch(`http://localhost:8080/app/bookshop/books`,
                {'mode': 'no-cors'},
                {
                headers: {
                    'Access-Control-Allow-Origin': '*',
                    'Access-Control-Allow-Methods': 'GET, POST, HEAD, OPTIONS, PUT, DELETE, PATCH',
                    'Access-Control-Allow-Headers': 'X-Requested-With,content-type',
                    'Access-Control-Allow-Credentials': true
                }})
                .then(res => {
                    const books = res.data;
                    // console.log(res.data);
                    this.setState({books});
                });
        }

    // http://localhost:8080/app/bookshop/books
cd
      render() {
          return (

              <div>
                  <h1>Books:</h1>
                  <table>
                      <tr>
                          <th>Name</th>
                          <th>Author</th>
                          <th>Izdat</th>
                          <th>Genre</th>
                          <th>Price</th>
                          <th>Amount</th>
                      </tr>
                      {this.state.books.map(book =>
                          <tr><th>{book.name}</th>
                              <th>{book.author}</th>
                              <th>{book.izdat}</th>
                              <th>{book.genre}</th>
                              <th>{book.price}</th>
                              <th>{book.amount}</th>
                          </tr>
                      )}
                  </table>
              </div>
          );
      }
}
export default App;

That's function on server (java, jax-rs):

@GET
@Produces({MediaType.APPLICATION_JSON})
@Path("/books")
public String Books() {
    BooksDao booksDao = new BooksDao();
    ArrayList<Book> books = booksDao.getBooks();
    String jsonBooks = new Gson().toJson(books);
    return jsonBooks;
}

What i have to do for fix?

Eugene Trofimov
  • 169
  • 1
  • 2
  • 13
  • What does the commented out line log? – Jonas Wilms Dec 15 '18 at 18:21
  • nothing, i tried to fix it. and forgot delete. edited. – Eugene Trofimov Dec 15 '18 at 18:23
  • I mean the `console.log(res.data)` Is it really an array? or rather a string? – Jonas Wilms Dec 15 '18 at 18:27
  • On react side it's do nothing, server returns `String jsonBooks = new Gson().toJson(books);` , json form of arraylist. – Eugene Trofimov Dec 15 '18 at 18:29
  • 1
    Don’t use mode: 'no-cors'. See the answer at https://stackoverflow.com/questions/43317967/handle-response-syntaxerror-unexpected-end-of-input-when-using-mode-no-cors/43319482#43319482. And don’t try to set any Access-Control-Allow-\* headers for the request from your frontend code. Those headers are all response headers. You need to instead CORS-enable the server for the `http://localhost:8080/app/bookshop/books` endpoint. You frontend code also isn’t getting the response body back in the right way. Responses from the fetch API don’t have a 'data` property. – sideshowbarker Dec 15 '18 at 23:01

2 Answers2

0

For fetch on front side it should be (without cors headers):

 componentDidMount() {
        fetch(`http://localhost:8080/app/bookshop/books`)
            .then(res => res.json())
            .then(books => this.setState({ books }))
            .catch(console.error)
    } 

And cors headers configured on server, in this case I did so:

@GET
@Produces({MediaType.APPLICATION_JSON})
@Path("/books")
public Response Books() {
    BooksDao booksDao = new BooksDao();
    ArrayList<Book> books = booksDao.getBooks();
    String jsonBooks = new Gson().toJson(books);
    return Response
            .status(200)
            .header("Access-Control-Allow-Origin", "*")
            .header("Access-Control-Allow-Headers", "origin, content-type, accept, authorization")
            .header("Access-Control-Allow-Credentials", "true")
            .header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD")
            .header("Access-Control-Max-Age", "1209600")
            .entity(jsonBooks)
            .build();
}
Eugene Trofimov
  • 169
  • 1
  • 2
  • 13
-1

Your render is happening before the fetch promise has resolved. You can add a field to your state called isLoading and set that to true before you make the fetch call. While that is true, display a loading icon, and then once the fetch has returned your data, set isLoading to false and render the books.

Bryan
  • 184
  • 1
  • 4
  • 1
    The state is initialized in the beginning of the class with : state = { books: [] } Even if the promise isnt fullfilled, map shouldn't throw an error on an empty array – Treycos Dec 15 '18 at 18:38