繁体   English   中英

反应,如何从父母访问孩子的状态? 无需更新父级状态

[英]React, how to access child's state from parent? no need to update parent's state

嗨,我对React很新,并且非常难以绕过整个状态管理并通过状态和道具传递数据。 我确实理解标准的反应方式是以单向方式传递数据 - 从父节点到子节点,我已经为所有其他组件这样做了。 但我有一个名为Book的组件,它根据用户选择形式'read,wantToRead,currentReading和none'改变其“shelf”状态。 在我的BookList组件中呈现Book组件,但它需要能够读取Book的架子状态并在名为“read,wantToRead,currentReading和none”的部分下呈现正确的书籍。 而且由于在这种情况下,Book组件是从BookList组件呈现的,而BookList是父组件,我真的无法理解如何启用BookList来访问Book的状态? BookList组件:

import React, { Component } from 'react'
import { Link } from 'react-router-dom'

import Book from './Book'

class BookList extends Component {
  render(){
    const { books, shelf } = this.props


    return (
    <div className="list-books">
      <div className="list-books-content">
        <div className="list-books-title">
          <h1>MyReads</h1>
        </div>

        <div className="bookshelf">
          <h2 className="bookshelf-title">None</h2>
          {books.map((book)=> {
            console.log(book)
            if (shelf === ''){
              return <div className="bookshelf-books">
                    {/* <BookStateless book= {book} /> */}
                    <Book book = {book} />
                      {/* <BookStateless book= {book} /> */}
                    </div>

            }
          })}
        </div>


        <div className="bookshelf">
          <h2 className="bookshelf-title">Currently Reading</h2>
            {books.map((book)=> {
              if (shelf === 'currentlyReading'){
                return <div className="bookshelf-books">
                      {/* <BookStateless book= {book} /> */}
                      <Book book = {book} />
                      </div>
              }
              // console.log(this.book.title, this.book.state)
            })}
          </div>

          <div className="bookshelf">
            <h2 className="bookshelf-title">Want to Read</h2>
            {books.map((book)=> {
              if (shelf === 'wantToRead'){
                return <div className="bookshelf-books">
                      {/* <BookStateless book= {book} /> */}
                      <Book book = {book} />
                        {/* <BookStateless book= {book} /> */}
                      </div>
              }
              // console.log(this.book.title, this.book.state)
            })}
          </div>
          <div className="bookshelf">
            <h2 className="bookshelf-title">Read</h2>
            {books.map((book)=> {
              if (shelf === 'read'){
                console.log(shelf)
                return <div className="bookshelf-books">
                      {/* <BookStateless book= {book} /> */}
                      <Book book = {book} />
                      </div>
              }
              // console.log(this.book.title, this.book.state)
            })}
          </div>

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

export default BookList

书籍组成:

import React, { Component } from 'react'
// import * as BooksAPI from './BooksAPI'

import Select from 'react-select'
import 'react-select/dist/react-select.css'

class Book extends Component {
  state={
    // state can be read, none, want to read, or currently reading
    shelf: ''
  }


  handleChange(e){
    this.setState({ shelf: e['value'] })
    console.log("this?", this)
  }



  render(){
    const { book } = this.props
    const { shelf } = this.state
    console.log("book", book.state)

    const options = [
      { value: 'currentlyReading', label: 'currentlyReading'},
      { value: 'wantToRead', label: 'wantToRead'},
      { value: 'read', label: 'read'},
      { value: 'none', label: 'none'}
    ]

    return (
      <li key={book.id}>
        <div className="book">
          <div className="book-top">
            <div className="book-cover" style={{ width: 128, height: 188, backgroundImage: `url("${book.imageLinks.thumbnail}")` }}></div>
            <div className="book-shelf-changer">

              <Select
                value=""
                options={options}
                onChange={(e)=>this.handleChange(e)}
              />


            </div>
          </div>
          <div className="book-title">{book.title}</div>
          <div className="book-authors">{book.authors}</div>
        </div>
      </li>

    )
  }
}

export default Book

在我的app.js我有:

import React from 'react'
import * as BooksAPI from './BooksAPI'
import './App.css'
import Search from './Search'
import BookList from './BookList'
import Book from './Book'


import { Route } from 'react-router-dom'

class BooksApp extends React.Component {


  state = {
    books : []

  }

  componentDidMount(){
    BooksAPI.getAll().then((books)=> {
      this.setState({ books: books })
      // console.log("bookstest",this)
    })
  }


  render() {
    return (
      <div className="App">
        <Route exact path="/" render={()=>(
          <Book books={this.state.books} />
        )} />
        <Route path="/search" render={()=>(
          <Search books={this.state.books} />
        )} />
        <Route path="/BookList" render={()=>(
          <BookList books={this.state.books} />
        )} />

      </div>
      )
    }
  }

export default BooksApp

现在,当我在浏览器中打开书单组件时,我没有书,因为它没有在任何if语句中获取状态:

if (shelf === 'currentlyReading'){
                return <div className="bookshelf-books">
}

非常感谢您提前阅读,任何帮助将不胜感激! 谢谢!

您不需要“访问”子项的状态,您可以将回调处理程序从父项传递给子项,并且在子项内部触发事件时,您可以通过该事件处理程序(回调)通知父项。
我会发一个小例子:

 class Book extends React.Component { handleClick = e => { const { bookId, onToggleBook } = this.props; onToggleBook(bookId); }; render() { const { name, isRead } = this.props; return ( <div className={`${isRead && "read"}`} onClick={this.handleClick}> <span>{name}</span> {isRead && <i> - You read me</i> } </div> ); } } class App extends React.Component { constructor(props) { super(props); this.state = { books: [ { id: 1, name: "book 1", isRead: false }, { id: 2, name: "book 2", isRead: false }, { id: 3, name: "book 3", isRead: true }, { id: 4, name: "book 4", isRead: false } ] }; } onToggleBookStatus = bookid => { const { books } = this.state; const nextBookState = books.map(book => { if (book.id !== bookid) return book; return { ...book, isRead: !book.isRead }; }); this.setState(prevState => ({ books: nextBookState })); }; render() { const { books } = this.state; return ( <div> <div>My Books</div> {books.map(book => ( <Book key={book.id} isRead={book.isRead} name={book.name} bookId={book.id} onToggleBook={this.onToggleBookStatus} /> ))} </div> ); } } ReactDOM.render(<App />, document.getElementById("root")); 
 .read { color: red; } 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> <div id="root"></div> 

如您所知,要将某些内容从父级传递给子级,您可以使用props。 为了从孩子到父母的东西,你再次使用道具,但这次你将一个函数传递给孩子,然后孩子调用该函数。

因此,例如,您可以将子项的句柄更改函数修改为以下内容:

handleChange(e){
  if (this.props.onShelfChanged) {
    this.props.onShelfChanged(e.value);
  }
  this.setState({ shelf: e.value })
}

然后在父级中,您将要将onShelfChanged prop传递给该书,以便在值更改时收到通知。 像这样的东西:

// in the render function
{books.map((book, index) => 
    <Book book={book} onShelfChanged={() => this.childBookChanged(index)}
)};

并且您需要创建并填写childBookChanged函数以执行您需要执行的任何更新。

有一点需要注意的是,您不希望手动保持书籍和书架同步。 Book正在跟踪它自己的一些状态,然后你将它传递出来并可能改变了书架的状态。 随着应用程序的增长保持这些同步可能是一个令人头疼的问题,也是一个错误来源。 相反,你应该有一段代码负责,它看起来可能是书架(因为它是最关心这个状态的组件)。 所以很可能你会想要从Book删除内部状态,而是通过道具告诉书本要做什么。

如果您需要Book组件有时作为独立工作并且有时在书架中工作,那么您可能需要做更多工作以使其支持“受控”和“不受控制”的实现,但它仍然是一个好的为受控案件移动国家的想法。 您可以在此处此处阅读有关受控和非受控组件的更多信息

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM