如何从Reactjs中的子组件更新父组件的状态

时间:2017-11-08 09:04:33

标签: javascript reactjs

我将父组件的状态从父组件传递给子组件。在子组件中,我有一个不同的状态。我正在对子组件的状态和结果执行一些操作必须将其添加到父组件的状态。所以,在我的父组件中,我编写了一个回调函数,它将更新父组件的状态。代码为:

updateState = (booksList) => {
          this.setState({books : this.state.books.push(booksList)});
        }

因此,此函数随后作为props传递给子组件:

<BookSearch
   books={this.state.books}
   handleShelfChange={this.handleShelfChange}
   updateState={this.updateState}/>

然后在我的子组件中,我试图将回调函数实现为:

let getBook = this.state.books.filter(filteredBook => filteredBook.shelf !== "none")
    this.props.updateState(getBook)

但这不符合预期。这是正确的方法吗?有人可以帮我这个吗?

我尝试通过实施此处提供的解决方案来解决我的问题:How to pass data from child component to its parent in ReactJS?,但我收到了一些错误。

修改

父组件:App.js

import React from 'react'
import * as BooksAPI from './BooksAPI'
import { Link } from 'react-router-dom'
import { Route } from 'react-router-dom'
import './App.css'
import BookList from './BookList'
import BookSearch from './BookSearch'


class BooksApp extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      books: [],
      showSearchPage : false
    };
    //this.updateState = this.updateState.bind(this)
  }

  componentDidMount() {
    BooksAPI.getAll().then((books) => {
      this.setState({ books })
    })
    console.log(this.state.books);
  }

  filterByShelf = (bookName,shelfName) =>
   bookName.filter(book => book.shelf===shelfName)


  isTheBookNew = book => {
    let is = false;
    if (book.shelf === "none")
     { this.setState(state =>
       {
         books: state.books.push(book)});
          is = true;
          console.log(this.state.books);
       }
       return is;
      };

      handleShelfChange = (bookOnChange, newSehlf) => {
         !this.isTheBookNew(bookOnChange) && this.setState(state => {
         let newBooks = state.books.map(book =>
           { if (bookOnChange.id === book.id)
             { book.shelf = newSehlf; }
             return book;
           });
             return {
                books: newBooks
               };
              }
            );
            BooksAPI.update(bookOnChange, newSehlf);
            };

        updateState = (booksList) => {
          const books = [...this.state.books, booksList]
          this.setState({ books });
        }

  render() {
    return (
      <div className="app">
        <Route exact path="/" render={() => (
          <div className="list-books">
              <div className="list-books-title">
                <h1>MyReads</h1>
              </div>

              <BookList
              books={this.filterByShelf(this.state.books,'currentlyReading')}
              shelfName='Currently Reading'
              handleShelfChange={this.handleShelfChange}/>

              <BookList
              books={this.filterByShelf(this.state.books,'wantToRead')}
              shelfName='Want to Read'
              handleShelfChange={this.handleShelfChange}/>

              <BookList
              books={this.filterByShelf(this.state.books,'read')}
              shelfName='Read'
              handleShelfChange={this.handleShelfChange}/>

              <div className="open-search">
                <Link
                to="./search" />
              </div>
          </div>
        )
      } />

            <Route path="/search" render={() =>
                <BookSearch
                 books={this.state.books}
                 handleShelfChange={this.handleShelfChange}
                 updateState={this.updateState}/>
              } />

      </div>
    )
  }
}

export default BooksApp

BookSearch.js:

import React, { Component } from 'react'
import { Link } from 'react-router-dom'
import escapeRegExp from 'escape-string-regexp'
import  sortBy from 'sort-by'
import * as BooksAPI from './BooksAPI'
import BookList from './BookList'

class BookSearch extends Component {
  constructor(props) {
    super(props);
    this.state = {
      search:'',
      books:[]
    }
  }

