到目前爲止咱們只學習了一種方法來更新UI。調用 ReactDOM.render( ) 方法來改變輸出。javascript
在前面博客中有一個時鐘的例子代碼:java
function tick() { const element = ( <div> <h1>Hello, world!</h1> <h2>It is {new Date().toLocaleTimeString()}.</h2> </div> ); ReactDOM.render( element, document.getElementById('root') ); } setInterval(tick, 1000);
將時鐘封裝爲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);
能夠經過5個步驟將函數組件 轉換爲 類組件瀏覽器
React.Component
的ES6 類render()
的空方法render()
方法中render()
方法中,使用 this.props
替換 props
使用類就容許咱們使用其它特性,例如局部狀態、生命週期鉤子。異步
時鐘組件被更改成函數
class Clock extends React.Component { render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.props.date.toLocaleTimeString()}.</h2> </div> ); } }
咱們經過3個步驟將 date 從屬性移動到狀態中。post
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> ); } }
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 設置本身的計時器並每秒更新一次。性能
在具備許多組件的應用程序中,在銷燬時釋放組件所佔用的資源很是重要。學習
掛載:每當 Clock 組件第一次加載到 DOM 中的時候,咱們都會想生產定時器,這在React中被稱爲掛載(componentDidMount( ){....})fetch
卸載:每當 Clock 生產的這個 DOM 被移除的時候,咱們也會想要清除定時器,這在 React 中被稱爲卸載( componentWillUnmount( ){....} )。
這些方法被稱做生命週期鉤子函數。
在 componentDidMount( ) 鉤子函數中創建定時器,在 componentWillUnmount( ) 鉤子函數中卸載計時器。使用 this.setState( ) 來更新組件局部狀態:
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') );
成功:
讓咱們快速回顧一下發生了什麼以及調用方法的順序:
當
<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()
這個鉤子函數,定時器也就會被清除。
關於 setState( ) 這裏有三件事情須要知道
//此代碼不會從新渲染組件: this.state.comment = 'Hello'; //應當使用 setState(): this.setState({comment: 'Hello'});
構造函數是惟一可以初始化 this.state 的地方。
React 能夠將多個setState()
調用合併成一個調用來提升性能。
由於 this.props
和 this.state
多是異步更新的,你不該該依靠它們的值來計算下一個狀態。
//此代碼可能沒法更新計數器: this.setState({ counter: this.state.counter + this.props.increment, }); //正確方法是,請使用第二種形式的 setState() 來接受一個函數而不是一個對象。 該函數將接收 //先前的狀態做爲第一個參數,將這次更新被應用時的props作爲第二個參數: this.setState((prevState, props) => ({ counter: prevState.counter + props.increment }));
當你調用 setState()
時,React 將你提供的對象合併到當前狀態。
狀態可能包含一些獨立的變量:
constructor(props) { super(props); this.state = { posts: [], comments: [] }; }
能夠調用 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
。
父組件或子組件都不能知道某個組件是有狀態仍是無狀態,而且它們不該該關心某組件是被定義爲一個函數仍是一個類。
這就是爲何狀態一般被稱爲局部或封裝。 除了擁有並設置它的組件外,其它組件不可訪問。
組件能夠選擇將其狀態做爲屬性傳遞給其子組件。
這一般被稱爲 自頂向下 或 單向數據流。 任何狀態始終由某些特定組件全部,而且從該狀態導出的任何數據或 UI 只能影響樹中下方的組件。