狀態(tài)和生命周期
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.
這一頁(yè)介紹React組件關(guān)于狀態(tài)的概念和聲明周期入问。你可以在這里找到組件涉及的詳細(xì)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:
思考下上一個(gè)部分那個(gè)時(shí)鐘的例子邑闺。在渲染元素中嚷掠,我門只學(xué)歷一種方式去更新ui团甲,我們調(diào)用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.
在這個(gè)部分稚补,我們將學(xué)習(xí)怎么讓Clock組件實(shí)現(xiàn)真正的可復(fù)用和封閉性摸屠。它將建立它自己的定時(shí)器并每秒更新一次鲜结。
We can start by encapsulating how the clock looks:
我門開(kāi)始學(xué)習(xí)如何去封裝時(shí)鐘的外觀吧
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.
然而域那,它忽略了一個(gè)關(guān)鍵的要求,時(shí)鐘設(shè)置定時(shí)器并每秒鐘更新审编,這個(gè)應(yīng)該是定時(shí)器的實(shí)現(xiàn)細(xì)節(jié)
Ideally we want to write this once and have the Clock update itself:
理想情況下撼班,我門想寫一次就能讓時(shí)Clock現(xiàn)自我更新
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
To implement this, we need to add “state” to the Clock component.
為了實(shí)現(xiàn)它,我門需要添加一個(gè)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
把 一個(gè)Function 轉(zhuǎn)化為 一個(gè) Class
You can convert a function component like Clock to a class in five steps:
你可以通過(guò)五步把一個(gè)類似Clock這樣的函數(shù)組件轉(zhuǎn)化為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.
- 創(chuàng)建一個(gè)class砰嘁,用相同的名稱,如何extends React勘究。Component
- 新增一個(gè)空的render 方法
- 移動(dòng)function的內(nèi)容到render方法里面去
- 吧render內(nèi)容里面的props替換為this矮湘。props
- 刪除遺留的空函數(shù)的聲明
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現(xiàn)在的定義為一個(gè)class而不是一個(gè)函數(shù)
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方法將會(huì)在每次更新的時(shí)候調(diào)用,但是一旦在同一個(gè)dom節(jié)點(diǎn)內(nèi)渲染<Clock />口糕,被使用的同一個(gè)Clock的實(shí)例缅阳。
This lets us use additional features such as local state and lifecycle methods.
這時(shí),我們需要使用另外的特性例如組件內(nèi)的state和生命周期方法
Adding Local State to a Class
加入一個(gè)本地狀態(tài)到class中
We will move the date from props to state in three steps:
我們將通過(guò)三個(gè)步驟把props的數(shù)據(jù)移動(dòng)到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
:
加入一個(gè)class的構(gòu)造器屬性初始化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:
注意我們是怎么通過(guò)傳遞props到一個(gè)基礎(chǔ)的構(gòu)造器里面的
constructor(props) {
super(props);
this.state = {date: new Date()};
}
Class components should always call the base constructor with props.
class組件應(yīng)該總是通過(guò)調(diào)用構(gòu)造器屬性獲得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.
我們將在后面往組件內(nèi)添加定時(shí)器代碼
The result looks like this:
結(jié)果看起來(lái)就像這樣
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自己的定時(shí)器實(shí)現(xiàn)每一秒更新自己
Adding Lifecycle Methods to a Class
加入生命周期方法到Class內(nèi)
In applications with many components, it’s very important to free up resources taken by the components when they are destroyed.
在具有許多組件的應(yīng)用中十办,當(dāng)組件被銷毀的時(shí)候釋放占用的內(nèi)存是非常重要的。
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之后的第一時(shí)間建立一個(gè)定時(shí)器超棺,這個(gè)時(shí)候在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組件被清除的時(shí)候清空定時(shí)器向族。這個(gè)時(shí)候在React被稱為“unmounting”
We can declare special methods on the component class to run some code when a component mounts and unmounts:
我們可以在組件mounts或unmounts的時(shí)候調(diào)用一個(gè)特殊的方法去執(zhí)行一些代碼
These methods are called “l(fā)ifecycle 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:
這個(gè)componentDidMount方法在組件以及被渲染之后調(diào)用,這時(shí)一個(gè)創(chuàng)建定時(shí)器的好地方
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
Note how we save the timer ID right on this (this.timerID).
注意我們?cè)趺凑_通過(guò)this保存定時(shí)器的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自己創(chuàng)建的说搅,且都有特殊的意思炸枣,但是你以可以手動(dòng)的添加一些額外的字段給class去儲(chǔ)存一些不參與數(shù)據(jù)流的字段
We will tear down the timer in the componentWillUnmount() lifecycle method:
我們將清除那個(gè)定時(shí)在在ComponentWillUnmount的生命周期當(dāng)中
componentWillUnmount() {
clearInterval(this.timerID);
}
Finally, we will implement a method called tick() that the Clock component will run every second.
最后,我門將實(shí)現(xiàn)一個(gè)方法調(diào)用Clock組件使它每一秒都運(yùn)轉(zhuǎn)起來(lái)
It will use this.setState() to schedule updates to the component local state:
這個(gè)方法是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.
現(xiàn)在時(shí)鐘每一秒都會(huì)刷新
Let’s quickly recap what’s going on and the order in which the methods are called:
讓我門快速回顧一下發(fā)生了些什么和這些方法被調(diào)用的順序
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.
- 當(dāng)把<Clock />傳遞給ReactDom适肠。render()霍衫,React會(huì)調(diào)用Clock的構(gòu)造器屬性。因?yàn)镃lock需要展示當(dāng)前時(shí)間侯养,它會(huì)初始化this敦跌。state,這是一個(gè)包含當(dāng)前時(shí)間的對(duì)象逛揩。我們將在后面更新這個(gè)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 調(diào)用Clock 組件的render()方法柠傍。這是React知道在屏幕上要展現(xiàn)什么的方式。然后React會(huì)匹配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.
- 當(dāng)Clock 輸出插入到dom之后辩稽,React調(diào)用componentDidMount生命周期方法惧笛。在這里,Clock組件會(huì)要求瀏覽器構(gòu)建一個(gè)定時(shí)器每一秒去調(diào)用組件的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.每一秒瀏覽器都會(huì)調(diào)用tick方法逞泄,在這里患整,Clock組件通過(guò)調(diào)用setState()傳入一個(gè)包含當(dāng)前時(shí)間的對(duì)象去安排ui更新。因?yàn)閟etState()的調(diào)用喷众,React知道state改變了各谚,并調(diào)用render方法再次去拿到當(dāng)前屏幕展示的元素。在這時(shí)到千,this昌渤。state。data在render()方法厘米昂將會(huì)改變憔四,所以渲染輸出將會(huì)包含更新的時(shí)間膀息。因此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會(huì)調(diào)用componeWillUnmount()生命周期方法使定時(shí)器被停止
Using State Correctly
正確的使用state
There are three things you should know about setState()
.
關(guān)于setState有三件事情是你應(yīng)該要知道的
Do Not Modify State Directly
不要直接修改State
For example, this will not re-render a component:
例如加矛,這樣操作并不會(huì)引起組件的再次渲染
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.
你只有在構(gòu)造器里面才可以直接賦值this。state的
State Updates May Be Asynchronous
狀態(tài)更新可能是異步的
React may batch multiple setState() calls into a single update for performance.
React可能會(huì)把多個(gè)setState調(diào)用合成一個(gè)
Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.
因?yàn)閠his斟览。props和this。state可能是異步更新的辑奈,你不應(yīng)該依賴他們的指去計(jì)算新的state
For example, this code may fail to update the counter:
例如苛茂,這個(gè)代碼可能會(huì)導(dǎo)致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()接收一個(gè)函數(shù)而不是一個(gè)對(duì)象。這個(gè)函數(shù)將會(huì)接收上一次的state作為第一個(gè)參數(shù)鸠窗,和此次被更新調(diào)用的props作為第二個(gè)參數(shù)
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
We used an arrow function above, but it also works with regular functions:
在上面的例子中妓羊,我們使用了一個(gè)箭頭函數(shù),但是它也可以使用普通函數(shù)
// Correct
this.setState(function(state, props) {
return {
counter: state.counter + props.increment
};
});
State Updates are Merged
狀態(tài)更新和合并
When you call setState(), React merges the object you provide into the current state.
當(dāng)你調(diào)用setState()稍计,Reacft會(huì)合并你當(dāng)前提供的對(duì)象和state對(duì)象
For example, your state may contain several independent variables:
例如躁绸,你的state可能包含幾個(gè)獨(dú)立的變量
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
Then you can update them independently with separate setState() calls:
當(dāng)你各自通過(guò)setState去更新他們時(shí)
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
數(shù)據(jù)是向下流動(dòng)的
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.
無(wú)論父組件還是子組件都無(wú)法知道某個(gè)組件是有狀態(tài)的還是無(wú)狀態(tài)的,并且他們也不會(huì)關(guān)心是函數(shù)組件還是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.
這是為什么狀態(tài)通常是局部調(diào)用或者封閉的净刮。除了擁有并設(shè)置它的組件剥哑,其他都無(wú)法去訪問(wèn)它
A component may choose to pass its state down as props to its child components:
一個(gè)組件可以選擇向下傳遞它的狀態(tài)給子組件的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 組件將會(huì)在它的props里接收到date而且它并不知道是來(lái)自于Clock的state,來(lái)自于Clock的props又或者是手動(dòng)傳入的
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.
這是種方式通常會(huì)被稱為由上而下或者是單向的數(shù)據(jù)流淹父。任何state總是屬于特定的組件株婴,并且從它衍生出來(lái)的任何數(shù)據(jù)或者ui都只能影響在樹(shù)中在它下方的組件
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.
如果你把一個(gè)組件樹(shù)想象成一個(gè)props數(shù)據(jù)的瀑布。那么每一個(gè)組件的state就像在任意的點(diǎn)上加的一個(gè)額外的水資源暑认,但是它們也是向下流動(dòng)的
To show that all components are truly isolated, we can create an App component that renders three <Clock>s:
為了展示所有組件是獨(dú)立的困介,我們可以創(chuàng)建一個(gè)app組件渲染三個(gè)<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.
每一個(gè)Clock都會(huì)建立它們自己的定時(shí)器和獨(dú)立的更新數(shù)據(jù)
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 應(yīng)用中,一個(gè)組件是有狀態(tài)或者是無(wú)狀態(tài)的是組件內(nèi)部實(shí)現(xiàn)的細(xì)節(jié)蘸际。它可能會(huì)隨著時(shí)間推移而改變座哩。你可以在一個(gè)有狀態(tài)組件內(nèi)使用無(wú)狀態(tài)組件,反之亦然