學習使用Clock
組件嗦随,來重用和封裝陡叠。并設置定時器概页。
封裝了一個定時器籽御,如:
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);
以上代碼忽略了一個關鍵的需求,就是設置定時器和每秒更新UI內(nèi)容。理想情況下技掏,我們希望只編寫一次代碼铃将,然后使用定時器來更新內(nèi)容。
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
如果想實現(xiàn)這個哑梳,我們需要在Clock
的組件添加state
劲阎。state
是一個參數(shù),但是他是私有的并且由組件全權控制鸠真。
把函數(shù)轉換成類
舉個例子悯仙,可以把Clock
組件轉換成類,只需要以下五步吠卷。
- 1.創(chuàng)建一個同名的ES6類锡垄,并且使用
extends React.Component.
- 2.添加一個空的方法到
render()
里面。 - 3.把函數(shù)的內(nèi)容放到
render()
方法里祭隔。 - 4.替換
render()
方法里的參數(shù)名
為this.參數(shù)名
- 5.刪除剩余的空函數(shù)聲明货岭。
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
Clock
現(xiàn)在被定義成一個類。這樣我們就可以對組件添加額外的特性序攘,如當前狀態(tài)和生命周期茴她。
添加本地state
到類中
把date
從參數(shù)到state
只需要3步。
- 1.在
render()
中替換this.參數(shù).date
成this.state.date
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
- 2.添加一個類構建方法到來創(chuàng)建
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>
);
}
}
如何把對象傳給父類的構建器程奠,如:
constructor(props) {
super(props);
this.state = {date: new Date()};
}
類組件會經(jīng)常調(diào)用父類構建的參數(shù)丈牢。
- 3.在
<Clock />
元素中移除date
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
之后添加一個定時器,效果如下
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')
);
之后添加個定時器就可以運作了瞄沙。
在類中添加一個生命周期方法
我們需要管理一個組件的生命周期己沛,尤其再不使用組建后,需要釋放使用的資源和空間距境。
當Clock
第一次渲染Dom的時候回在React中使用mounting
方法申尼,釋放的時候會使用unmounting
方法。我們可以在這兩個特殊的點里添加特有的方法垫桂。
如下
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
//第一次啟動 如IOS的viewDidLoad
}
componentWillUnmount() {
//釋放 如IOS的dealloc
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
這個方法就是生命周期师幕。
componentDidMount()
這個方法會在組件輸出渲染dom后使用。這個時候可以設置一個定時器诬滩。
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
然后在組件釋放前霹粥,釋放不使用的定時器,如
componentWillUnmount() {
clearInterval(this.timerID);
}
最后需要實現(xiàn)tick()
方法內(nèi)容疼鸟,來執(zhí)行定時器任務后控。
如下:
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')
);
剖析上面的代碼流程。
- 1.執(zhí)行
ReactDOM.render()
后會運行組件<Clock />
空镜,運行后會調(diào)用構建函數(shù)constructor
浩淘,就會初始化this.state
對象并獲取當前時間捌朴。 - 2.接著會運行組件
<Clock />
的render()
方法。由于React把組件的render()
綁定了ReactDOM.render()
张抄。所以誰輸出到界面上砂蔽。 - 3.當組件
<Clock />
輸出顯示就會調(diào)用componentDidMount()
創(chuàng)建定時器,并每秒執(zhí)行定時器里面的方法tick()
欣鳖。 - 4.在
tick()
方法中察皇,會設置this.state
這個對象,并且獲取當前時間泽台,React會被告知狀態(tài)修改,并進行執(zhí)行render()
矾缓。當render()
執(zhí)行后怀酷,會通知ReactDOM.render
并刷新。 - 5.當把組件
<Clock />
移除會執(zhí)行componentWillUnmount
并釋放定時器嗜闻。
整個生命周期完畢蜕依。
使用當前的state
關于setState()
,有三樣東西需要注意琉雳。
- 不能直接賦值
this.state
//錯誤的做法
this.state.comment = 'Hello';
需要使用
// Correct
this.setState({comment: 'Hello'});
只有在構建方法中才能直接賦值样眠。
-
state
的更新盡可能異步進行
React 為了性能,可能在一個的更新里批量處理多次的setState() 調(diào)用翠肘。
因為this.props
和this.state
可能異步更新了檐束,你不應該依賴他們的值來計算下一個state。
如:
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
以上做法錯誤的束倍,但是可以使用函數(shù)來解決這個問題被丧,如:
// Correct
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
也可以寫成這樣,更佳規(guī)范
// Correct
this.setState(function(prevState, props) {
return {
counter: prevState.counter + props.increment
};
});
State
的更新是合并的
當執(zhí)行setState()
绪妹,React會合并你提供的當前State
到對象里甥桂。
例如,state
包含了多個獨立的對象邮旷。
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
當你需要更新時黄选,可以對state里的對象分開回調(diào),如
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)的,并且他們也不應該關心自己是被定義成一個函數(shù)或者是類組件狡孔。
這就是為什么state經(jīng)常被本地調(diào)用或者被封裝懂诗。對于別的組件來說,組件的擁有的state是不可被訪問的苗膝。
一個組件可能會把自己的state作為props傳遞給他們的子組件中:
這同樣適用用戶定義的組件中:
FormattedDate組件將會接收data作為他的props并且將不知道他是來自哪殃恒,是Clock's state植旧,是來自Clock's state, 還是來自手動輸入的离唐。
這就是我們平常所說的從上往下或者單向數(shù)據(jù)流病附。任何的state都是屬于一些特定的組件,并且任何的數(shù)據(jù)或者UI視圖 只能影響在他組件樹下面的的組件亥鬓。
如果你把一個組件的props想象成是瀑布完沪,每一個組件的state就像一個額外的水資源,并且這在任意點處鏈接還往下流嵌戈。
為了展示所有的組件都是孤立的覆积,我們創(chuàng)建一個App組件來渲染三個<Clock>組件:
每個Clock都會獨立設置以及更新自己的定時器。
在React app里熟呛,無論一個stateful or stateless的組件都被認為組件獨立的細節(jié)都可能隨著時間而改變宽档。
你能用stateless組件代替stateful組件,反之亦然庵朝。