3noki
3noki

Reputation: 397

React undefined errors

Full code here

I am getting errors for udpateShelf is not defined, but Changeshelf is imported and it has properties assigned in the function, I'm so confused, why am I still getting undefined errors? I feel like I'm not understanding fully how react passes props through the components, if someone cane explain that better I feel that will help me with creating different components or functions for this app.

Bookshelf.js

import React, { Component } from 'react'
import * as BooksAPI from './BooksAPI'
import PropTypes from 'prop-types'
import Books from './Books.js'
import logo from '../icons/logo.svg'
import { Link } from 'react-router-dom'
import ChangeShelf from "./ChangeShelf.js"

const shelves = [
  { key: 'currentlyReading',
    name: 'Currently Reading' },
  { key: 'wantToRead',
    name: 'Want to Read' },
  { key: 'read',
    name: 'Read' }
]

export default class Bookshelf extends Component {

static propTypes = {
  book: PropTypes.object.isRequired,
  books: PropTypes.array.isRequired,
  updateShelf: PropTypes.func.isRequired
}

render() {
  const { book, books, shelfkey } = this.props;

  return (

  <div className="list-books">
      <div className="react-app">
        <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <h1 className="App-title">React bookshelf app</h1>
        </header>
      </div>

        <div className="list-books-title">
          <h1>my bookshelf</h1>
        </div>

    <div className="list-books-content">
      {shelves.map(shelf => (
        <div key={ shelf.key } className="bookshelf">
          <h2 className="bookshelf-title">{ shelf.name }</h2>

          { updateShelf(shelfkey).length === 0 ?
            (<div>no books on this shelf</div>)
            :
            (<div className="bookshelf-books">
                <ol className="books-grid">
                  <li>
                    {updateShelf(shelf.key).map(book => (
                      <Books
                        book={book}
                        books={books}
                        key={book.id}
                        onupdateShelf={this.UpdateShelf}
                      />
                    ))}
                  </li>
                </ol>
            </div>
            )
          }

        </div>
      ))}
      <Link to="/search" className="open-search">Add a book</Link>
    </div>
  </div>

    )
  }
}

Changeshelf.js

import React, { Component } from 'react'
import * as BooksAPI from './BooksAPI'
import PropTypes from 'prop-types'
import Books from './Books.js'

export default class ChangeShelf extends Component {

  static propTypes = {
    book: PropTypes.object.isRequired,
    books: PropTypes.array.isRequired,
    onChangeShelf: PropTypes.func.isRequired
}

updateShelf = (book, newShelf) => {
  this.setState(previousState => {
    const newBooks = previousState.books.filter((b) => b.id !== 
book.id)
    newBooks.push({ book, newShelf })
    return { books: newBooks }
  })
  BooksAPI.update(book, newShelf)
}
moveBook = (shelf) => {
  const { books } = this.props
  return books.filter(book => book.shelf === shelf)
}

render() {
  const { book, books, updateShelf } = this.props

    return (
      <div className="book-shelf-changer">
        <select onChange={(event) => updateShelf(book, 
event.target.value)}>
          <option value="move" disabled>Move to...</option>
          <option value="Currently Reading">Currently Reading</option>
          <option value="Want To Read">Want to Read</option>
          <option value="Read">Read</option>
          <option value="none">None</option>
        </select>
      </div>
    )
  }
}

Upvotes: 2

Views: 72

Answers (1)

xadm
xadm

Reputation: 8418

I'll try to explain ;)

Component App

  • loads books and saves data in state;

  • render uses imported component <Bookshelf />;

  • Bookshelf gets this.state.books as books prop and not existing (in App) handler/method this.updateShelf as onUpdateShelf prop;

In Bookshelf you can use them by this.props.books and this.props.onUpdateShelf. This is passing handler by prop. It lets call method in parent from child. It can be passed/used deeply. Called method, async processes usually ends with new state (setState) which forces rerender (updating props passed to childs).

For simplicity const { book, books, shelfkey } = this.props; (in render) these props can be used by local identifiers book (instead this.props.book), books, shelfkey.

Problems:

  • no book passed;

  • no shelfkey passed;

  • missing book and updateShelf defined by propTypes as required (we have onUpdateShelf instead, but passed missing method, undefined);

  • updateShelf not defined, not passed (should be this.props.updateShelf), not defined locally (should be this.updateShelf);

  • updateShelf used (expected) there as fn returning array while in ChangeShelf as event handler;

You can move updateShelf method from ChangeShelf to App and pass it as prop to Bookshelf, Search and deeper when needed.

Upvotes: 1

Related Questions