状态和生命周期

这篇介绍 React 组件中状态和声明周期的概念。详情可以查看API参考 。

思考前一部分中时钟的例子。渲染元素中,我们仅学习了一种更新 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'));
}

在线尝试

这部分,我们学习如何编写真正可复用的封装 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)

[在线尝试]()

但这个忽视了一个最重要的需求: Clock 创建一个计时器且每秒更新自身 UI 应该是一个 Clock 的细节实现。

理想情况下我们写一次让 Clock 更新自身:

ReactDOM.render(<Clock />,document.getElementById('root')
);

实现这一需求我们需要给 Clock 组件添加 "state".

State 很类似 props, 不同的是它完全私有由组件控制。

函数转化为类

转化一个类似 Clock 的函数组件为类组件需要五步:

  1. 创建一个 [ES6标准的类](), 名称不变,继承 React.Component.
  2. 重写 render()方法。
  3. 函数的祖逖移到 render() 方法中。
  4. render() 方法体重使用 this.props替换 props
  5. 删除空的函数声明。
class Clock extends React.Component {render() {return (<div><h1>Hello, world!</h1><h2>It is {this.props.date.toLocaleTimeString()}.</h2></div>);}
}

[在线尝试]()

函数组件定义的 Clock 现在由类组件定义。

render() 方法会在每次更新时调用,但是只要我们渲染 <Clock /> 到同样的 DOM 节点,就会使用 Clock 类的单一实例。这让我们可以使用如 local state 和 lifecycle 钩子等额外的特性。

为类添加本地状态

三步把 date 从 props 移动到 state:

  1. render() 方法中使用 this.state.date 替换 this.props.date
class Clock extends React.Component {render() {return (<div><h1>Hello, world!</h1><h2>It is {this.state.date.toLocaleTimeString()}.</h2></div>);}
}
  1. 添加一个[类构造器]()来分管 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 到基础构造器:

  construct(props) {super(props);this.state = {date: new Date()};}

类组件总是通过 props 调用基础构造器。

  1. 移除<Clock /> 元素中的 date props :
  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')
);

[在线尝试]()

下面,我们编写 Clock 设置他自己的计时器每秒更新自身。

为类添加声明周期方法

多个组件的应用中,当组件销毁时释放组件占用的资源非常重要。

我们想 [设置一个计时器]() 无论何时 Clock 第一次被渲染到 DOM. React 中称之为 ”mounting".

我们也想 [清楚一个计时器]() 无论何时 Clock 被DOM 移除。 React 中称之为 “unmounting".

当组件 mounts 和 unmounts 时我们可以在组件类声明特殊的方法来运行。

class Clock extends React.Component {constructor(props) {super(props);this.state = {date: new Date()};}componentDidMount() {}componentWillMount() {}render() {return (<div><h1>Hello, world!</h1><h2>It is {this.state.date.toLocaleTimeString()}.</h2></div>);}
}

这些方法称为 "lifecycle hooks(生命周期钩子)".

componentDidMount() 钩子在组件输出渲染至DOM 后运行。这个位置很适合建立一个计时器:

  componentDidMount() {this.timerID = setInterval(() => this.tick(), 1000);}

注意我们如何正确的将 计时器 ID 保存到 this.

当 React 设置 this.props this.state 有了特别的含义,你可以随意为类手动添加额外字段,如果你需要保存一些不参与数据流(比如 timerID)。

我们在 componentWillUnmount() 生命周期钩子函数中去掉 计时器。

  componentWillUnmount() {clearInterval(this.timerID);}

最后我们,实现一个称为 tick() 的方法实现 Clock 组件每秒运行。

这会用到 this.setState() 来调度更新组件的本地状态:

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')
);

在线尝试

现在时钟按秒运行。

