react基礎篇二
組件 & Props & 生命周期
組件可以將UI切分成一些獨立的、可復用的部件,這樣你就只需專註於構建每一個單獨的部件。
組件從概念上看就像是函數,它可以接收任意的輸入值(稱之為“props”),並返回一個需要在頁面上展示的React元素。
定義組件的方式:函數定義/類定義組件
定義一個組件最簡單的方式是使用JavaScript函數:
function Welcome(props) { return <h1>Hello, {props.name}</h1>; }
該函數是一個有效的React組件,它接收一個單一的“props”對象並返回了一個React元素。我們之所以稱這種類型的組件為函數定義組件,是因為從字面上來看,它就是一個JavaScript函數。
你也可以使用 ES6 class 來定義一個組件:
class Welcome extends React.Component { render() { return <h1>Hello, {this.props.name}</h1>; } }
上面兩個組件在React中是相同的。
Props的只讀性
無論是使用函數或是類來聲明一個組件,它決不能修改它自己的props。來看這個sum
函數:
function sum(a, b) { return a + b; }
類似於上面的這種函數稱為“純函數”,它沒有改變它自己的輸入值,當傳入的值相同時,總是會返回相同的結果。
與之相對的是非純函數,它會改變它自身的輸入值:
function withdraw(account, amount) { account.total -= amount; }
React是非常靈活的,但它也有一個嚴格的規則:
所有的React組件必須像純函數那樣使用它們的props。
利用class建立一個Clock的組件
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‘) );
使用生命周期改造之後的組件
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()
這裏有三件事情需要知道
不要直接更新狀態
例如,此代碼不會重新渲染組件:
// Wrong this.state.comment = ‘Hello‘;
應當使用 setState()
:
// Correct this.setState({comment: ‘Hello‘});
構造函數是唯一能夠初始化 this.state
的地方。
狀態更新可能是異步的
React 可以將多個setState()
調用合並成一個調用來提高性能。
因為 this.props
和 this.state
可能是異步更新的,你不應該依靠它們的值來計算下一個狀態。
例如,此代碼可能無法更新計數器:
// Wrong this.setState({ counter: this.state.counter + this.props.increment, });
要修復它,請使用第二種形式的 setState()
來接受一個函數而不是一個對象。 該函數將接收先前的狀態作為第一個參數,將此次更新被應用時的props做為第二個參數:
// Correct this.setState((prevState, props) => ({ counter: prevState.counter + props.increment }));
數據自頂向下流動
父組件或子組件都不能知道某個組件是有狀態還是無狀態,並且它們不應該關心某組件是被定義為一個函數還是一個類。
這就是為什麽狀態通常被稱為局部或封裝。 除了擁有並設置它的組件外,其它組件不可訪問。
組件可以選擇將其狀態作為屬性傳遞給其子組件:
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
這也適用於用戶定義的組件:
<FormattedDate date={this.state.date} />
FormattedDate
組件將在其屬性中接收到 date
值,並且不知道它是來自 Clock
狀態、還是來自 Clock
的屬性、亦或手工輸入:
function FormattedDate(props) { return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
在 CodePen 中嘗試
這通常被稱為自頂向下
或單向
數據流。 任何狀態始終由某些特定組件所有,並且從該狀態導出的任何數據或 UI 只能影響樹中下方
的組件。
如果你想象一個組件樹作為屬性的瀑布,每個組件的狀態就像一個額外的水源,它連接在一個任意點,但也流下來。
為了表明所有組件都是真正隔離的,我們可以創建一個 App
組件,它渲染三個Clock
:
function App() { return ( <div> <Clock /> <Clock /> <Clock /> </div> ); } ReactDOM.render( <App />, document.getElementById(‘root‘) );
每個 Clock
建立自己的定時器並且獨立更新。
在 React 應用程序中,組件是有狀態還是無狀態被認為是可能隨時間而變化的組件的實現細節。 可以在有狀態組件中使用無狀態組件,反之亦然。
react基礎篇二