React文档(六)state和生命周期
想一下之前的章节时钟的例子。
目前为止我们只学习了一直方式去更新UI。
我们调用ReactDOM.render()方法去改变渲染的输出:
function tick() {const element = (<div><h1>Hello, world!</h1><h2>It is {new Date().toLocaleTimeString()}.</h2></div> );ReactDOM.render(element,document.getElementById('root')); }setInterval(tick, 1000);
在CodePen中试一试。
在本章节,我们将学习怎样使Clock组件真正的可复用和封装起来。它将可以设置它自己的定时器并且每一秒更新自己。
我们可以先看看封装后的时钟是什么样子:
function Clock(props) {return (<div><h1>Hello, world!</h1><h2>It is {props.date.toLocaleTimeString()}.</h2></div> ); }function tick() {ReactDOM.render(<Clock date={new Date()} />,document.getElementById('root')); }setInterval(tick, 1000);
在CodePen中试一试。
然而,它遗漏了一个重要的条件:事实是Clock组件设置一个定时器并且每一秒更新UI这件事应该是Clock组件的一部分。
理想情况下,我们想要只写一次就可以让Clock更新自己:
ReactDOM.render(<Clock />,document.getElementById('root') );
想要实现,需要添加state到Clock组件里。
状态state类似于属性,但是它是私有的并且完全由组件来控制。
我们前面提到过组件如果用类定义会有一些额外的特性。局部状态就是如此:一个功能只适用于类。
将函数转变成类
你可以把类似Clock的函数组件转变成类,只需五个步骤:
- 创建一个ES6类,名字和函数的名字相同,并且继承React.Component。
- 给这个类添加一个空的方法起名为render()。
- 把函数的主体内容移动到render()方法内。
- 在render()内把属性props替换成this.props。
- 删除遗留的空的函数声明。
class Clock extends React.Component {render() {return (<div><h1>Hello, world!</h1><h2>It is {this.props.date.toLocaleTimeString()}.</h2></div> );} }
在CodePen里试一试。
Clock组件现在被定义成了类而不止是函数。
这样我们可以使用额外特性就像state和生命周期钩子ligecycle hooks。
为类添加状态
我们将把date从属性props里移动到state里,需要三个步骤:
1)在render()方法里将this.props.date替换成this.state.date:
class Clock extends React.Component {render() {return (<div><h1>Hello, world!</h1><h2>It is {this.state.date.toLocaleTimeString()}.</h2></div> );} }
2)添加一个class constructor 构造函数来分配初始的this.state值:
class Clock extends React.Component {constructor(props) {super(props);this.state = {date: new Date()};}render() {return (<div><h1>Hello, world!</h1><h2>It is {this.state.date.toLocaleTimeString()}.</h2></div> );} }
注意我们如何传递props给基本构造函数:
constructor(props) {super(props);this.state = {date: new Date()};}
类组件应该一直使用props作为参数调用基本构造函数。
3)从<Clock />元素中去除date属性:
ReactDOM.render(<Clock />,document.getElementById('root') );
我们在之后将重新添加定时器代码到组件内部。
结果会是这样:
class Clock extends React.Component {constructor(props) {super(props);this.state = {date: new Date()};}render() {return (<div><h1>Hello, world!</h1><h2>It is {this.state.date.toLocaleTimeString()}.</h2></div> );} }ReactDOM.render(<Clock />,document.getElementById('root') );
在CodePen里试一试。
接下来,我们会让Clock组件设置自己的定时器并且每一秒都更新自己。
为类添加生命周期方法
在一个有很多组件的应用里,当组件被销毁的时候释放资源是一件很重要的事情。
每当Clock被第一次渲染到DOM里,我们想要设置一个定时器。这个时刻被叫做“mounting”。
每当Clock生成的这个DOM被移除的时候,我们也想清除定时器。这个时刻被叫做“unmounting”。
我们可以在组件类里声明特殊的方法,当mount和unmount的状态的时候去运行这些方法。
class Clock extends React.Component {constructor(props) {super(props);this.state = {date: new Date()};}componentDidMount() {}componentWillUnmount() {}render() {return (<div><h1>Hello, world!</h1><h2>It is {this.state.date.toLocaleTimeString()}.</h2></div> );} }
这些方法被称为“生命周期钩子”lifecycle hooks。
当组件的输出被渲染到DOM中的时候,componentDidMount()钩子会执行。这里是最合适的地方设置一个定时器:
componentDidMount() {this.timerID = setInterval(() => this.tick(),1000);}
注意我们怎样正确保存定时器到this上。
this.props是React自己设置的,this.state有一种特殊的含义,如果你需要存储一些不需要视觉的输出,那么你可以随意手动添加额外的字段到类里面。
如果你不需要在render()使用什么东西,那那些东西就不会存在于state里。
在componentWillUnmount()生命周期钩子中我们将清除定时器:
componentWillUnmount() {clearInterval(this.timerID);}
最终,我们实现了tick()在每一秒都会运行。
this.setState()会安排更新组件的state。
class Clock extends React.Component {constructor(props) {super(props);this.state = {date: new Date()};}componentDidMount() {this.timerID = setInterval(() => this.tick(),1000);}componentWillUnmount() {clearInterval(this.timerID);}tick() {this.setState({date: new Date()});}render() {return (<div><h1>Hello, world!</h1><h2>It is {this.state.date.toLocaleTimeString()}.</h2></div> );} }ReactDOM.render(<Clock />,document.getElementById('root') );
在CodePen中试一试。
现在表每一秒都会滴答一下。
现在来快速地概括一下这段代码发生了什么以及调用方法的顺序:
1)当<Clock />被传递给ReactDOM.render(),React会调用Clock组件的构造函数。因为Clock组件需要显示当前时间,它就用一个包含当前时间的对象初始化this.state。之后我们会更新这个state。
2)之后React会调用Clock组件的render()方法。React就是这样得知应该把什么显示在屏幕上。之后React会更新DOM匹配Clock组件的渲染输出。
3)当Clock组件的输出被插入了DOM,React会调用componentDisMount()生命周期钩子函数。在钩子函数里,Clock组件会询问浏览器去设置一个定时器每秒调用一次tick()。
4)每一秒浏览器调用tick()方法。在tick里,Clock组件安排了UI的更新,通过调用setState()方法和一个参数,参数是一个包含当前时间的对象。多亏了setState()的调用,React知道了state被改变了,然后就会重新调用render()方法来得知什么应该被显示到屏幕上。在这个时刻,render()方法里的this.state.date会发生变化,一次渲染输出会包含更新了的时间。React会相应地更新DOM。
5)如果Clock组件在某时从DOM中移除,React会调用componentWillUnmount()生命周期钩子来停止定时器。
正确使用state
关于setState()你需要知道三件事:
不要直接改变state
举个例子,这样不会重新渲染DOM:
// Wrong this.state.comment = 'Hello';
应该使用setState():
// Correct this.setState({comment: 'Hello'});
你只能在构造函数里初始化this.state。
state的更新可能是异步的
React也许会批量将很多setState()调用放进一次更新里。
因为this.props和this.state也许会异步更新,所以你不应该依据它们的值来计算下一次state。
举个例子,以下代码更新counter也许会失败:
// Wrong this.setState({counter: this.state.counter + this.props.increment, });
要修改这个问题,使用一个第二种形态的setState(),它接受一个函数为参数而不是对象。那个函数会接受先前的state为第一个参数,第二个参数是当更新被应用的那个时候的props:
// Correct this.setState((prevState, props) => ({counter: prevState.counter + props.increment }));
上面我们用了箭头函数,但是也可以使用常规的函数:
// Correct this.setState(function(prevState, props) {return {counter: prevState.counter + props.increment}; });
state更新是合并了的
constructor(props) {super(props);this.state = {posts: [],comments: []};}
然后你可以分别调用setState()来独立地更新它们:
componentDidMount() {fetchPosts().then(response => {this.setState({posts: response.posts});});fetchComments().then(response => {this.setState({comments: response.comments});});}
合并是很浅的,所以this.setState({comments})没有改变this.state.posts,但是完全替换了this.state.comments。
数据自顶向下流动
父组件和子组件都不能知道某一个组件是有状态的还是无状态的,并且它们也不应该关心组件是函数的还是类的。
这就是为什么state经常被叫做本地的或者封装的。组件不能设置别人的state只能设置自己的。
一个组件也许会选择去传递它的state作为props给它的子组件:
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
<FormattedDate date={this.state.date} />
FormattedDate组件会从它的props里接受到date并且不会知道date是否来自于Clock的state,来自于Clock的props,或者是手动添加的:
function FormattedDate(props) {return <h2>It is {props.date.toLocaleTimeString()}.</h2>; }
在CodePen里试一试。
这样情况都被叫做“自顶向下”或者“单向的”数据流动。任何state都总是被一些特定的组件拥有,并且从state导出的任何数据或者UI只能影响比自己低的组件。
如果你想象一个组件树是一个props的瀑布,每一个组件的state就像一股附加的水流,会在任意点加入到瀑布中往下游流动。
为了表示所有的组件都是被隔离的,我们可以创建一个APP组件,用它来渲染三个<Clock>:
function App() {return (<div><Clock /><Clock /><Clock /></div> ); }ReactDOM.render(<App />,document.getElementById('root') );
转载于:https://www.cnblogs.com/hahazexia/p/6383020.html
React文档(六)state和生命周期相关推荐
- React 第五章 state 组件生命周期
本章我们主要讲解 组件之state使用,以及组建的生命周期,在本章我们将使用class进行组件编写. 1,组件的状态state 在之前我们讲过一个页面时间的例子,之前我们使用的属性props进行定时页 ...
- React Native组件的结构和生命周期
React Native组件的结构和生命周期 一.组件的结构 1.导入引用 可以理解为C++编程中的头文件. 导入引用包括导入react native定义的组件.API,以及自定义的组件. 1.1 导 ...
- 03 为什么 React 16 要更改组件的生命周期?(下)
通过对上一个课时的学习,你已经对 React 15 的生命周期有了系统的掌握和理解.本课时,我将在此基础上,对 React 16 以来的生命周期进行剖析.在理解"是什么"的基础上, ...
- Flutter State 的生命周期
本文主要介绍类比 Android 和 iOS,了解 Flutter State 的生命周期. 从 Android 或 iOS 转到 Flutter 开发,最让人疑惑的是 Flutter 如何处理生命周 ...
- [react] react的性能优化在哪个生命周期?它优化的原理是什么?
[react] react的性能优化在哪个生命周期?它优化的原理是什么? shouldComponentUpdate 减少不必要的重新渲染 个人简介 我是歌谣,欢迎和大家一起交流前后端知识.放弃很容易 ...
- [react] react的函数式组件有没有生命周期?
[react] react的函数式组件有没有生命周期? 没有 个人简介 我是歌谣,欢迎和大家一起交流前后端知识.放弃很容易, 但坚持一定很酷.欢迎大家一起讨论 主目录 与歌谣一起通关前端面试题
- [react] react16跟之前的版本生命周期有哪些变化?
[react] react16跟之前的版本生命周期有哪些变化? 个人简介 我是歌谣,欢迎和大家一起交流前后端知识.放弃很容易, 但坚持一定很酷.欢迎大家一起讨论 主目录 与歌谣一起通关前端面试题
- [react] react中修改prop引发的生命周期有哪几个?
[react] react中修改prop引发的生命周期有哪几个? static getDerivedStateFromPropsshouldComponentUpdaterendergetSnapsh ...
- React State和生命周期 3
微信小程序开发交流qq群 173683895 承接微信小程序开发.扫码加微信. 一:组件的生命周期 组件的生命周期可分成三个状态: 安装:已插入真实DOM 更新:正在被重新渲染 卸载:已移出 ...
最新文章
- linux网络编程一:主机字节序与网络字节序的的判断
- CentOS中升级openssl与卸载重装以及提示:error while loading shared libraries: libssl.so.1.1: cannot open shared ob
- NHibernate入门hello world
- github使用_简单使用Git与github
- marvell raid linux,华硕P7F-M (-MARVELL 88SE6145 SATA RAID)主板驱动-版下载,适用于win7,Win7-64,winxp-驱动精灵...
- 冰点还原离线激活_冰点还原密钥,小编教你如何激活冰点还原
- 配置MatConvNet
- 有赞订单导出的配置化实践
- C#实现QQ窗体的步骤和总结
- qlv转mp4绿色版工具
- QT下获取汉字拼音首字母
- 红色性格和蓝色性格的优缺点
- 熊猫烧香 给人的深思
- mong命令学习记录
- Discriminative Learning of Relaxed Hierarchy for Large-scale Visual Recognition
- 一看就会的Nginx学习教程(千万别告诉其他人),java视频百度云盘
- shell易错点整理
- 5-6中央处理器-多处理器系统硬件多线程
- Java基础编程\第四-六章(面向对象编程)
- Mybatis——增删改查CLUD
热门文章
- 互联网广告综述之点击率特征工程
- EXECL导入(检查服务器版本.包括NPOI方式导入.可以通过配置文件信息导入EXECL)代码记录下....
- Windows 网络服务架构系列课程详解(六) ---利用NLB群集实现WEB服务器的可靠性...
- 银行启动开放战略,能否赢回金融科技下半场?
- ACL控制访问列表原理+实验
- JavaScript面向对象与传统面向对象的一点区别
- 【SICP练习】91 练习2.64
- python django 安装
- 视频直播网站开发千万不能忘的一个知识点
- java程序解压/压缩.gz文件