  updateSearch = (searchString) => {
    this.setState({search: searchString.trim()})
    let searchResults = BooksAPI.search(this.state.search,1).then((book_search) => {
      if (book_search != undefined) {
        console.log(book_search);
          book_search.map((book) => book.shelf = 'none');
          this.setState({ books : book_search }, this.check); // callback function to this.setState
          console.log(this.state.books)
        }
    })

    }


  check = () => {
    let parent_books = this.props.books;
    console.log(this.state.books)
    const book_result = this.state.books.map((book) => {
      const parent = parent_books.find(parent => parent.title === book.title );
       if(parent) {
        //console.log(parent);
        book.shelf = parent.shelf;
        //console.log(book)
          }

        return book;
      })
    this.setState({books: book_result}, () => {console.log(this.state.books)})
  }

  updateParentState = () => {
    let getBook = this.state.books.filter(filteredBook => filteredBook.shelf !== "none")
    this.props.updateState(getBook)

  }

  render() {
    return(

      <div className="search-books">
        <div className="search-books-bar">
          <Link
            to="/"
            className="close-search">
            Close
          </Link>
          <div className="search-books-input-wrapper">
            <input
             type="text"
             placeholder="Search by title or author"
             value={this.state.search}
             onChange={(event) => this.updateSearch(event.target.value)}/>
          </div>
        </div>

        <div className="search-books-results">
          <ol className="books-grid">
            <BookList
              books={this.state.books}
              handleShelfChange={this.props.handleShelfChange}
              updateParentState={this.updateParentState}/>
          </ol>
        </div>
      </div>
    )
  }
}

export default BookSearch

BookList.js

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

class BookList extends Component {
constructor(props) {
  super(props);
  this.state = {
    showSearchPage : false
  }
  console.log(this.props.books)
}


  render() {
    return(
      <div className="app">
          <div>
           <div className="list-books-content">
              <div>
                <div className="bookshelf">
                  <h2 className="bookshelf-title">{this.props.shelfName}</h2>
                  <div className="bookshelf-books">
                    <ol className="books-grid">

                      {this.props.books.map(book =>
                        <li key={book.title}>
                          <Book
                          book={book}
                          handleShelfChange={this.props.handleShelfChange}
                          update={this.props.updateParentState} />
                        </li>)
                      }
                    </ol>
                  </div>

                </div>
              </div>
            </div>
          </div>
    </div>
    )
  }
}


export default BookList;

Book.js

import React, { Component } from 'react'

class Book extends Component {
  constructor(props) {
    super(props);
    this.props.updateParentState;
  }

  render() {
    return(
      <div className="book">
        <div key={this.props.book.title}>
          <div className="book-top">
            <div className="book-cover" style={{width:128, height:193, backgroundImage: `url(${this.props.book.imageLinks.thumbnail})`}}>
                  <div className="book-shelf-changer">
                    <select id="bookName" value={this.props.book.shelf}
                      onChange={(event) => this.props.handleShelfChange(this.props.book, event.target.value)}>
                      <option value="moveTo" 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>
          <div className="book-title">{this.props.book.title}</div>
          <div className="book-authors">{this.props.book.authors}</div>
        </div>

  </div>
      )
  }
}

export default Book

所以,我有4个组件,如上所示。从App组件,我调用BookSearch组件,我可以搜索书籍,并通过选择下拉列表中的值将书籍发送到App组件。 BookSearch组件中的每本书最初都会被分配一个货架属性&#34; none&#34;。当用户从Booksearch中选择货架价值时,该书将自动添加到App组件中。所以,当我导航回到来自BookSearch的App组件,我应该能够在指定的架子中看到这本书。所以,对于这种情况,我使用的是updateSearch函数。这些书籍通过Book组件显示,该组件具有下拉值。

2 个答案:

答案 0 :(得分:1)

如果我理解正确,那么你正在改变函数updateState中的状态。

你应该做的是,

const updateState = (booksList) => {
   const books = [ ...this.state.books, ...booklist ];
   this.setState({ books });
}

答案 1 :(得分:0)

在父组件构造函数中添加以下行:

this.updateState = this.updateState.bind(this)

更多https://reactjs.org/docs/handling-events.html