新聞中心
通過調(diào)用 ReactDOM.render() 來修改我們想要渲染的元素:

function tick() {
const element = (
Hello, world!
It is {new Date().toLocaleTimeString()}.
);
ReactDOM.render(element,document.getElementById('root'));
}
setInterval(tick, 1000);在 CodePen 上嘗試
如何封裝真正可復(fù)用的 Clock 組件。將設(shè)置自己的計時器并每秒更新一次。
我們可以從封裝時鐘的外觀開始:
function Clock(props) {
return (
Hello, world!
It is {props.date.toLocaleTimeString()}.
);
}
function tick() {
ReactDOM.render(
,
document.getElementById('root')
);
}
setInterval(tick, 1000);在 CodePen 上嘗試
然而,它忽略了一個關(guān)鍵的技術(shù)細(xì)節(jié):Clock 組件需要設(shè)置一個計時器,并且需要每秒更新 UI。
理想情況下,我們希望只編寫一次代碼,便可以讓 Clock 組件自我更新:
ReactDOM.render(
,
document.getElementById('root')
);我們需要在 Clock 組件中添加 “state” 來實現(xiàn)這個功能。
State 與 props 類似,但是 state 是私有的,并且完全受控于當(dāng)前組件。
將函數(shù)組件轉(zhuǎn)換成 class 組件
通過以下五步將 Clock 的函數(shù)組件轉(zhuǎn)成 class 組件:
- 創(chuàng)建一個同名的 ES6 class,并且繼承于 React.Component。
- 添加一個空的 render() 方法。
- 將函數(shù)體移動到 render() 方法之中。
- 在 render() 方法中使用 this.props 替換 props。
- 刪除剩余的空函數(shù)聲明。
class Clock extends React.Component {
render() {
return (
Hello, world!
It is {this.props.date.toLocaleTimeString()}.
);
}
}在 CodePen 上嘗試
現(xiàn)在 Clock 組件被定義為 class,而不是函數(shù)。
每次組件更新時 render 方法都會被調(diào)用,但只要在相同的 DOM 節(jié)點中渲染
這就使得我們可以使用如 state 或生命周期方法等很多其他特性。
向 class 組件中添加局部的 state
我們通過以下三步將 date 從 props 移動到 state 中:
- 把 render() 方法中的 this.props.date 替換成 this.state.date :
class Clock extends React.Component {
render() {
return (
Hello, world!
It is {this.state.date.toLocaleTimeString()}.
);
}
}- 添加一個 class 構(gòu)造函數(shù),然后在該函數(shù)中為 this.state 賦初值:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
Hello, world!
It is {this.state.date.toLocaleTimeString()}.
);
}
}通過以下方式將 props 傳遞到父類的構(gòu)造函數(shù)中:
constructor(props) {
super(props);
this.state = {date: new Date()};
}Class 組件應(yīng)該始終使用 props 參數(shù)來調(diào)用父類的構(gòu)造函數(shù)。
- 移除
元素中的 date 屬性:
ReactDOM.render(
,
document.getElementById('root')
);我們之后會將計時器相關(guān)的代碼添加到組件中。
代碼如下:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
Hello, world!
It is {this.state.date.toLocaleTimeString()}.
);
}
}
ReactDOM.render(
, document.getElementById('root')
);在 CodePen 上嘗試
接下來,我們會設(shè)置 Clock 的計時器并每秒更新它。
將生命周期方法添加到 Class 中
在具有許多組件的應(yīng)用程序中,當(dāng)組件被銷毀時釋放所占用的資源是非常重要的。
當(dāng) Clock 組件第一次被渲染到 DOM 中的時候,就為其設(shè)置一個計時器。
這在 React 中被稱為“掛載(mount)”。
同時,當(dāng) DOM 中 Clock 組件被刪除的時候,應(yīng)該清除計時器。
這在 React 中被稱為“卸載(unmount)”。
我們可以為 class 組件聲明一些特殊的方法,當(dāng)組件掛載或卸載時就會去執(zhí)行這些方法:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() { }
componentWillUnmount() { }
render() {
return (
Hello, world!
It is {this.state.date.toLocaleTimeString()}.
);
}
}這些方法叫做“生命周期方法”。
componentDidMount() 方法會在組件已經(jīng)被渲染到 DOM 中后運行,所以,最好在這里設(shè)置計時器:
componentDidMount() {
this.timerID = setInterval(() => this.tick(),1000);
}接下來把計時器的 ID 保存在 this 之中(this.timerID)。
盡管 this.props 和 this.state 是 React 本身設(shè)置的,且都擁有特殊的含義,但是其實你可以向 class 中隨意添加不參與數(shù)據(jù)流(比如計時器 ID)的額外字段。
我們會在 componentWillUnmount() 生命周期方法中清除計時器:
componentWillUnmount() {
clearInterval(this.timerID);
}最后,我們會實現(xiàn)一個叫 tick() 的方法,Clock 組件每秒都會調(diào)用它。
使用 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 (
Hello, world!
It is {this.state.date.toLocaleTimeString()}.
);
}
}
ReactDOM.render(
,
document.getElementById('root')
);在 CodePen 上嘗試
現(xiàn)在時鐘每秒都會刷新。
讓我們來快速概括一下發(fā)生了什么和這些方法的調(diào)用順序:
- 當(dāng)
被傳給 ReactDOM.render()的時候,React 會調(diào)用 Clock 組件的構(gòu)造函數(shù)。因為 Clock 需要顯示當(dāng)前的時間,所以它會用一個包含當(dāng)前時間的對象來初始化 this.state。我們會在之后更新 state。 - 之后 React 會調(diào)用組件的 render() 方法。這就是 React 確定該在頁面上展示什么的方式。然后 React 更新 DOM 來匹配 Clock 渲染的輸出。
- 當(dāng) Clock 的輸出被插入到 DOM 中后,React 就會調(diào)用 ComponentDidMount() 生命周期方法。在這個方法中,Clock 組件向瀏覽器請求設(shè)置一個計時器來每秒調(diào)用一次組件的 tick() 方法。
- 瀏覽器每秒都會調(diào)用一次 tick() 方法。 在這方法之中,Clock 組件會通過調(diào)用 setState() 來計劃進行一次 UI 更新。得益于 setState() 的調(diào)用,React 能夠知道 state 已經(jīng)改變了,然后會重新調(diào)用 render() 方法來確定頁面上該顯示什么。這一次,render() 方法中的 this.state.date 就不一樣了,如此以來就會渲染輸出更新過的時間。React 也會相應(yīng)的更新 DOM。
- 一旦 Clock 組件從 DOM 中被移除,React 就會調(diào)用 componentWillUnmount() 生命周期方法,這樣計時器就停止了。
正確地使用 State
關(guān)于 setState() 你應(yīng)該了解三件事:
不要直接修改 State
例如,此代碼不會重新渲染組件:
// Wrong
this.state.comment = 'Hello';而是應(yīng)該使用 setState():
// Correct
this.setState({comment: 'Hello'});構(gòu)造函數(shù)是唯一可以給 this.state 賦值的地方:
State 的更新可能是異步的
出于性能考慮,React 可能會把多個 setState() 調(diào)用合并成一個調(diào)用。
因為 this.props 和 this.state 可能會異步更新,所以你不要依賴他們的值來更新下一個狀態(tài)。
例如,此代碼可能會無法更新計數(shù)器:
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});要解決這個問題,可以讓 setState() 接收一個函數(shù)而不是一個對象。這個函數(shù)用上一個 state 作為第一個參數(shù),將此次更新被應(yīng)用時的 props 做為第二個參數(shù):
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));上面使用了箭頭函數(shù),不過使用普通的函數(shù)也同樣可以:
// Correct
this.setState(function(state, props) {
return {
counter: state.counter + props.increment
};
});State 的更新會被合并
當(dāng)你調(diào)用 setState() 的時候,React 會把你提供的對象合并到當(dāng)前的 state。
例如,你的 state 包含幾個獨立的變量:
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}然后你可以分別調(diào)用 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。
數(shù)據(jù)是向下流動的
不管是父組件或是子組件都無法知道某個組件是有狀態(tài)的還是無狀態(tài)的,并且它們也并不關(guān)心它是函數(shù)組件還是 class 組件。
這就是為什么稱 state 為局部的或是封裝的的原因。除了擁有并設(shè)置了它的組件,其他組件都無法訪問。
組件可以選擇把它的 state 作為 props 向下傳遞到它的子組件中:
It is {this.state.date.toLocaleTimeString()}.
這對于自定義組件同樣適用:
FormattedDate 組件會在其 props 中接收參數(shù) date,但是組件本身無法知道它是來自于 Clock 的 state,或是 Clock 的 props,還是手動輸入的:
function FormattedDate(props) {
return It is {props.date.toLocaleTimeString()}.
;
}在 CodePen 上嘗試
這通常會被叫做“自上而下”或是“單向”的數(shù)據(jù)流。任何的 state 總是所屬于特定的組件,而且從該 state 派生的任何數(shù)據(jù)或 UI 只能影響樹中“低于”它們的組件。
如果你把一個以組件構(gòu)成的樹想象成一個 props 的數(shù)據(jù)瀑布的話,那么每一個組件的 state 就像是在任意一點上給瀑布增加額外的水源,但是它只能向下流動。
為了證明每個組件都是真正獨立的,我們可以創(chuàng)建一個渲染三個 Clock 的 App 組件:
function App() {
return (
);
}
ReactDOM.render(
,
document.getElementById('root')
);在 CodePen 上嘗試
每個 Clock 組件都會單獨設(shè)置它自己的計時器并且更新它。
在 React 應(yīng)用中,組件是有狀態(tài)組件還是無狀態(tài)組件屬于組件實現(xiàn)的細(xì)節(jié),它可能會隨著時間的推移而改變。你可以在有狀態(tài)的組件中使用無狀態(tài)的組件,反之亦然。
當(dāng)前文章:創(chuàng)新互聯(lián)React教程:ReactState(狀態(tài))&生命周期
網(wǎng)址分享:http://m.5511xx.com/article/ccojgph.html


咨詢
建站咨詢
