State&生命周期

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);
//每秒调用一次tick,tick每秒执行一次ReactDOM.render()方法来更新视图。

本节封装Clock组件,组件将会设置自己的定时器,并每秒更新一次。

从封装时钟开始:

//封装Clock组件
function Clock(props){
return (
<div>
<h1>Hello,world!</h1>
<h2>It is {props.date.toLocaleTimeString()}</h2>
</div>
)
} //将组件在tick函数中使用 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,组件添加状态

状态与属性十分相似,但状态是私有的,完全受控于当前组件。

之前提到过,定义为类的组件有一些特性。局部状态就是如此:一个功能只适用于类。

将函数转换为类

可以通过五个步骤将函数组件Clock转换为类

  1. 创建一个名称扩展为React.ComponentES6类

  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现在被定义为一个类而不是一个函数

使用类就允许我们使用其他特性,例如局部状态,生命周期钩子。

为一个类添加局部状态

我们会通过3个步骤将date属性移动到状态中:

  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到基础构造函数的:

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

类组件应始终使用props条用基础构造函数。

  1. <Clock />元素移出data属性:
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中被称为挂载

同样,每当Clock生成的这个DOM被移除的时候,我们也要清除定时器,这在React中被称为卸载

我们可以再组件类上声明特殊的方法,当组件挂载或卸载时,来运行一些代码:

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>
)
)
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
}

这些方法被称为生命周期钩子函数

当组件输出到DOM后会执行componentDidMount()钩子,我们在这里建立定时器:

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

注意我们如何在this中白村定时器ID。

虽然this.props由React本身设置以及this.state具有特殊含义,但如果需要存储不用于视觉输出的东西,则可以手动向类中添加其他字段。

如果你不在render()中使用某些东西,它就不应该在状态中。

我们将在componentWillUnmount()生命周期钩子中卸载定时器:

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

最后我们实现了每秒中执行tick()的方法。

它将使用this.setState()来更新组件局部状态:

class Clock extends  React.Component{
constructor(props){
super(props);
this.state={date:new Date()}
}
componentDidMount(){ }
componentWillUnmount(){ }
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。我们稍后会更新此状态。

  2. React然后调用Clock组件的render()方法。这是React了解屏幕上应该显示什么内容,然后React更新DOM以匹配Clock的渲染输出。

  3. Clock的输出插入到DOM中时,React调用componentDidMount()生命周期钩子。再起中,Clock组件要求浏览器设置一个定时器,每秒钟调用一次tick().

  4. 浏览器每秒中调用tick()方法。在其中,Clock组件通过使用包含当前时间的对象调用setState(),来调度UI更新。通过调用setState(),React知道状态已经改变,并在此调用render(),方法来确定屏幕上应当显示什么。这一次,render()方法中的this.state.date将不同,所以渲染输出将包含更新的时间,并相应地更新DOM。

  5. 一旦Clock组件被从DOM中移除,React将会条用componentWillUnmount()这个钩子函数,并清除定时器。

正确的使用状态

关于this.setState()有三个地方需要知道

不要直接更新状态

例如,此代码不会重新渲染组件:

//Wrong

this.state.comment='hello';

应当使用this.setState():

//Correct

this.setState({comment:'hello'});

构造函数是唯一能够初始化this.state的地方。

状态更新可能是异步的

React可以将多个setState()调用和并成一个调用来提高性能。

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

例如,此代码可能无法更新计数器:

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

要修复它,请使用第二种形式的setState()来接受一个函数而不是一个对象。该函数将接收先前的状态作为第一个参数,将此次更新被应用时的props作为第二个参数:

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

上方代码使用了箭头函数,但它也适用于常规函数:

// Correct
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

数据自顶向下流动

父组件或子组件都不能知道某个组件是有状态还是无状态,并且它们不应该关心某组件是被定义为一个函数还是一个类。

这就是为什么状态通常被称为局部或封装。 除了拥有并设置它的组件外,其它组件不可访问。

组件可以选择将其状态作为属性传递给其子组件:

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

这也适用于用户定义的组件:

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

FormattedDate 组件将在其属性中接收到 date 值,并且不知道它是来自 Clock 状态、还是来自 Clock 的属性、亦或手工输入:

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

这通常被称为自顶向下单向数据流。 任何状态始终由某些特定组件所有,并且从该状态导出的任何数据或 UI 只能影响树中下方的组件。

如果你想象一个组件树作为属性的瀑布,每个组件的状态就像一个额外的水源,它连接在一个任意点,但也流下来。

为了表明所有组件都是真正隔离的,我们可以创建一个 App 组件,它渲染三个Clock

function App() {
return (
<div>
<Clock />
<Clock />
<Clock />
</div>
);
} ReactDOM.render(
<App />,
document.getElementById('root')
);

每个 Clock 建立自己的定时器并且独立更新。

在 React 应用程序中,组件是有状态还是无状态被认为是可能随时间而变化的组件的实现细节。 可以在有状态组件中使用无状态组件,反之亦然。

React State&生命周期的更多相关文章

