您的位置:首页 > Web前端 > React

React中文文档之State and Lifecycle

2016-11-17 22:53 459 查看
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);


在这一小节,我们将学习如何创建真实地可重用的以及封装的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')
);


为了实现这个,我们需要添加 'state' 到 Clock 组件。

state同props非常相似,但是它是私有的,而且完全被组件自身控制

我们前面提到过,以类方式,定义的组件,有一些额外的特性。

局部state就是类可用的功能之一

将函数式组件,转换成类组件

你可以传唤一个函数式组件,例如 Clock组件,到一个类组件,通过5个步骤:

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

这就让我们可以使用 '附加特性',例如:局部state和生命周期钩子

向类中添加局部state

我们通过3步,将props中的data移动到state:

1.在render()方法中,使用 'this.state.data' 替换 'this.props.date'

class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>	// 这行
</div>
);
}
}


2.添加类的构造器,分配初始的 '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)

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


组件类,应该总是传入 'props' 调用基础构造器

3.从<Clock />元素上,移除 'date' prop

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组件首次渲染为dom时,我们设置了一个计时器(set up a timer)。这在React中,被称作 'mounting'

当移除由clock组件产生的dom时,我们清除mount设置的计时器。这在React中,被称作 'unmouting'

我们可以在组件类中,声明一些特殊的方法,当组件 mount 和 unmount 时,来运行我们设置的代码:

class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {data: new Date()};
}
componentDitMount(){

}
componentWillUnmount(){

}
render() {
return (
<h1>It is {this.state.date.toLocaleTimeString()}.</h1>
);
}
}


这些方法被称作 '生命周期 钩子'

组件输出已经被渲染为dom后,运行 'componentDitMount()' 钩子。这是个设置计时器的好地方:

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


注意我们是如何在 'this' 对象上,保存 计时器ID的。

this.props由React自身创建,而this.state有特殊的意义,如果你需要存储一些数据,且这些数据并不用于可见输出(the visual output),你可以手动给组件类添加额外的字段

如果你在 render() 中不使用其他字段,则不应该在 state 中添加这些字段

我们将在 'componentWillunmount' 生命周期钩子中,清除计时器

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


最后,我们将实现 tick() 方法,将会每秒运行一次

tick() 方法,使用 'this.setState()' 来更新组件的私有state

tick() {
this.setState({
data: new Date();
});
}


正确使用 state

关于setState(),你应该知道3件事:

1.不要直接修改state

例如,下面的代码,将不会重新渲染组件:

this.state.comment = 'Hello';			// 错误

使用 setState() 来代替:

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

2.state的更新,可能异步的

为了性能考虑,React可以在一次更新中,批量执行 setSate() 调用

因为 this.props 和 this.state 可能被异步更新,在计算接下来的state,你不应该依赖它们的值(可能还未被更新)

例如,下面的更新counter的代码可能失败:

this.setState({
counter: this.state.counter + this.props.increment;		// 错误
});


为了修复上面的错误,使用setState()的第二种形式,接收一个函数(function),而非一个对象(object)。函数接收上一次的state作为第一个参数,更新应用时的props作为第二个参数:

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


上面,我们使用了一个 '箭头函数'(arrow function),也可以使用一般的函数:

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


3.state更新会被合并

当调用 'setState()'时,React会合并 你提供的对象(the object you provide) 到当前的state。

例如:提供的state可能包含几个独立的变量:

constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}


接着,可以使用独立的 setState() 调用来分别更新它们:

componentDitMount() {
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

数据流向下传递(the data flows down)

父组件和子组件,都不知道某个组件是 'stateful' 还是 'stateless',而且,它们不关心它们被定义为一个函数还是类。

这就是为什么state经常被称作 局部(local) 或者 包装、封装(encapsulated)。对于除了拥有并设置它的组件之外的任意其他组件,它是不可访问的。

同 props 一样,组件也可以选择,将它的state传递给它的子组件:

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

对用户自定义组件也是有效的:

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

FormattedDate 组件接收props中的date,但是并不知道它是来自'clock' 的state,props,还是手动输入:

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


这通常被称为“自上而下”或“单向”的数据流。任何由某些特定组件拥有的state,任何来自该state的数据或UI,仅仅会影响树形结构中,它们的下级组件.

想象一个组件树是一个props瀑布,每个组件的state就像一个附加的水源,在任意点加入到瀑布中,但是也向下流动。

为了显示所有的组件是完全隔离的,我们可以创建一个app组件,渲染3个 clock 组件:

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


每个Clock设置它自己的计时器,并且独立的更新。

在React应用中,不论组件是有状态的(stateful),还是无状态的(stateless),都被认为是组件的实现细节,可以随着时间而改变。你可以在有状态的组件中使用无状态组件,反过来也一样。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  React React中文文档