React,如何从父母那里访问孩子的状态?无需更新父母的状态

Posted

技术标签:

【中文标题】React,如何从父母那里访问孩子的状态?无需更新父母的状态【英文标题】:React, how to access child's state from parent? no need to update parent's state 【发布时间】:2018-03-19 20:37:21 【问题描述】:

您好,我对 React 还是很陌生,我很难完全理解整个状态管理以及通过状态和道具传递数据。我确实明白标准的反应方式是以单向方式传递数据——从父级到子级,我对所有其他组件都这样做了。 但我有一个名为 Book 的组件,它根据用户选择表单“已读、想要读、当前读和无”更改其“搁置”状态。在我的 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

现在,当我在浏览器中打开 booklist 组件时,我没有得到任何书籍,因为它没有在此处的任何 if 语句中获取状态:

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

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

【问题讨论】:

你在哪里为书单建立路由 url? 这能回答你的问题吗? How to access child's state in React? 【参考方案1】:

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

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>

【讨论】:

老兄,你刚刚给了我开启我心中 React 智慧的钥匙!非常感谢!!【参考方案2】:

如您所知,要将某些东西从父级传递给子级,您需要使用道具。为了从孩子那里得到一些东西给父母,你再次使用道具,但这次你将一个函数传递给孩子,然后孩子调用那个函数。

例如,您可以将孩子的句柄更改功能修改为如下所示:

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

然后在父级中,您需要将 onShelfChanged 属性传递给 book,以便在值更改时收到通知。像这样的:

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

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

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

如果您需要 Book 组件有时作为独立组件工作,有时在书架内工作,那么您可能需要做更多工作以使其同时支持“受控”和“不受控”实现,但是对于受控情况,将状态向上移动仍然是一个好主意。您可以阅读更多关于受控和非受控组件here 和here

【讨论】:

以上是关于React,如何从父母那里访问孩子的状态?无需更新父母的状态的主要内容,如果未能解决你的问题,请参考以下文章

问题:为什么在我为孩子设置状态时,React为什么更新我的父母?仅发生在数组

React Native - 将父母的状态传递给孩子的麻烦

当孩子必须更新父母的状态时,this.setState 不起作用

从某些父母那里退回那些他们的孩子的某些财产等于某个价值的记录?

React context api,将数据从孩子传递给父母

父元素的入队React更新是否也总是更新子元素吗?