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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/46632830/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 03:31:48  来源:igfitidea点击:

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

javascriptreactjsstate

提问by Kimmiekim

Hi I am pretty new to React and having really hard time wrapping my head around this whole state management and passing data through state and props. I do understand that the standard react way is to pass down data in a unidirectional way- from parent to child, which I have done so for all other components. But I have this component called Book, which changes its 'shelf' state, based on user selection form 'read, wantToRead, currentlyReading, and none'. And in my BookList component which renders Book component, but it needs to be able to read Book's shelf state and render the correct books under sections called 'read, wantToRead, currentlyReading, and none'. And since in this case, Book component is being rendered from BookList component and BookList being the parent, i really cannot understand how to enable BookList to access Book's state? BookList component:

嗨,我是 React 的新手,我真的很难将整个状态管理和数据传递到状态和道具上。我确实理解标准的反应方式是以单向方式传递数据 - 从父级到子级,我已经为所有其他组件这样做了。但是我有一个名为 Book 的组件,它根据用户选择形式“read、wantToRead、currentlyReading 和 none”更改其“货架”状态。在我的 BookList 组件中,它呈现 Book 组件,但它需要能够读取 Book 的书架状态并在名为“read、wantToRead、currentlyReading 和 none”的部分下呈现正确的书籍。因为在这种情况下,Book 组件是从 BookList 组件渲染的,而 BookList 是父组件,我真的不明白如何让 BookList 访问 Book 的状态?书单组件:

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

Book component:

书籍组件:

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

in my app.js i have:

在我的 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

Right now, when i open the booklist component in browser, i get no books because it's not picking up the state in any of the if statements here:

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

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

Thank you so much in advance for reading through and, any help would be much appreciated! Thank you!

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

回答by Sagiv b.g

You don't need to "access" the child's state, you can pass a callback handler from the parent to the child and when an event is triggered inside the child you can notify the parent through that event handler (callback).
I'll post a small example:

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

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>

回答by Nicholas Tower

As you know, to pass something from the parent to the child, you use props. To get something from the child to the parent, you again use props, but this time you pass a functiondown to the child, and then the child calls that function.

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

So for example, you would modify the child's handle change function to something like this:

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

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

And then in the parent, you'll want to pass an onShelfChanged prop down to the book, so that you can get notified when the value changes. Something like this:

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

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

And you'll need to create and fill out the childBookChanged function to do whatever updates you need to do.

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

One thing to be mindful of is that you don't want to be manually keeping the book and the bookshelf in sync. The Bookis tracking some state of its own, and then you're passing that up and probably altering the state of the bookshelf. Keeping these in sync as your application grows can be a headache and a source of bugs. Instead, you should have one piece of code be in charge, which it looks like will likely be the bookshelf (since it's the topmost component that cares about this state). So most likely you'll want to remove the internal state from Book, and instead tell the book what to do via props.

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

If you need the Bookcomponent to sometimes work as a standalone and sometimes work inside a bookshelf, then you may need to do a bit more work to get it to support both a "controlled" and "uncontrolled" implementation, but it's still a good idea to move the state up for the controlled case. You can read more about controlled and uncontrolled components hereand here

如果您需要Book组件有时作为独立的工作,有时在书架内工作,那么您可能需要做更多的工作来让它同时支持“受控”和“非受控”的实现,但这仍然是一个好主意向上移动受控案例的状态。您可以在此处此处阅读有关受控和非受控组件的更多信息