快速的回顾下发生了什么还有方法调用顺序:

  1. <Clock /> 传给 ReactDOM.render(), React 调用Clock 组件的构造器。从 Clock 需要显示当前时间,它通过包含当前时间的对象初始化 this.state。稍后更新 state.
  2. React 调用 Clock 组件的 render() 方法,知道应该在屏幕上显示什么。React 之后更新 DOM 来匹配 Clock 的渲染后输出。
  3. Clock 的输出被插入 DOM, React 调用 componentDidMount()生命周期钩子。在方法内部,Clock 组件让浏览器设置一个计时器按秒来调用组件的 tick()方法。
  4. 浏览器按秒调用 tick() 方法。其中,Clock组件通过包含当前时间的对象调用setState() 来调度 UI 更新。React通过 setState() 犯法调用知晓组件状态发生改变,随后调用 render() 方法再次知晓屏幕上应该显示什么。这时,render() 方法中 this.state.date 会发生改变,因此渲染结果将会包含更新后的时间。React 相应的更新 DOM.
  5. 如果 Clock 组件一旦移除DOM, React 调用 componentWillUnmount() 生命周期钩子,然后计时器停止。

正确的更新 State

关于setState() 的三个须知:

不要直接修改修改 State

例如,这样不会 重新渲染一个组件:

// Wrong
this.state.comment = 'Hello';

应该使用 setState():

// Correct
this.setState({comment: 'Hello'});

构造器是唯一可为 this.state 赋值的地方。

State 更新可能同步

React 为了性能可能批量多次 调用 setState() 一个单独的更新。

因为 this.propsthis.state 可能异步更新,不应该依赖它们的值来计算下一个状态。

例如,如下代码可能更新计数器失败:

// Wrong
this.setState({counter: this.state.counter + this.props.increment,
});

修复这个问题,使用 setState() 的第二种形式,接受函数而不是一个对象。这个函数接受之前的 state 作为第一个参数, 当时间更新时 props 作为第二个参数。

// Correct
this.setState((prevState, props) => ({counter: prevState.counter + props.increment
}));

上面的例子中我们使用了 [箭头函数](),但常规函数也是可以的。

// Correnct
this.setState(function(prevState, props) {return {counter: prevState.counter + props.increment};});

状态更新合并

当调用 setState(), React会合并你提供给当前状态的对象。

例如, 你的状态可能包含多个独立的变量:

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 向下作为 props 给它的子组件:

<h2>It is {this.state.date.toLocaleTimeString()}.</h2>

这对用户自定义组件也同样有效:

<FormattedDate date={this.state.date} />

FormattedDate 组件接受它 props 中的 date,并不知道他来自 Clock的 state,还是来自 Clock 的 props, 或者是手动输入:

function FormattedDate(props) {return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}

在线尝试

这个通常称为 "top-down(自上而下)" 或者 "unidirectional(单向)" 数据流。任何 state 总数被特定的组件所拥有,任何通过 state 传递的数据或 UI 都只能影响树形结构的下方组件。

你可以家乡组件树是一个 props 瀑布,每个组件的状态就像一个额外的水源在随机点加入它同时向下流。
为展示所有组件真正独立,我们创建一个 App 组件 渲染三个 <Clock />:

function App() {
return (

<div><Clock /><Clock /><Clock />
</div>
);

}

