回顧:
根據目前學習內容,更新界面內容的方法為ReactDOM.render()
//定義一個方法tick,包含組件和渲染
function tick(){
const element = <h1>{new Date().toLocalTimeString()}</h1>
ReactDOM.render(
element,document.getElementById('root')
)
}
//運行方法,每秒鐘重新構建元素和渲染,計時器效果
setInterval(tick,1000);
本次目的:實現(xiàn)clock組件的封裝和可重用
1.將函數轉換成class
①創(chuàng)建一個名為React.Component的ES6類
②創(chuàng)建一個render()的空方法
③把函數體移動到render(){}方法中
④在render方法中,使用this.props替換props
⑤刪除剩余的空函數聲明
class Clock extends React.Component{
render(){
return(
<div>
<h1>{this.props.date.toLocaleTimeString()}</h1>
</div>
)
}
}
ReactDOM.render(
<Clock date={new Date()} />,
document.getElementById('root')
)
現(xiàn)在Clock被定義為一個類,而不只是一個函數,
使用類就允許我們使用它的特性,例如局部狀態(tài),生命狀態(tài)鉤子。
1.為了一個類添加局部狀態(tài)
①將date從屬性中轉移到狀態(tài)中
②在render()方法張使用this.state.date代替this.props.date
③添加一個類構造函數來初始化State,類組件應始終用props調用基礎構造函數
④在Clock組件中,去除date屬性
class Clock extends React.Component{
constructor(){
super(props);
this.state = {date:new Date()};
}
render(){
return(
<h1>it is {this.state.date.toLocalTimeString()}</h1>
)
}
}
ReactDOM.render(
<Clock />,document.getElementById('root')
)
2.將生命周期方法添加到類中
在具有許多組件的應用程序中,銷毀時釋放組件所占用的資源非常重要。
每當Clock 組件加載到DOM中,都會想要用它生成一個定時器,在React中被稱為掛載
同樣,每當Clock生成的這個DOM被移除時,就需要清除定時器,這被稱為 卸載。
可以在組件中生命一些方法,當組件 掛載 或者 卸載時執(zhí)行。
class Clock extends React.Component{
constructor(props){
super(props);
this.state = {date:new Date()};
}
componentDidMount(){
console.log('掛載開始')
this.timerID = setInterval(()=>{
this.tick();
},1000)
}
componentWillUnmounte(){
console.log('結束')
clearInterval(this.timerID);
}
tick(){
this.setState({
date:new Date()
})
}
render(){
return(
<div>
<h1>{this.state.date.toLocaleTimeString()}</h1>
</div>
)
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
)
以上的componentDidMount()和componentWillUnmount()方法被稱為生命周期鉤子
當組件輸出到DOM后會執(zhí)行componentDidMount()方法,這時候適合建立一個定時器
步驟如下:
①當 <Clock /> 被傳遞給 ReactDOM.render() 時,React 調用 Clock 組件的構造函數。 由于 Clock 需要顯示當前時間,所以使用包含當前時間的對象來初始化 this.state 。即this.state={date:new Date()};
②React 然后調用 Clock 組件的 render()方法。把React 更新 DOM 以匹配 Clock 的渲染輸出。
③當 Clock 的輸出插入到 DOM 中時,React 調用 componentDidMount()生命周期鉤子。 在其中,Clock 組件要求瀏覽器設置一個定時器,每秒鐘調用一次 tick(),此時DOM已經構建好,類似vue的mounted()。
④瀏覽器每秒鐘調用 tick()方法。 在其中,Clock 組件通過使用包含當前時間的對象調用 setState()來調度UI更新。 通過調用 setState(),React 知道狀態(tài)已經改變,并再次調用 render()方法來確定屏幕上應當顯示什么。 這一次,render()方法中的 this.state.date將不同,所以渲染輸出將包含更新的時間,并相應地更新DOM。
⑤一旦Clock組件被從DOM中移除,React會調用componentWillUnmount()這個鉤子函數,定時器也就會被清除。
從中可知:
修改this.state狀態(tài)需要使用this.setState({})方法,這里有點類似微信小程序的設置方式。
3.正確的使用狀態(tài)
關于this.setState({})的幾個重點
①更新狀態(tài)或者this.state中的內容
this.setState({comment:'hello'}); //正確修改方式
this.state.comment='hello' //錯誤方式
this.state.comment //獲取state狀態(tài)中的內容
②構造函數是唯一能夠初始化this.state的地方
③狀態(tài)的更新可能是異步操作導致的
React可以將多個setState({})調用合并成一個來提高性能。
因為this.props和this.state可能是異步更新的,所以不應該根據這個來計算下一個狀態(tài)的值,因為可能不是最新的數據。
這里使用第二種形式
讓setState({})接收一個方法,而不是一個對象,其中包含2個參數,先前的狀態(tài)state作為第一個參數,應用被更新時的props作為第二個參數
// 錯誤的方式,計算的結果不是最新的
this.setState({
counter: this.state.counter + this.props.increment,
});
// 正確的方式
this.setState((prevState,props)=>{
counter:prevState.counter+props.increment
})
4.狀態(tài)的更新并合并
當使用this.setState的時候,React會將當前的提供的對象合并到當前狀態(tài)
狀態(tài)中可能包含一些獨立的變量
constructor(props){
super(props);
this.state({
posts:[],
comment:[]
})
}
此時,可以分別調用```this.setState···獨立調用他們
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
5.數據的自頂向下流動
父組件或者子組件都不能知道其他組件是否有狀態(tài)state,以及組件是被定義為一個class類還是函數。
除了擁有并且設置他的組件外,其他組件不可訪問內部狀態(tài)。
組件可以選擇將他的狀態(tài)作為屬性傳遞給其他子組件。通過this.state來獲取
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
也適用于用戶自定義的組件
<hello date={this.state.date} />
其中hello組件收到的date屬性值,并不知道是來自Clock組件的屬性還是狀態(tài)。
這通常被稱為 自頂向下的數據流,或者是單向數據流。任何狀態(tài)始終只在某些特定的組件中,并且該狀態(tài)的導出的數據或者UI視圖,也只能影響 樹 下方的組件。
DEMO
為了證明所有組件都是隔離的,先創(chuàng)建一個組件<App />
class App extends React.Component{
render(){
return(
<div>
<Clock />
<Clock />
<Clock />
</div>
)
}
}
ReactDOM.render(
<App />,document.getElementById('root')
)
其中的每一個<Clock />會建立自己的組件并且獨立更新。