欢迎来到DIVCSS5查找CSS资料与学习DIV CSS布局技术!
  当我问自己第一百次时,我正在研究一个典型的CRUD屏幕:“我应该将状态保留在这个组件中还是将其移动到父组件?”。
 
  如果需要对子组件的状态进行轻微控制。您可能也遇到了同样的问题。
 
  让我们通过一个简单的例子和三种修复方法来回顾它。前两种方法是常见的做法,第三种方法不太常规。
 
  问题;
 
  为了向您展示我的意思,我将使用一个简单的书籍CRUD(译者注:增加(Create)、读取查询(Retrieve)、更新(Update)和删除(Delete))屏幕(如此简单,它没有创建和删除操作)。
 
  381280250-5b6ae9839a548_articlex.png
 
  我们有三个组成部分。<BookList/>是一个组件,显示了用于编辑它们的书籍和按钮列表。<BookForm/>有两个输入和一个按钮,用于保存对书籍的更改。以及包含其他两个组件的<BookApp/>。
 
  那么,我们的状态是什么?好吧,<BookApp/>应该跟踪书籍清单以及识别当前正在编辑的书籍的内容。<BookList/>没有任何状态。并且<BookForm/>应该保持输入的当前状态,直到单击“保存”按钮。
 
  
 
  
 
  
 
  
 
  
 
  importReact,{Component}from"react";
 
  import{render}from"react-dom";
 
  constbooks=[
 
  {
 
  title:"TheEndofEternity",
 
  author:"IsaacAsimov"
 
  },
 
  //...
 
  ];
 
  constBookList=({books,onEdit})=&gt;(
 
  <table>
 
  <tr>
 
  <th>BookTitle</th>
 
  <th>Actions</th>
 
  </tr>
 
  {books.map((book,index)=&gt;(
 
  <tr>
 
  <td>{book.title}</td>
 
  <td>
 
  &lt;buttononClick={()=&gt;onEdit(index)}&gt;Edit&lt;/button&gt;
 
  </td>
 
  </tr>
 
  ))}
 
  </table>
 
  );
 
  classBookFormextendsComponent{
 
  state={...this.props.book};
 
  render(){
 
  if(!this.props.book)returnnull;
 
  return(
 
  &lt;form&gt;
 
  <h3>Book</h3>
 
  &lt;label&gt;
 
  Title:
 
  &lt;input
 
  value={this.state.title}
 
  onChange={e=&gt;this.setState({title:e.target.value})}
 
  /&gt;
 
  &lt;/label&gt;
 
  &lt;label&gt;
 
  Author:
 
  &lt;input
 
  value={this.state.author}
 
  onChange={e=&gt;this.setState({author:e.target.value})}
 
  /&gt;
 
  &lt;/label&gt;
 
  &lt;buttononClick={()=&gt;this.props.onSave({...this.state})}&gt;
 
  Save
 
  &lt;/button&gt;
 
  &lt;/form&gt;
 
  );
 
  }
 
  }
 
  classBookAppextendsComponent{
 
  state={
 
  books:books,
 
  activeIndex:-1
 
  };
 
  render(){
 
  const{books,activeIndex}=this.state;
 
  constactiveBook=books[activeIndex];
 
  return(
 
  <div>
 
  &lt;BookList
 
  books={books}
 
  onEdit={index=&gt;
 
  this.setState({
 
  activeIndex:index
 
  })}
 
  /&gt;
 
  &lt;BookForm
 
  book={activeBook}
 
  onSave={book=&gt;
 
  this.setState({
 
  books:Object.assign([...books],{[activeIndex]:book}),
 
  activeIndex:-1
 
  })}
 
  /&gt;
 
  </div>
 
  );
 
  }
 
  }
 
  render(&lt;BookApp/&gt;,document.getElementById("root"));
 
  在codesandbox尝试一下
 
  看起来不错,但是他不起作用。
 
  我们正在创建组件实例时初始化<BookForm/>状态,因此,当从列表中选择另一本书时,父级无法让它知道它需要更改它。
 
  我们改如何修复它?
 
  方法1:受控组件
 
  一种常见的方法是将状态提升,将<BookForm/>转换为受控组件。我们删除<BookForm/>状态,将activeBook添加到<BookApp/>状态,并向<BookForm/>添加一个onChange道具,我们在每次输入时都会调用它。
 
  
 
  
 
  
 
  
 
  //...
 
  classBookFormextendsComponent{
 
  render(){
 
  if(!this.props.book)returnnull;
 
  return(
 
  &lt;form&gt;
 
  <h3>Book</h3>
 
  &lt;label&gt;
 
  Title:
 
  &lt;input
 
  value={this.props.book.title}
 
  onChange={e=&gt;
 
  this.props.onChange({
 
  ...this.props.book,
 
  title:e.target.value
 
  })}
 
  /&gt;
 
  &lt;/label&gt;
 
  &lt;label&gt;
 
  Author:
 
  &lt;input
 
  value={this.props.book.author}
 
  onChange={e=&gt;
 
  this.props.onChange({
 
  ...this.props.book,
 
  author:e.target.value
 
  })}
 
  /&gt;
 
  &lt;/label&gt;
 
  &lt;buttononClick={()=&gt;this.props.onSave()}&gt;Save&lt;/button&gt;
 
  &lt;/form&gt;
 
  );
 
  }
 
  }
 
  classBookAppextendsComponent{
 
  state={
 
  books:books,
 
  activeBook:null,
 
  activeIndex:-1
 
  };
 
  render(){
 
  const{books,activeBook,activeIndex}=this.state;
 
  return(
 
  <div>
 
  &lt;BookList
 
  books={books}
 
  onEdit={index=&gt;
 
  this.setState({
 
  activeBook:{...books[index]},
 
  activeIndex:index
 
  })}
 
  /&gt;
 
  &lt;BookForm
 
  book={activeBook}
 
  onChange={book=&gt;this.setState({activeBook:book})}
 
  onSave={()=&gt;
 
  this.setState({
 
  books:Object.assign([...books],{[activeIndex]:activeBook}),
 
  activeBook:null,
 
  activeIndex:-1
 
  })}
 
  /&gt;
 
  </div>
 
  );
 
  }
 
  }
 
  //...
 
  方法2:同步state
 
  现在它可以工作,但对我来说,提升<BookForm/>的状态感觉不对。在用户单击“保存”之前,<BookApp/>不关心对书的任何更改,那么为什么需要将其保持在自己的状态?
 
  在codesandbox尝试一下
 
  现在它可以工作,但对我来说,提升<BookForm/>的状态感觉不对。在用户单击“保存”之前,<BookApp/>不关心对书的任何更改,那么为什么需要将其保持在自己的状态?
 
  
 
  
 
  //...
 
  classBookFormextendsComponent{
 
  state={...this.props.book};
 
  componentWillReceiveProps(nextProps){
 
  constnextBook=nextProps.book;
 
  if(this.props.book!==nextBook){
 
  this.setState({...nextBook});
 
  }
 
  }
 
  render(){
 
  if(!this.props.book)returnnull;
 
  return(
 
  &lt;form&gt;
 
  <h3>Book</h3>
 
  &lt;label&gt;
 
  Title:
 
  &lt;input
 
  value={this.state.title}
 
  onChange={e=&gt;this.setState({title:e.target.value})}
 
  /&gt;
 
  &lt;/label&gt;
 
  &lt;label&gt;
 
  Author:
 
  &lt;input
 
  value={this.state.author}
 
  onChange={e=&gt;this.setState({author:e.target.value})}
 
  /&gt;
 
  &lt;/label&gt;
 
  &lt;buttononClick={()=&gt;this.props.onSave({...this.state})}&gt;
 
  Save
 
  &lt;/button&gt;
 
  &lt;/form&gt;
 
  );
 
  }
 
  }
 
  //...
 
  在codesandbox尝试一下
 
  这种方法通常被认为是一种不好的做法,因为它违背了React关于拥有单一事实来源的想法。我不确定是这种情况,然而,同步状态并不总是那么容易。此外,我尽量避免使用生命周期方法。
 
  方法3:由Key控制的组件
 
  但为什么我们要回收旧的状态呢?每次用户选择一本书时,拥有一个全新状态的新实例是不是有意义?
 
  为此,我们需要告诉React停止使用旧实例并创建一个新实例。这就是keyprop的用途。
 
  
 
  
 
  //...
 
  classBookAppextendsComponent{
 
  state={
 
  books:books,
 
  activeIndex:-1
 
  };
 
  render(){
 
  const{books,activeIndex}=this.state;
 
  constactiveBook=books[activeIndex];
 
  return(
 
  <div>
 
  &lt;BookList
 
  books={books}
 
  onEdit={index=&gt;
 
  this.setState({
 
  activeIndex:index
 
  })}
 
  /&gt;
 
  &lt;BookForm
 
  key={activeIndex}
 
  book={activeBook}
 
  onSave={book=&gt;
 
  this.setState({
 
  books:Object.assign([...books],{[activeIndex]:book}),
 
  activeIndex:-1
 
  })}
 
  /&gt;
 
  </div>
 
  );
 
  }
 
  }
 
  //...
 
  在codesandbox尝试一下。
 
  如果元素具有与上一个渲染不同的键,则React会为其创建一个新实例。因此,当用户选择新书时,<BookForm/>的键更改,将创建组件的新实例,并从props初始化状态。





本文转载自中文网

 

如需转载,请注明文章出处和来源网址:http://www.divcss5.com/html5/h54950.shtml