ReactDOM.render() {
<App />,
document.getElementById('root')

[在线尝试]()每个 **Clock** 设置他自己的计时器独立更新他们。React 应用中,无论一个组件是有状态还是无状态都被当做一个组件可随时间改变的实现细节。
你可在有状态组件中使用无状态组件,反之亦然。

React文档 state and lifecycle相关推荐

  1. React文档(六)state和生命周期

    想一下之前的章节时钟的例子. 目前为止我们只学习了一直方式去更新UI. 我们调用ReactDOM.render()方法去改变渲染的输出: function tick() {const element ...

  2. React文档(一)安装

    React是一个灵活的可以用于各种不同项目的框架,你可以用它来写新应用,你也可以逐步将它引进已有的代码库而不用重写整个项目. 试用React 如果你想玩一玩React,那么就去CodePen上试一试. ...

  3. React文档(七)处理事件

    React元素处理事件和DOM元素处理事件很类似.下面是一些语法的不同之处: React事件的命名是用驼峰命名,而不是小写字母. 利用JSX你传递一个函数作为事件处理器,而不是一个字符串. 举个例子, ...

  4. React文档(五)组件和props

    组件可以让你将UI分割成独立的,可复用的模块,然后考虑将每个模块彼此隔离. 从概念上理解,组件就像js中的函数.他们接受随意的输入(被称为props)然后返回React元素来描述屏幕上应该出现什么. ...

  5. React文档(十九)不使用ES6

    通常你会将一个React组件定义成一个普通的js类: class Greeting extends React.Component {render() {return <h1>Hello, ...

  6. React文档(十八)最佳性能

    在内部,React使用好几种聪明的技巧去最小化更新UI所需要的DOM操作.对于很多应用来说,使用React会使得构建用户界面非常之快而且不需要做太多专门的性能优化.虽然如此,还是有一些方法可以让你为R ...

  7. React文档(十四)深入JSX

    根本上,JSX只是为React.createElement(component, props, ...children)函数提供语法糖.JSX代码是这样的: <MyButton color=&q ...

  8. Onsen UI for React文档

    注:采用ES6+JSX语法 1.开始一个项目 在React中使用Onsen UI 需要首先安装onsenui和react-onsenui模块. 可以使用monaca CLI工具包快速初始化一个应用: ...

  9. React文档(十五)使用propTypes进行类型检查

    注意: React.PropTypes 自 React v15.5 起已弃用.请使用 prop-types 库代替. 随着你的应用的开发,你会使用类型检查的方法来捕获很多bug.对于一些应用,你可以使 ...

最新文章

  1. 高并发场景下,到底先更新缓存还是先更新数据库?
  2. 部署 Communicator Web Access
  3. python idea控制台中文乱码_python 解决cv2绘制中文乱码问题
  4. Javascript - Jquery - 事件
  5. Scala 中的文件操作
  6. p2762-太空飞行计划问题【网络流,最大权闭合图,最小割】
  7. 使用WildFly和Java EE 7映像与Docker提供者一起流浪
  8. 计算机科学与技术专业改革,浅析计算机科学与技术专业教学改革
  9. 读书笔记《高性能网站建设指南:前端工程师技能精髓》
  10. Android stdio安装
  11. 用c语言将学生系统插入音效,增加音效.cpp
  12. 【VBA】Str与CStr辨析
  13. 拓路前行-TDSQL追求极致体验的这一路
  14. 修改并完善框架协议、合同的类型、有效期及目标值等
  15. echarts中设置柱状图(条形图)为横向展示-每条线上不同颜色
  16. 华为od统一考试B卷【分月饼】C++ 实现
  17. NB-IOT开发|nbiot开发教程《三》AT指令类模组驱动-STM32实现AT指令状态机
  18. js原生写图片轮播和切换
  19. Nginx简单配置转发
  20. 【XXE漏洞01】XML漏洞原理及实验

热门文章

  1. 关于android布局的两个属性dither和tileMode,background 平铺
  2. ARKIT/ARCore对比分析(一)
  3. Centos5.6 VNC安装配置【无错版】
  4. 在Asp.Net中从sqlserver检索(retrieve)图片
  5. Spring源码分析【1】-Tomcat的初始化
  6. 朴素、Select、Poll和Epoll网络编程模型实现和分析——Poll模型
  7. Windows 64位机上C/C++代码静态检查工具Logiscope RuleChecker的安装和使用
  8. 运动目标检测__光流法
  9. 解决Windows7下vs2008 Active control test container 不能运行的问题
  10. 【OpenCV】正确创建用于保存YUV420P格式的cv::Mat