0

There is the sample code that I received this error:

I'm getting this error via Search component. I assume , I'm overriding state.books to the object. However, can't find the solution as a junior. How should I update state.books correctly ?

GetData.js

import React, { Component } from 'react';
import Search from '../pages/Search';
import CreateForm from '../components/CreateForm';
import axios from 'axios';

class GetData extends Component {
  state = {
    books: []
  };

  componentDidMount(){
    axios.get('/api/books/').then(res => {
      console.log(res);
      this.setState({
        books: res.data
      });
    });
  }

  addBook = (book) => {
    let books = [...this.state.books, book]
    axios.post('/api/books/', books).then(res => {
      console.log(res);
      this.setState({
        books: res.data
      });
    });
  }

  render() {
    return (
      <div className="app">
        <Search books={this.state.books} />
        <CreateForm addBook={this.addBook}/>
      </div>
    );
  }
}
export default GetData;

Search.js

import React from 'react';

const Search = ({books}) => {
  const bookElements = books.map(book => {
    return(
      <div className="book-data" key={book._id}>
        <div>{book.author}</div>
        <div>{book.publication}</div>
        <div>{book.publisher}</div>
      </div>
    )
  })

  return(
    <div className="book-elements">
      {bookElements}
    </div>
  )
}

export default Search;

CreateForm.js

import React, {Component} from 'react';

class CreateForm extends Component {
  state = {
    author: null,
    publication: null,
    publisher: null,
  };

  handleChange = (e) => {
    this.setState({
      [e.target.id]: e.target.value
    })
  }

  handleSubmit = (e) => {
    e.preventDefault();
    this.props.addBook(this.state);
  }

  render () {
    return (
      <div>
        <form onSubmit={this.handleSubmit}>
          <label htmlFor="author">Author:</label>
          <input type="text" id="author" onChange={this.handleChange}/>
          <label htmlFor="publication">Publication:</label>
          <input type="text" id="publication" onChange={this.handleChange}/>
          <label htmlFor="publisher">Publisher:</label>
          <input type="text" id="publisher" onChange={this.handleChange}/>
          <button>Submit</button>
        </form>
      </div>
    )
  }
}

export default CreateForm;

api.js

const express = require('express');
const router = express.Router();
const Book = require('../models/book');

router.get('/books', function(req, res, next){
    Book.find({}).then(function(books){
        res.send(books);
    }).catch(next);
});

// Add a new book to the database.
router.post('/books', function(req, res, next){
    Book.create(req.body).then(function(book){
        res.send(book);
    }).catch(next);
});

// Update a book in the database.
router.put('/books/:id', function(req, res, next){
    Book.findByIdAndUpdate({_id: req.params.id}, req.body).then(function(){
        Book.findOne({_id: req.params.id}).then(function(book){
            res.send(book);
        });
    }).catch(next);
});

// Delete a book from the database.
router.delete('/books/:id', function(req, res, next){
    Book.findByIdAndRemove({_id: req.params.id}).then(function(book){
        res.send(book);
    }).catch(next);
});

module.exports = router;

and model.js

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

// CREATE BOOK-DATA SCHEMA and MODEL
const bookSchema = new Schema({
    author: {
        type: String,
        required: [false, 'Author field is required'] 
    },

    publication: {
        type: String
    },

    publisher: {
        type: String
    },

    /*available: {
        type: Boolean,
        default: false
    }*/
});

const Book = mongoose.model('book', bookSchema);

module.exports = Book;

Added api.js and that's model. Is there missing something ? Added CreateForm.js and there is the place where I'm passing the new data. Is state definition wrong ?

gilgamesh
  • 462
  • 2
  • 5
  • 9

3 Answers3

0

This might be because books is not Array. Always follow practice of checking Array and then map like.

const bookElements = Array.isArray(books) && books.map(book => {
    return( ...)

or

 const bookElements = Array.isArray(books) ? books.map(book => {
        return( ...) : // if not array then show some msg may b error/loader/empty list depending upon use case

For better solution provide more details like API reply etc

Revansiddh
  • 2,932
  • 3
  • 19
  • 33
  • Getting this error via console.data: error: "E11000 duplicate key error collection: bookJUMP.books index: _id_ dup key: { : ObjectId('5baddfc183aa381484e5cc62') }" – gilgamesh Sep 28 '18 at 10:14
  • it means in your array of objects, there are two objects with same key (book._id)`5baddfc183aa381484e5cc62` To temporary remove this error remove `keyfield` from div just for testing purpose – Revansiddh Sep 28 '18 at 10:17
  • I added api and model if you want to look. – gilgamesh Sep 28 '18 at 10:21
  • I think this related to mongoose. https://stackoverflow.com/questions/38347186/mongoose-caused-by-11000-e11000-duplicate-key-error-index – Revansiddh Sep 28 '18 at 10:24
  • https://stackoverflow.com/questions/24430220/e11000-duplicate-key-error-index-in-mongodb-mongoose – Revansiddh Sep 28 '18 at 10:24
  • I dropped out database and make a new req. still doesn't work. – gilgamesh Sep 28 '18 at 11:00
  • try sending dummy array to `Search` component. If it works then error might be in not in react or not in `Search` component. Try debugging – Revansiddh Sep 28 '18 at 11:03
0

Always do conditional check when doing .map or .forEach

.map with return

 const bookElements = books && books.map(book => {
    return(
      <div className="book-data" key={book._id}>
        <div>{book.author}</div>
        <div>{book.publication}</div>
        <div>{book.publisher}</div>
      </div>
    )
  })

.map without return

const bookElements = books && books.map(book => (
      <div className="book-data" key={book._id}>
        <div>{book.author}</div>
        <div>{book.publication}</div>
        <div>{book.publisher}</div>
      </div>
    ))
Hemadri Dasari
  • 32,666
  • 37
  • 119
  • 162
0

Try this:

1. Client-Side

App.js

addBook = book => {
    axios.post('/api/books/', book).then(res => {
      this.setState({
        books: [...this.state.books, res.data]
      });
    });
  }; 

2. Server-Side

Api.js

router.post('/books', function(req, res, next) {
  const book = new Book(req.body);
  book
    .save()
    .then(book => res.send(book))
    .catch(next);
});

Hopefully, that helps!

You Nguyen
  • 9,961
  • 4
  • 26
  • 52