在React中跨组件分发状态的三种方法
在React中跨组件分发状态的三种方法
当我问自己第一百次时,我正在研究一个典型的CRUD屏幕:“我应该将状态保留在这个组件中还是将其移动到父组件?”。
如果需要对子组件的状态进行轻微控制。您可能也遇到了同样的问题。
让我们通过一个简单的例子和三种修复方法来回顾它。前两种方法是常见的做法,第三种方法不太常规。
问题;
为了向您展示我的意思,我将使用一个简单的书籍CRUD(译者注:增加(Create)、读取查询(Retrieve)、更新(Update)和删除(Delete))屏幕(如此简单,它没有创建和删除操作)。
我们有三个组成部分。<BookList />是一个组件,显示了用于编辑它们的书籍和按钮列表。<BookForm />有两个输入和一个按钮,用于保存对书籍的更改。以及包含其他两个组件的<BookApp />。
那么,我们的状态是什么?好吧,<BookApp />应该跟踪书籍清单以及识别当前正在编辑的书籍的内容。 <BookList />没有任何状态。并且<BookForm />应该保持输入的当前状态,直到单击“保存”按钮。
import React, { Component } from "react";
import { render } from "react-dom";
const books = [
{
title: "The End of Eternity",
author: "Isaac Asimov"
},
//...
];
const BookList = ({ books, onEdit }) => (
<table>
<tr>
<th>Book Title</th>
<th>Actions</th>
</tr>
{books.map((book, index) => (
<tr>
<td>{book.title}</td>
<td>
<button onClick={() => onEdit(index)}>Edit</button>
</td>
</tr>
))}
</table>
);
class BookForm extends Component {
state = { ...this.props.book };
render() {
if (!this.props.book) return null;
return (
<form>
<h3>Book</h3>
<label>
Title:
<input
value={this.state.title}
onChange={e => this.setState({ title: e.target.value })}
/>
</label>
<label>
Author:
<input
value={this.state.author}
onChange={e => this.setState({ author: e.target.value })}
/>
</label>
<button onClick={() => this.props.onSave({ ...this.state })}>
Save
</button>
</form>
);
}
}
class BookApp extends Component {
state = {
books: books,
activeIndex: -1
};
render() {
const { books, activeIndex } = this.state;
const activeBook = books[activeIndex];
return (
<div>
<BookList
books={books}
onEdit={index =>
this.setState({
activeIndex: index
})}
/>
<BookForm
book={activeBook}
onSave={book =>
this.setState({
books: Object.assign([...books], { [activeIndex]: book }),
activeIndex: -1
})}
/>
</div>
);
}
}
render(<BookApp />, document.getElementById("root"));
在codesandbox尝试一下
看起来不错,但是他不起作用。
我们正在创建组件实例时初始化<BookForm />状态,因此,当从列表中选择另一本书时,父级无法让它知道它需要更改它。
我们改如何修复它?
方法1:受控组件
一种常见的方法是将状态提升,将<BookForm />转换为受控组件。我们删除<BookForm />状态,将activeBook添加到<BookApp />状态,并向<BookForm />添加一个onChange道具,我们在每次输入时都会调用它。
//...
class BookForm extends Component {
render() {
if (!this.props.book) return null;
return (
<form>
<h3>Book</h3>
<label>
Title:
<input
value={this.props.book.title}
onChange={e =>
this.props.onChange({
...this.props.book,
title: e.target.value
})}
/>
</label>
<label>
Author:
<input
value={this.props.book.author}
onChange={e =>
this.props.onChange({
...this.props.book,
author: e.target.value
})}
/>
</label>
<button onClick={() => this.props.onSave()}>Save</button>
</form>
);
}
}
class BookApp extends Component {
state = {
books: books,
activeBook: null,
activeIndex: -1
};
render() {
const { books, activeBook, activeIndex } = this.state;
return (
<div>
<BookList
books={books}
onEdit={index =>
this.setState({
activeBook: { ...books[index] },
activeIndex: index
})}
/>
<BookForm
book={activeBook}
onChange={book => this.setState({ activeBook: book })}
onSave={() =>
this.setState({
books: Object.assign([...books], { [activeIndex]: activeBook }),
activeBook: null,
activeIndex: -1
})}
/>
</div>
);
}
}
//...
方法2:同步state
现在它可以工作,但对我来说,提升<BookForm />的状态感觉不对。在用户单击“保存”之前,<BookApp />不关心对书的任何更改,那么为什么需要将其保持在自己的状态?
在codesandbox尝试一下
现在它可以工作,但对我来说,提升<BookForm />的状态感觉不对。在用户单击“保存”之前,<BookApp />不关心对书的任何更改,那么为什么需要将其保持在自己的状态?
//...
class BookForm extends Component {
state = { ...this.props.book };
componentWillReceiveProps(nextProps) {
const nextBook = nextProps.book;
if (this.props.book !== nextBook) {
this.setState({ ...nextBook });
}
}
render() {
if (!this.props.book) return null;
return (
<form>
<h3>Book</h3>
<label>
Title:
<input
value={this.state.title}
onChange={e => this.setState({ title: e.target.value })}
/>
</label>
<label>
Author:
<input
value={this.state.author}
onChange={e => this.setState({ author: e.target.value })}
/>
</label>
<button onClick={() => this.props.onSave({ ...this.state })}>
Save
</button>
</form>
);
}
}
//...
在codesandbox尝试一下
这种方法通常被认为是一种不好的做法,因为它违背了React关于拥有单一事实来源的想法。我不确定是这种情况,然而,同步状态并不总是那么容易。此外,我尽量避免使用生命周期方法。
方法3:由Key控制的组件
但为什么我们要回收旧的状态呢?每次用户选择一本书时,拥有一个全新状态的新实例是不是有意义?
为此,我们需要告诉React停止使用旧实例并创建一个新实例。这就是key prop的用途。
//...
class BookApp extends Component {
state = {
books: books,
activeIndex: -1
};
render() {
const { books, activeIndex } = this.state;
const activeBook = books[activeIndex];
return (
<div>
<BookList
books={books}
onEdit={index =>
this.setState({
activeIndex: index
})}
/>
<BookForm
key={activeIndex}
book={activeBook}
onSave={book =>
this.setState({
books: Object.assign([...books], { [activeIndex]: book }),
activeIndex: -1
})}
/>
</div>
);
}
}
//...
在codesandbox尝试一下。
如果元素具有与上一个渲染不同的键,则React会为其创建一个新实例。因此,当用户选择新书时,<BookForm />的键更改,将创建组件的新实例,并从props初始化状态。
有什么收获?重用组件实例意味着更少的DOM突变,这意味着更好的性能。因此,当我们强制React创建组件的新实例时,我们会为额外的DOM突变获得一些开销。但是对于这样的情况,这种开销是最小的,其中密钥没有变化太快而且组件不大。
感谢你的阅读。
在React中跨组件分发状态的三种方法的更多相关文章
- React Router v4 页面传值的三种方法
传值方法 1.props.params 使用React router定义路由时,我们可以给指定一个path,然后指定通配符可以携带参数到指定的path: <Route path='/user/: ...
- React组件绑定this的三种方法
我们在使用React组件时,调用方法常常用到this和event对象,默认情况是不会绑定到组件上的,需要特殊处理. 节点上使用bind绑定 特点:该方法会在每次渲染组件时都会重新绑定一次,消耗一定的性 ...
- iOS 判断当前网络状态的三种方法
http://www.cocoachina.com/ios/20171103/21039.html 在项目中,为了好的用户体验,有些场景必须线判断网络状态,然后才能决定改干嘛.比如视频播放,需要线判断 ...
- 1.Git起步-Git的三种状态以及三种工作区域、CVCS与DVCS的区别、Git基本工作流程
1.Git基础 版本控制系统是一种用于记录一个或多个文件内容变化,以便将来查阅恢复特定版本修订情况的系统. Git是一种分布式版本控制系统(Distributed Version Control Sy ...
- 转:extjs 添加loading状态的三种解决办法:
extjs 添加loading状态的三种解决办法: 方法一: //materialGrid 指需要显示loading状态的控件id var o=Ext.getCmp('materialGrid'); ...
- C#中??和?分别是什么意思? 在ASP.NET开发中一些单词的标准缩写 C#SESSION丢失问题的解决办法 在C#中INTERFACE与ABSTRACT CLASS的区别 SQL命令语句小技巧 JQUERY判断CHECKBOX是否选中三种方法 JS中!=、==、!==、===的用法和区别 在对象比较中,对象相等和对象一致分别指的是什么?
C#中??和?分别是什么意思? 在C#中??和?分别是什么意思? 1. 可空类型修饰符(?):引用类型可以使用空引用表示一个不存在的值,而值类型通常不能表示为空.例如:string str=null; ...
- nodejs高并发大流量的设计实现,控制并发的三种方法
nodejs高并发大流量的设计实现,控制并发的三种方法eventproxy.async.mapLimit.async.queue控制并发Node.js是建立在Google V8 JavaScript引 ...
- (OPC Client .NET 开发类库)网上很多网友都有提过,.NET开发OPC Client不外乎下面三种方法
1. 背景 OPC Data Access 规范是基于COM/DCOM定义的,因此大多数的OPC DA Server和client都是基于C++开发的,因为C++对COM/DCOM有最好的支持.现在, ...
- 【转载】取得系统中网卡MAC地址的三种方法
From:http://blog.csdn.net/zhangting1987/article/details/2732135 网卡地址这个概念有点混淆不清.因为实际上有两个地址,mac地址和物理地址 ...
随机推荐
- Oracle简单学习
最近一段时间重温了oracle关于存储过程和oracle包以及function中的定义, 先看一下要用的表: devices(id number, name varchar2, age number) ...
- Hexo-博客yilia主题创建分类(categories)和标签(tags)首页
转载自:http://orzcss.com/posts/5a207d64/ 概述 默认安装的 hexo 本身是没有分类和标签首页的,例如:http://orzcss.com/categories/页面 ...
- Selenium+Java环境搭建
1. 安装JDK URL:http://www.oracle.com/technetwork/java/javase/downloads/ 2. 配置环境变量 JAVA_HOME = E:\Java\ ...
- mysql 主从复制(mysql双机热备的实现)
转:http://blog.csdn.net/qq394829044/article/details/53203645 Mysql数据库没有增量备份的机制,当数据量太大的时候备份是一个很大的问题.还好 ...
- 《剑指offer》面试题7 用两个栈实现队列 Java版
书中方法:队列是先进先出的,栈是先进后出的,试想把一串数压入A栈,接着一个个出栈并压入B栈,便会完成"头在下"到"头在上"的转变.B栈内还有元素时,直接出栈表示 ...
- Vuejs中关于computed、methods、watch,mounted的区别
1.computed是在HTML DOM加载后马上执行的,如赋值: 2.methods则必须要有一定的触发条件才能执行,如点击事件: 3.watch呢?它用于观察Vue实例上的数据变动.对应一个对象, ...
- CodeChef GCD2
GCD2 Problem code: GCD2 Submit All Submissions All submissions for this problem are available. ...
- 2019牛客暑期多校训练营(第一场) - E - ABBA - 贪心 - dp - 组合
https://ac.nowcoder.com/acm/contest/881/E 从dp的角度来看是比较正常的.无后效性来源于前面只要的合法的方案分配,那么对后面造成的影响就只有A,B的数目. 从贪 ...
- P4553 80人环游世界(上下界费用流)
P4553 80人环游世界 emm......先从上下界网络流(转)开始 再到现在的上下界费用流 因为有上下界,我们需要记下每个点的流量差$ex[i]$,用于调整 $ins(x,y,l,r,v)=li ...
- CSRF——跨站请求伪造
一.CSRF是什么CSRF,全称:Corss-site request forgery,中文名称:跨站请求伪造.CSRF攻击比XSS攻击更具危险性,被安全界称为“沉睡的巨人”. 二.CSRF可以做什么 ...