React State(狀態)
React 把元件看成是一個狀態機(State Machines)。通過與使用者的互動,實現不同狀態,然後渲染 UI,讓使用者介面和資料保持一致。
React 裡,只需更新元件的 state,然後根據新的 state 重新渲染使用者介面(不要操作 DOM)。
以下例項建立一個名稱擴充套件為 React.Component 的 ES6 類,在 render() 方法中使用 this.state 來修改當前的時間。
新增一個類建構函式來初始化狀態 this.state,類元件應始終使用 props 呼叫基礎建構函式。
React 例項
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>現在是 {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('example')
);
嘗試一下 ?
接下來,我們將使Clock設定自己的計時器並每秒更新一次。
將生命週期方法新增到類中
在具有許多元件的應用程式中,在銷燬時釋放元件所佔用的資源非常重要。
每當 Clock 元件第一次載入到 DOM 中的時候,我們都想生成定時器,這在 React 中被稱為掛載。
同樣,每當 Clock 生成的這個 DOM 被移除的時候,我們也會想要清除定時器,這在 React 中被稱為解除安裝。
我們可以在元件類上宣告特殊的方法,當元件掛載或解除安裝時,來執行一些程式碼:
React 例項
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>現在是 {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('example')
);
嘗試一下 ?
例項解析:
componentDidMount() 與 componentWillUnmount() 方法被稱作生命週期鉤子。
在元件輸出到 DOM 後會執行 componentDidMount() 鉤子,我們就可以在這個鉤子上設定一個定時器。
this.timerID 為定時器的 ID,我們可以在 componentWillUnmount() 鉤子中解除安裝定時器。
程式碼執行順序:
-
當 <Clock />
被傳遞給 ReactDOM.render()
時,React 呼叫 Clock
元件的建構函式。 由於 Clock
需要顯示當前時間,所以使用包含當前時間的物件來初始化 this.state
。 我們稍後會更新此狀態。
-
React 然後呼叫 Clock
元件的 render()
方法。這是 React 瞭解螢幕上應該顯示什麼內容,然後 React 更新 DOM 以匹配 Clock
的渲染輸出。
-
當 Clock
的輸出插入到 DOM 中時,React 呼叫 componentDidMount()
生命週期鉤子。 在其中,Clock
元件要求瀏覽器設定一個定時器,每秒鐘呼叫一次 tick()
。
-
瀏覽器每秒鐘呼叫 tick()
方法。 在其中,Clock
元件通過使用包含當前時間的物件呼叫 setState()
來排程UI更新。 通過呼叫 setState()
,React 知道狀態已經改變,並再次呼叫 render()
方法來確定螢幕上應當顯示什麼。 這一次,render()
方法中的 this.state.date
將不同,所以渲染輸出將包含更新的時間,並相應地更新 DOM。
-
一旦 Clock
元件被從 DOM 中移除,React 會呼叫 componentWillUnmount()
這個鉤子函式,定時器也就會被清除。
資料自頂向下流動
父元件或子元件都不能知道某個元件是有狀態還是無狀態,並且它們不應該關心某元件是被定義為一個函式還是一個類。
這就是為什麼狀態通常被稱為區域性或封裝。 除了擁有並設定它的元件外,其它元件不可訪問。
以下例項中 FormattedDate 元件將在其屬性中接收到 date 值,並且不知道它是來自 Clock 狀態、還是來自 Clock 的屬性、亦或手工輸入:
React 例項
function FormattedDate(props) {
return <h2>現在是 {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')
);
嘗試一下 ?
這通常被稱為自頂向下或單向資料流。 任何狀態始終由某些特定元件所有,並且從該狀態匯出的任何資料或 UI 只能影響樹中下方的元件。
如果你想象一個元件樹作為屬性的瀑布,每個元件的狀態就像一個額外的水源,它連線在一個任意點,但也流下來。
為了表明所有元件都是真正隔離的,我們可以建立一個 App 元件,它渲染三個Clock:
React 例項
function FormattedDate(props) {
return <h2>現在是 {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'));
嘗試一下 ?
以上例項中每個 Clock 元件都建立了自己的定時器並且獨立更新。
在 React 應用程式中,元件是有狀態還是無狀態被認為是可能隨時間而變化的元件的實現細節。
我們可以在有狀態元件中使用無狀態元件,也可以在無狀態元件中使用有狀態元件。