我有一个基本的React应用程序,在App.js
组件中有一个函数,作为后续组件的支柱传递,以处理在书架之间移动书籍。
当我将该函数向下传递一个级别时,从App.js
传递到ListBooks.js
以传递到Books.js
组件,用户将在该组件上选择新的磁盘架并触发API调用和状态更新,这很好。
然而,当我尝试从App.js
>做同样的事情时SearchBooks.js
> Book.js
它似乎不起作用。
我期望调用updateShelf
函数来更新书籍和状态。
如果那里有太多的“代码墙”,请道歉,只是不确定问题的确切位置。
编辑:
正如评论中所建议的,这是CodeSandbox版本: https://codesandbox.io/s/github/richardcurteis/myreads-udacity
App.js
import React, { Component } from 'react'
import ListBooks from './ListBooks'
import SearchBooks from './SearchBooks'
import * as BooksAPI from './utils/BooksAPI'
import { Route } from 'react-router-dom'
class BooksApp extends Component {
state = {
books: []
}
componentDidMount() {
BooksAPI.getAll()
.then((books) => {
this.setState(() => ({
books
}))
})
}
updateShelf = (book, shelf) => {
this.state.books.forEach(b => {
if(b.id === book.id) {
b.shelf = shelf
this.setState((currentState) => ({
books: currentState.books
}))
BooksAPI.update(book, shelf)
}
});
}
render() {
return (
<div>
<Route exact path='/' render={() => (
<ListBooks
books={this.state.books}
onUpdateShelf={this.updateShelf}
/>
)} />
<Route exact path='/search' render={() => (
<SearchBooks
onUpdateShelf={this.updateShelf}
/>
)} />
</div>
)
}
}
export default BooksApp
SearchBooks.js
import React, { Component } from 'react'
import * as BooksAPI from './utils/BooksAPI'
import Book from './Book';
export default class SearchBooks extends Component {
state = {
query: '',
books: []
}
updateQuery(query) {
this.setState(() => ({
books: [],
query: query
}))
this.bookSearch(query)
}
bookSearch(e) {
if (e.length > 0) BooksAPI.search(e)
.then(books => this.setState(currentState => ({
books: books
})));
}
render() {
const { query, books } = this.state
const { onUpdateShelf } = this.props
return(
<div className="search-books">
<div className="search-books-bar">
<a className="close-search" >Close</a>
<div className="search-books-input-wrapper">
<input
type="text"
placeholder="Search by title, author or subject"
value={query}
onChange={(event) => this.updateQuery(event.target.value)}
/>
</div>
</div>
<div className="search-books-results">
<ol className="books-grid">
<li>
{ !books.error ? (
books.map((book) => (
<Book
key={book.id}
book={book}
updateShelf={onUpdateShelf}
/>
))
) : (
<h4>"{query}", is not a valid search</h4>
)}
</li>
</ol>
</div>
</div>
)
}
}
Book.js
import React, { Component } from 'react'
import PropTypes from 'prop-types'
class Book extends Component {
static propTypes = {
book: PropTypes.object.isRequired
}
render() {
const { book, updateShelf } = this.props
return(
<div key={book.id} className="book">
<div className="book-top">
<div className="book-cover" style={{ width: 128, height: 193, backgroundImage: `url(${book.imageLinks.thumbnail})` }}></div>
<div className="book-shelf-changer">
<select value={book.shelf ? book.shelf : 'none'} onChange={(e) => updateShelf(book, e.target.value)}>
<option disabled >Move to...</option>
<option value="currentlyReading" >Currently Reading</option>
<option value="wantToRead" >Want to Read</option>
<option value="read" >Read</option>
<option value="none" >None</option>
</select>
</div>
</div>
<div className="book-title">{book.title}</div>
<div className="book-authors">{book.authors}</div>
</div>
)
}
}
export default Book
仅供参考:
ListBooks.js
import React, { Component } from 'react';
import PropTypes from 'prop-types'
import './App.css'
import { Link } from 'react-router-dom'
import Book from './Book'
const shelves = [
{
key: 'currentlyReading',
name: 'Currently Reading'
},
{
key: 'wantToRead',
name: 'Want To Read'
},
{
key: 'read',
name: 'Read'
}
];
class ListBooks extends Component {
static propTypes = {
books: PropTypes.array.isRequired,
onUpdateShelf: PropTypes.func.isRequired
}
render() {
const { books, onUpdateShelf } = this.props
function getBooksForShelf(shelfKey) {
return books.filter(book => book.shelf === shelfKey);
}
return(
<div className="app">
<div className="list-books">
<div className="list-books-title">
<h1>My Reads</h1>
</div>
<div className="list-books-content">
<div>
{ shelves.map((shelf) => (
<div key={shelf.key} className="bookshelf">
<h2 className="bookshelf-title">{shelf.name}</h2>
{ getBooksForShelf(shelf.key).length === 0 ? (
<div>
<h4>No books in this shelf</h4>
</div>
) : (
<div className="bookshelf-books">
<ol className="books-grid">
<li>
{ getBooksForShelf(shelf.key).map((book) => (
<Book key={book.id}
book={book}
updateShelf={onUpdateShelf}/>
))}
</li>
</ol>
</div>
)}
</div>
)) }
</div>
</div>
<Link
to='/search'
className="open-search">
Find a Book
</Link>
</div>
</div>
)
}
}
export default ListBooks
答案 0 :(得分:2)
正如我们所讨论的那样,道具传球不是问题,而是更新状态。以下是修改后的代码:
updateShelf = (book, shelf) => {
const bookFromState = this.state.books.find(b => b.id === book.id);
if (bookFromState) {
// update existing
bookFromState.shelf = shelf;
this.setState(currentState => ({
books: currentState.books
}));
BooksAPI.update(book, shelf);
} else {
// add new one
this.setState(prevState => ({ books: [...prevState.books, book] }));
BooksAPI.update(book, shelf);
}
};
或者更好而不改变状态:
updateShelf = (book, shelf) => {
this.setState(prevState => {
const booksCopy = prevState.books.filter(b => b.id !== book.id);
booksCopy.push({ ...book, shelf });
return { books: booksCopy }
});
BooksAPI.update(book, shelf);
};