状态和生命周期
State and Lifecycle
This page introduces the concept of state and lifecycle in a React component. You can find a detailed component API reference here.
这一页介绍React组件关于状态的概念和声明周期。你可以在这里找到组件涉及的详细api
Consider the ticking clock example from one of the previous sections. In Rendering Elements, we have only learned one way to update the UI. We call ReactDOM.render()
to change the rendered output:
思考下上一个部分那个时钟的例子。在渲染元素中,我门只学历一种方式去更新ui,我们调用React.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);
Try it on CodePen
在CodePen中试一试
In this section, we will learn how to make the Clock component truly reusable and encapsulated. It will set up its own timer and update itself every second.
在这个部分,我们将学习怎么让Clock组件实现真正的可复用和封闭性。它将建立它自己的定时器并每秒更新一次。
We can start by encapsulating how the clock looks:
我门开始学习如何去封装时钟的外观吧
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);
Try it on CodePen
在CodePen中尝试一下
However, it misses a crucial requirement: the fact that the Clock sets up a timer and updates the UI every second should be an implementation detail of the Clock.
然而,它忽略了一个关键的要求,时钟设置定时器并每秒钟更新,这个应该是定时器的实现细节
Ideally we want to write this once and have the Clock update itself:
理想情况下,我门想写一次就能让时Clock现自我更新
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
To implement this, we need to add “state” to the Clock component.
为了实现它,我门需要添加一个state在Clock组件里面
State is similar to props, but it is private and fully controlled by the component.
State和props很像,但是它是组件里面私有的并完全由该组件控制的
Converting a Function to a Class
把 一个Function 转化为 一个 Class
You can convert a function component like Clock to a class in five steps:
你可以通过五步把一个类似Clock这样的函数组件转化为class组件
- Create an ES6 class, with the same name, that extends
React.Component
. - Add a single empty method to it called
render()
. - Move the body of the function into the
render()
method. - Replace
props
withthis.props
in therender()
body. - Delete the remaining empty function declaration.
- 创建一个class,用相同的名称,如何extends React。Component
- 新增一个空的render 方法
- 移动function的内容到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>
);
}
}
Try it on CodePen
在CodePen上试一下
Clock is now defined as a class rather than a function.
Clock现在的定义为一个class而不是一个函数
The render method will be called each time an update happens, but as long as we render <Clock /> into the same DOM node, only a single instance of the Clock class will be used. This lets us use additional features such as local state and lifecycle methods.
render方法将会在每次更新的时候调用,但是一旦在同一个dom节点内渲染<Clock />,被使用的同一个Clock的实例。
This lets us use additional features such as local state and lifecycle methods.
这时,我们需要使用另外的特性例如组件内的state和生命周期方法
Adding Local State to a Class
加入一个本地状态到class中
We will move the date from props to state in three steps:
我们将通过三个步骤把props的数据移动到state
- Replace this.props.date with this.state.date in the render() method:
把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>
);
}
}
- Add a class constructor that assigns the initial
this.state
:
加入一个class的构造器属性初始化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>
);
}
}
Note how we pass props to the base constructor:
注意我们是怎么通过传递props到一个基础的构造器里面的
constructor(props) {
super(props);
this.state = {date: new Date()};
}
Class components should always call the base constructor with props.
class组件应该总是通过调用构造器属性获得props
3.Remove the date prop from the <Clock /> element:
移除<Clock />组件上的date prop
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
We will later add the timer code back to the component itself.
我们将在后面往组件内添加定时器代码
The result looks like this:
结果看起来就像这样
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')
);
Next, we’ll make the Clock
set up its own timer and update itself every second.
下一步,我门将建立Clock自己的定时器实现每一秒更新自己
Adding Lifecycle Methods to a Class
加入生命周期方法到Class内
In applications with many components, it’s very important to free up resources taken by the components when they are destroyed.
在具有许多组件的应用中,当组件被销毁的时候释放占用的内存是非常重要的。
We want to set up a timer whenever the Clock
is rendered to the DOM for the first time. This is called “mounting” in React.
我们想在Clock被渲染成dom之后的第一时间建立一个定时器,这个时候在React被称为“mounting”
We also want to clear that timer whenever the DOM produced by the Clock
is removed. This is called “unmounting” in React.
我们也想要在Clock组件被清除的时候清空定时器。这个时候在React被称为“unmounting”
We can declare special methods on the component class to run some code when a component mounts and unmounts:
我们可以在组件mounts或unmounts的时候调用一个特殊的方法去执行一些代码
These methods are called “lifecycle methods”.
这些方法被称为生命周期
The componentDidMount() method runs after the component output has been rendered to the DOM. This is a good place to set up a timer:
这个componentDidMount方法在组件以及被渲染之后调用,这时一个创建定时器的好地方
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
Note how we save the timer ID right on this (this.timerID).
注意我们怎么正确通过this保存定时器的id
While this.props is set up by React itself and this.state has a special meaning, you are free to add additional fields to the class manually if you need to store something that doesn’t participate in the data flow (like a timer ID).
尽管this.prrops和this.state都是React自己创建的,且都有特殊的意思,但是你以可以手动的添加一些额外的字段给class去储存一些不参与数据流的字段
We will tear down the timer in the componentWillUnmount() lifecycle method:
我们将清除那个定时在在ComponentWillUnmount的生命周期当中
componentWillUnmount() {
clearInterval(this.timerID);
}
Finally, we will implement a method called tick() that the Clock component will run every second.
最后,我门将实现一个方法调用Clock组件使它每一秒都运转起来
It will use this.setState() to schedule updates to the component local state:
这个方法是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')
);
Now the clock ticks every second.
现在时钟每一秒都会刷新
Let’s quickly recap what’s going on and the order in which the methods are called:
让我门快速回顾一下发生了些什么和这些方法被调用的顺序
When <Clock /> is passed to ReactDOM.render(), React calls the constructor of the Clock component. Since Clock needs to display the current time, it initializes this.state with an object including the current time. We will later update this state.
- 当把<Clock />传递给ReactDom。render(),React会调用Clock的构造器属性。因为Clock需要展示当前时间,它会初始化this。state,这是一个包含当前时间的对象。我们将在后面更新这个state
React then calls the Clock component’s render() method. This is how React learns what should be displayed on the screen. React then updates the DOM to match the Clock’s render output.
- 然后React 调用Clock 组件的render()方法。这是React知道在屏幕上要展现什么的方式。然后React会匹配Clock的渲染输出更新dom
When the Clock output is inserted in the DOM, React calls the componentDidMount() lifecycle method. Inside it, the Clock component asks the browser to set up a timer to call the component’s tick() method once a second.
- 当Clock 输出插入到dom之后,React调用componentDidMount生命周期方法。在这里,Clock组件会要求浏览器构建一个定时器每一秒去调用组件的tick()方法
Every second the browser calls the tick() method. Inside it, the Clock component schedules a UI update by calling setState() with an object containing the current time. Thanks to the setState() call, React knows the state has changed, and calls the render() method again to learn what should be on the screen. This time, this.state.date in the render() method will be different, and so the render output will include the updated time. React updates the DOM accordingly.
4.每一秒浏览器都会调用tick方法,在这里,Clock组件通过调用setState()传入一个包含当前时间的对象去安排ui更新。因为setState()的调用,React知道state改变了,并调用render方法再次去拿到当前屏幕展示的元素。在这时,this。state。data在render()方法厘米昂将会改变,所以渲染输出将会包含更新的时间。因此React更新了dom
If the Clock component is ever removed from the DOM, React calls the componentWillUnmount() lifecycle method so the timer is stopped.
5.如果Clock组件一旦从dom中一出。React会调用componeWillUnmount()生命周期方法使定时器被停止
Using State Correctly
正确的使用state
There are three things you should know about setState()
.
关于setState有三件事情是你应该要知道的
Do Not Modify State Directly
不要直接修改State
For example, this will not re-render a component:
例如,这样操作并不会引起组件的再次渲染
this.state.comment = 'Hello';
Instead, use setState():
相反,我门使用setState
// Correct
this.setState({comment: 'Hello'});
The only place where you can assign this.state
is the constructor.
你只有在构造器里面才可以直接赋值this。state的
State Updates May Be Asynchronous
状态更新可能是异步的
React may batch multiple setState() calls into a single update for performance.
React可能会把多个setState调用合成一个
Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.
因为this。props和this。state可能是异步更新的,你不应该依赖他们的指去计算新的state
For example, this code may fail to update the counter:
例如,这个代码可能会导致conter更新失败
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
To fix it, use a second form of setState() that accepts a function rather than an object. That function will receive the previous state as the first argument, and the props at the time the update is applied as the second argument:
正确的做法是使用setState()接收一个函数而不是一个对象。这个函数将会接收上一次的state作为第一个参数,和此次被更新调用的props作为第二个参数
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
We used an arrow function above, but it also works with regular functions:
在上面的例子中,我们使用了一个箭头函数,但是它也可以使用普通函数
// Correct
this.setState(function(state, props) {
return {
counter: state.counter + props.increment
};
});
State Updates are Merged
状态更新和合并
When you call setState(), React merges the object you provide into the current state.
当你调用setState(),Reacft会合并你当前提供的对象和state对象
For example, your state may contain several independent variables:
例如,你的state可能包含几个独立的变量
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
Then you can update them independently with separate setState() calls:
当你各自通过setState去更新他们时
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
The merging is shallow, so this.setState({comments}) leaves this.state.posts intact, but completely replaces this.state.comments.
这样的合并是浅合并,所以this.setState({comments})完整的保留了this.state.posts.但是完全的替换了this.state.comments
The Data Flows Down
数据是向下流动的
Neither parent nor child components can know if a certain component is stateful or stateless, and they shouldn’t care whether it is defined as a function or a class.
无论父组件还是子组件都无法知道某个组件是有状态的还是无状态的,并且他们也不会关心是函数组件还是class组件
This is why state is often called local or encapsulated. It is not accessible to any component other than the one that owns and sets it.
这是为什么状态通常是局部调用或者封闭的。除了拥有并设置它的组件,其他都无法去访问它
A component may choose to pass its state down as props to its child components:
一个组件可以选择向下传递它的状态给子组件的props
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
This also works for user-defined components:
这在自定义组件同样适用
<FormattedDate date={this.state.date} />
The FormattedDate component would receive the date in its props and wouldn’t know whether it came from the Clock’s state, from the Clock’s props, or was typed by hand:
FormattedDate 组件将会在它的props里接收到date而且它并不知道是来自于Clock的state,来自于Clock的props又或者是手动传入的
function FormattedDate(props) {
return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}
This is commonly called a “top-down” or “unidirectional” data flow. Any state is always owned by some specific component, and any data or UI derived from that state can only affect components “below” them in the tree.
这是种方式通常会被称为由上而下或者是单向的数据流。任何state总是属于特定的组件,并且从它衍生出来的任何数据或者ui都只能影响在树中在它下方的组件
If you imagine a component tree as a waterfall of props, each component’s state is like an additional water source that joins it at an arbitrary point but also flows down.
如果你把一个组件树想象成一个props数据的瀑布。那么每一个组件的state就像在任意的点上加的一个额外的水资源,但是它们也是向下流动的
To show that all components are truly isolated, we can create an App component that renders three <Clock>s:
为了展示所有组件是独立的,我们可以创建一个app组件渲染三个<Clock >
function App() {
return (
<div>
<Clock />
<Clock />
<Clock />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
Each Clock sets up its own timer and updates independently.
每一个Clock都会建立它们自己的定时器和独立的更新数据
In React apps, whether a component is stateful or stateless is considered an implementation detail of the component that may change over time. You can use stateless components inside stateful components, and vice versa.
在React 应用中,一个组件是有状态或者是无状态的是组件内部实现的细节。它可能会随着时间推移而改变。你可以在一个有状态组件内使用无状态组件,反之亦然
网友评论