bugfix> reactjs > 投稿

私は App.js の機能を持つ基本的なReactアプリを持っています棚間での本の移動を処理する後続のコンポーネントの小道具として受け渡すコンポーネント。

関数を1レベル下に渡すと、 App.js から ListBooks.jsBooks.js に渡されるユーザーが新しいシェルフを選択し、API呼び出しと状態の更新をトリガーするコンポーネントです。これは正常に機能します。

しかし、その後 App.js から同じことをしようとすると>  SearchBooks.jsBook.js 動作していないようです。

私が期待しているのは、 updateShelf ブックと状態を更新するために呼び出される関数。

「コードの壁」が多すぎる場合はおologiesび申し上げます。問題がどこにあるのか正確にはわかりません。

編集:

コメントで示唆されているように、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

回答 1 件
  • 議論したように、それは小道具の受け渡しの問題ではなく、状態の更新の問題です。動作しているように見える変更されたコードは次のとおりです。

     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);
    };
    
    

あなたの答え