React 把組件看成是一個狀態(tài)機(jī)(State Machines)。通過與用戶的交互,實(shí)現(xiàn)不同狀態(tài),然后渲染 UI,讓用戶界面和數(shù)據(jù)保持一致。
React 里,只需更新組件的 state,然后根據(jù)新的 state 重新渲染用戶界面(不要操作 DOM)。
以下實(shí)例創(chuàng)建一個名稱擴(kuò)展為 React.Component 的 ES6 類,在 render() 方法中使用 this.state 來修改當(dāng)前的時間。
添加一個類構(gòu)造函數(shù)來初始化狀態(tài) this.state,類組件應(yīng)始終使用 props 調(diào)用基礎(chǔ)構(gòu)造函數(shù)。
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>現(xiàn)在是 {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('example')
);
將生命周期方法添加到類中
在具有許多組件的應(yīng)用程序中,在銷毀時釋放組件所占用的資源非常重要。
每當(dāng) Clock 組件第一次加載到 DOM 中的時候,我們都想生成定時器,這在 React 中被稱為掛載。
同樣,每當(dāng) Clock 生成的這個 DOM 被移除的時候,我們也會想要清除定時器,這在 React 中被稱為卸載。
我們可以在組件類上聲明特殊的方法,當(dāng)組件掛載或卸載時,來運(yùn)行一些代碼:
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>現(xiàn)在是 {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('example')
);
實(shí)例解析:
**componentDidMount() **與 componentWillUnmount() 方法被稱作生命周期鉤子。
在組件輸出到 DOM 后會執(zhí)行 componentDidMount() 鉤子,我們就可以在這個鉤子上設(shè)置一個定時器。
this.timerID 為計算器的 ID,我們可以在 componentWillUnmount() 鉤子中卸載計算器。
代碼執(zhí)行順序:
- 當(dāng) <Clock /> 被傳遞給 ReactDOM.render() 時,React 調(diào)用 Clock 組件的構(gòu)造函數(shù)。 由于 Clock 需要顯示當(dāng)前時間,所以使用包含當(dāng)前時間的對象來初始化 this.state 。 我們稍后會更新此狀態(tài)。
- React 然后調(diào)用 Clock 組件的 render() 方法。這是 React 了解屏幕上應(yīng)該顯示什么內(nèi)容,然后 React 更新 DOM 以匹配 Clock 的渲染輸出。
- 當(dāng) Clock 的輸出插入到 DOM 中時,React 調(diào)用 componentDidMount() 生命周期鉤子。 在其中,Clock 組件要求瀏覽器設(shè)置一個定時器,每秒鐘調(diào)用一次 tick()。
- 瀏覽器每秒鐘調(diào)用 tick() 方法。 在其中,Clock 組件通過使用包含當(dāng)前時間的對象調(diào)用 setState() 來調(diào)度UI更新。 通過調(diào)用 setState() ,React 知道狀態(tài)已經(jīng)改變,并再次調(diào)用 render() 方法來確定屏幕上應(yīng)當(dāng)顯示什么。 這一次,render() 方法中的 this.state.date 將不同,所以渲染輸出將包含更新的時間,并相應(yīng)地更新 DOM。
- 一旦 Clock 組件被從 DOM 中移除,React 會調(diào)用 componentWillUnmount() 這個鉤子函數(shù),定時器也就會被清除。
數(shù)據(jù)自頂向下流動
父組件或子組件都不能知道某個組件是有狀態(tài)還是無狀態(tài),并且它們不應(yīng)該關(guān)心某組件是被定義為一個函數(shù)還是一個類。
這就是為什么狀態(tài)通常被稱為局部或封裝。 除了擁有并設(shè)置它的組件外,其它組件不可訪問。
以下實(shí)例中 FormattedDate 組件將在其屬性中接收到 date 值,并且不知道它是來自 Clock 狀態(tài)、還是來自 Clock 的屬性、亦或手工輸入:
function FormattedDate(props) {
return <h2>現(xiàn)在是 {props.date.toLocaleTimeString()}.</h2>;
}
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>
<FormattedDate date={this.state.date} />
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('example')
);
這通常被稱為自頂向下或單向數(shù)據(jù)流。 任何狀態(tài)始終由某些特定組件所有,并且從該狀態(tài)導(dǎo)出的任何數(shù)據(jù)或 UI 只能影響樹中下方的組件。
如果你想象一個組件樹作為屬性的瀑布,每個組件的狀態(tài)就像一個額外的水源,它連接在一個任意點(diǎn),但也流下來。
為了表明所有組件都是真正隔離的,我們可以創(chuàng)建一個 App 組件,它渲染三個Clock:
function FormattedDate(props) {
return <h2>現(xiàn)在是 {props.date.toLocaleTimeString()}.</h2>;
}
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>
<FormattedDate date={this.state.date} />
</div>
);
}
}
function App() {
return (
<div>
<Clock />
<Clock />
<Clock />
</div>
);
}
ReactDOM.render(<App />, document.getElementById('example'));
以上實(shí)例中每個 Clock 組件都建立了自己的定時器并且獨(dú)立更新。
在 React 應(yīng)用程序中,組件是有狀態(tài)還是無狀態(tài)被認(rèn)為是可能隨時間而變化的組件的實(shí)現(xiàn)細(xì)節(jié)。
我們可以在有狀態(tài)組件中使用無狀態(tài)組件,也可以在無狀態(tài)組件中使用有狀態(tài)組件。