  1. react.js 生命周期componentDidUpdate的另类用法:防止页面过渡刷新

    场景:数据新增成功之后,需要返回原来的查询表,这时候的查询,需要使用react的生命周期:componentDidUpdate componentDidUpdate() 这个生命周期的作用是当prop ...

  2. React的生命周期

    我们先来看一张图,其实看完这张图基本就懂了,如果还不懂,请继续往下看. getDefaultProps 执行过一次后,被创建的类会有缓存,映射的值会存在this.props,前提是这个prop不是父组 ...

  3. 附实例!图解React的生命周期及执行顺序

    本文由云+社区发表 作者:前端林子 1.七个可选的生命周期 可以结合下图来看: (1) componentWillMount() 仅在render()方法前被调用一次,如果在该方法中调用了setSta ...

  4. React之生命周期

    哈喽,这是我的第一篇博客,请大家多多关照~ 追根溯源:What's the lifeCycle? 生命周期函数指在某一时刻组件会自动调用执行的函数: React生命周期概览: 接下来我们就着生命周期的 ...

  5. React组件生命周期小结

    React组件生命周期小结 下面所写的,只适合前端的React.(React也支持后端渲染,而且和前端有点小区别,不过我没用过.) 相关函数 简单地说,React Component通过其定义的几个函 ...

  6. React—组件生命周期详解

    React—组件生命周期详解 转自 明明的博客  http://blog.csdn.net/slandove/article/details/50748473 (非原创) 版权声明:转载请注明出处,欢 ...

  7. React 函数生命周期

      React 函数生命周期基础 1 ,概念 在组件创建.到加载到页面上运行.以及组件被销毁的过程中,总是伴随着各种各样的事件,这些在组件特定时期,触发的事件,统称为组件的生命周期:* 2,组件生命周 ...

  8. 七天接手react项目 —— 生命周期&受控和非受控组件&Dom 元素&Diffing 算法

    生命周期&受控和非受控组件&Dom 元素&Diffing 算法 生命周期 首先回忆一下 vue 中的生命周期: vue 对外提供了生命周期的钩子函数,允许我们在 vue 的各个 ...

  9. 帮你理清React的生命周期

    这是一个从印记中文 | react官方文档提取总结的,算是帮自己理清并且强化记忆React的生命周期,以便以后编写组件的时候能够有更清晰的思路.本文如有纰漏,欢迎指正 整体上来讲,React生命周期分 ...

随机推荐

  1. bfs+dfs乱搞+类似拓扑排序——cf1182D

    代码不知道上了多少补丁..终于过了 用类似拓扑排序的办法收缩整棵树得到x,然后找到x直连的最远的和最近的点 只有这三个点可能是根,依次判一下即可 另外题解的第一种方法时找直径,然后判两端点+重心+所有 ...

  2. AutoLayout(自动布局)入门

    这是WWDC2012笔记系列中的一篇,完整的笔记列表可以参看这里.如果您是首次来到本站,也许您会有兴趣通过RSS的方式订阅本站. AutoLayout在去年的WWDC上被引入Cocoa,而在今年的WW ...

  3. 杂项-Maven-guava:guava

    ylbtech-杂项-Maven-guava:guava Guava是一种基于开源的Java库,其中包含谷歌正在由他们很多项目使用的很多核心库.这个库是为了方便编码,并减少编码错误.这个库提供用于集合 ...

  4. HDU-1492-The number of divisors(约数) about Humble Numbers -求因子总数+唯一分解定理的变形

    A number whose only prime factors are 2,3,5 or 7 is called a humble number. The sequence 1, 2, 3, 4, ...

  5. SQL的语言分类

    SQL的语言分类 DQL DQL(Data Query Language):数据查询语言 select DML DML(Data Manipulate Language):数据操作语言 insert ...

  6. Linux文本编辑命令

    sort 排序工具,比较排序(根据字典排序) -t 指定分隔符(默认是空格) -k 指定第几域排序(默认第一域) -n 以数字大小排序 -r 逆向排序 -v 去掉重复行 -o 输出到文件中 -c 测试 ...

  7. WPF基础之Grid面板

    一.显示 Grid的线条,设置ShowGridLiens="True".

  8. Kill- Linux必学的60个命令

    1.作用 kill命令用来中止一个进程. 2.格式 kill [ -s signal | -p ] [ -a ] pid ... kill -l [ signal ] 3.参数 -s:指定发送的信号. ...

  9. 学习 debug

    要在代码编辑器中设置源代码断点,有以下 4 种操作方式. (1) 把光标移到要设为断点的行上,按下 F5 键. (2) 用鼠标左键单击要设为断点的行的最左端. (3) 用鼠标右键单击要设为断点的行,在 ...

  10. odoo js

    1.相关库/框架 主要:jQuery(使用1.8.3,如果使用新版本,其他jQuery插件也要升级或修改).Underscore.QWeb 其他:都在addons\web\static\lib路径下. ...