快速入門
點選進入——React中文文件
JSX及元素渲染
-
React 是一個 JavaScript 庫 。
-
const element = <h1>Hello, world!</h1>;
考慮一下這個變數的宣告,它既不是字串也不是HTML,這就是 JSX ,它是 JavaScrip 的一種擴充套件語法。我們推薦在 React 中使用這種語法來描述 UI 資訊,它具有 JavaScrip 的全部能力,可以生成React “元素”。 -
比起 HTML,JSX 更接近於 JavaScript,所以 React DOM 使用駝峰(camelCase)屬性命名約定,而不是HTML屬性名稱。 例如,
class
在JSX中變為className
,tabindex
變為tabIndex
。 -
元素(Elements)是 React 應用中最小的構建部件,它是構成組建的“材料”,一個元素用於描述你在將在螢幕上看到的內容,例如:
const element = <h1>Hello, world</h1>;
。 -
React 元素是 不可突變(immutable) 的,一旦你建立了一個元素, 就不能再修改其子元素或任何屬性。一個元素就像電影裡的一幀: 它表示在某一特定時間點的 UI 。更新 UI 的唯一方法是建立一個新的元素, 並將其傳入
ReactDOM.render()
方法。但實際上,大多數 React 應用只會呼叫ReactDOM.render()
一次。更新UI更多的是通過與 狀態(state) 和 生命週期 掛鉤來實現的。
元件(Components) 和 屬性(Props)
-
元件名稱總是以大寫字母開始。
-
元件必須返回一個單獨的根元素。
-
function sum(a, b) { return a + b; }
,這種函式稱為 “ 純函式 ” ,因為它們不會試圖改變它們的輸入,並且 對於同樣的輸入,始終可以得到相同的結果 。 反之, 以下是非純函式, 因為它改變了自身的輸入值:function withdraw(account, amount) { account.total -= amount; }
-
所有 React 元件都必須是 純函式,並禁止修改其自身 props 。
狀態(State) 和 生命週期
-
this.props
由 React 本身設定, 而this.state
具有特殊的含義,但如果需要儲存一些不用於視覺輸出的內容,則可以手動向類中新增額外的欄位。 如果在render()
方法中沒有被引用, 它不應該出現在state
中。 -
關於 setState() 有三件事是你應該知道的:
-
不要直接修改 state(狀態)
例如,這樣將不會重新渲染一個元件:
this.state.comment = 'Hello';
正確的做法是用
setState()
來修改state
:this.setState({comment: 'Hello'});
-
state(狀態) 更新可能是非同步的
React 為了優化效能,有可能會將多個
setState()
呼叫合併為一次更新。因為this.props
和this.state
可能是非同步更新的,你不能依賴他們的值計算下一個state(狀態)。例如, 以下程式碼可能導致 counter(計數器)更新失敗:
// 錯誤 this.setState({ counter: this.state.counter + this.props.increment, }); 複製程式碼
要彌補這個問題,使用另一種
setState()
的形式,它 接受一個函式而不是一個物件 。這個函式將接收 前一個狀態 作為 第一個引數 ,應用 更新時的 props 作為 第二個引數 :// 正確 this.setState((prevState, props) => ({ counter: prevState.counter + props.increment })); 複製程式碼
-
state(狀態)更新會被合併
當你呼叫
setState()
, React 將合併你提供的物件到當前的狀態中。 -
資料向下流動
如果把元件樹想像為 props(屬性) 的瀑布,所有元件的 state(狀態) 就如同一個額外的水源匯入主流,且只能隨著主流的方向向下流動。
任何 state(狀態) 始終由某個特定元件所有,並且從該 state(狀態) 匯出的任何資料 或 UI 只能影響樹中 “下方” 的元件。
處理事件
- 通過 React 元素處理事件跟在 DOM 元素上處理事件非常相似。但是有一些語法上的區別:
-
React 事件使用駝峰命名,而不是全部小寫。
-
通過 JSX , 你傳遞一個函式作為事件處理程式,而不是一個字串。
-
在 React 中你不能通過返回
false
來阻止預設行為。必須明確呼叫preventDefault
。例如,對於純 HTML ,要阻止連結開啟一個新頁面的預設行為,可以這樣寫:<a href="#" onclick="console.log('The link was clicked.'); return false"> Click me </a> 複製程式碼
在 React 中, 應該這麼寫:
function ActionLink() { function handleClick(e) { e.preventDefault(); console.log('The link was clicked.'); } return ( <a href="#" onClick={handleClick}> Click me </a> ); } 複製程式碼
條件渲染
-
在 React 中,你可以建立不同的元件封裝你所需要的行為。然後,只渲染它們之中的一些,取決於你的應用的狀態。 React 中的條件渲染就和在 JavaScript 中的條件語句一樣 。使用 JavaScript 操作符如 if 或者條件操作符來建立渲染當前狀態的元素,並且讓 React 更新匹配的 UI 。
雖然宣告一個變數並使用一個 if 語句是一個有條件地渲染元件的好方法,有時你可能想要使用一個更簡短的語法。在 JSX 中有幾種內聯條件的方法,如下所述。
-
使用邏輯 && 操作符的內聯 if 用法
例如當陣列
arr
不為空時渲染元件<ShowMessages>
,可以寫成{ arr.length>0 && <ShowMessages> } 複製程式碼
它可以正常執行,因為在 JavaScript 中,
true && expression
總是會評估為expression
,而false && expression
總是執行為false
。 因此,如果條件為true
,則&&
後面的元素將顯示在輸出中。 如果是false
,React 將會忽略並跳過它。 -
使用條件操作符的內聯 If-Else
另一個用於條件渲染元素的內聯方法是使用 JavaScript 的條件操作符
If-Else
或者是三目運算子condition ? true : false
。
還要記住,無論何時何地,當條件變得太複雜時,可能是提取元件的好時機。
-
-
防止元件渲染
在極少數情況下,您可能希望元件隱藏自身,即使它是由另一個元件渲染的。為此,返回 null 而不使其渲染輸出。
例如在如下程式碼中:
//方法一: function WarningBanner(props) { if (!props.warn) { return null; } return ( <div className="warning"> Warning! </div> ); } class Page extends React.Component { constructor(props) { super(props); this.state = {showWarning: true} this.handleToggleClick = this.handleToggleClick.bind(this); } handleToggleClick() { this.setState(prevState => ({ showWarning: !prevState.showWarning })); } render() { return ( <div> <WarningBanner warn={this.state.showWarning} /> <button onClick={this.handleToggleClick}> {this.state.showWarning ? 'Hide' : 'Show'} </button> </div> ); } } ReactDOM.render( <Page />, document.getElementById('root') ); 複製程式碼
Hide
按鈕可以控制Warning
div 的顯示隱藏。 同樣,以下程式碼也能達到相同效果://方法二: class Page extends React.Component { constructor(props) { super(props); this.state = {showWarning: true} this.handleToggleClick = this.handleToggleClick.bind(this); } handleToggleClick() { this.setState(prevState => ({ showWarning: !prevState.showWarning })); } render() { return ( <div> <div className="warning" style={{display:this.state.showWarning?'':'none'}}> Warning! </div> <button onClick={this.handleToggleClick}> {this.state.showWarning ? 'Hide' : 'Show'} </button> </div> ); } } ReactDOM.render( <Page />, document.getElementById('root') ); 複製程式碼
這裡沒有把
Warning
div 當做元件抽離出來,雖然也能達到相同的效果,但是使用display
只是控制了Warning
div 的顯示隱藏,不論如何,Warning
div 其實都已經在頁面載入了,消耗了資源;而第一種方法返回null
,卻是完完全全的控制了Warning
div 的載入,效能上更勝一籌。PS:從元件的 render 方法返回 null 不會影響元件生命週期方法的觸發。 例如,
componentWillUpdate
和componentDidUpdate
仍將被呼叫。
列表(Lists) 和 鍵(Keys)
-
首先,先來看一段程式碼:
const numbers = [1, 2, 3, 4, 5]; const listItems = numbers.map((numbers) => <li>{numbers}</li> ); ReactDOM.render( <ul>{listItems}</ul>, document.getElementById('root') ); 複製程式碼
這段程式碼顯示從 1 到 5 的數字列表。
當執行上述程式碼時,雖然會正確顯示出列表,但是會收到一個警告:a key should be provided for list items(應該為列表元素提供一個鍵)。當建立元素列表時,“key” 是一個你需要包含的特殊字串屬性。 鍵(Keys) 幫助 React 標識哪個項被修改、新增或者移除了 。陣列中的 每一個元素 都應該有一個 唯一不變 的鍵(Keys)來標識。挑選 key 最好的方式是使用一個在它的同輩元素中不重複的標識字串。多數情況你可以使用資料中的 ID 作為 keys;當要渲染的列表項中沒有穩定的 IDs 時,你可以使用資料項的索引值作為 key 的最後選擇,例如:
const numbers = [1, 2, 3, 4, 5]; const listItems = numbers.map((numbers,index) => <li key={index}>{numbers}</li> ); ReactDOM.render( <ul>{listItems}</ul>, document.getElementById('root') ); 複製程式碼
-
使用 keys 提取元件 keys 只在陣列的上下文中存在意義。例如,如果你提取 一個
ListItem
元件,應該把 key 放置在陣列處理的<ListItem />
元素中,不能放在ListItem
元件自身中的<li>
根元素上。例如:
function ListItem(props) { const value = props.value; return ( // 錯誤!不需要在這裡指定 key: <li key={value.toString()}> {value} </li> ); } function NumberList(props) { const numbers = props.numbers; const listItems = numbers.map((number) => // 錯誤!key 應該在這裡指定: <ListItem value={number} /> ); return ( <ul> {listItems} </ul> ); } const numbers = [1, 2, 3, 4, 5]; ReactDOM.render( <NumberList numbers={numbers} />, document.getElementById('root') ); 複製程式碼
一個好的經驗準則是:元素中呼叫 map() 需要 keys 。
-
keys 在同輩元素中必須是唯一的
在陣列中使用的 keys 必須在它們的同輩之間唯一。然而它們並不需要全域性唯一。我們可以在操作兩個不同陣列的時候使用相同的 keys。
表單(Forms)
-
受控元件(Controlled Components)
在 HTML 中,表單元素如
<input>
,<textarea>
和<select>
表單元素通常保持自己的狀態,並根據使用者輸入進行更新。而在 React 中,可變狀態一般儲存在元件的 state(狀態) 屬性中,並且只能通過 setState() 更新。我們可以通過使 React 的 state 成為 “單一資料來源原則” 來結合這兩個形式。然後渲染表單的 React 元件也可以控制在使用者輸入之後的行為。這種形式,其 值由 React 控制的輸入表單元素 稱為“ 受控元件 ”。
例如,如果我們想在提交時記錄名稱,我們可以將表單寫為受控元件:
class NameForm extends React.Component { constructor(props) { super(props); this.state = {value: ''}; } handleChange(event) { this.setState({value: event.target.value}); } handleSubmit(event) { alert('A name was submitted: ' + this.state.value); event.preventDefault(); } render() { return ( <form onSubmit={this.handleSubmit.bind(this)}> <label> Name: <input type="text" value={this.state.value} onChange={this.handleChange.bind(this)} /> </label> <input type="submit" value="Submit" /> </form> ); } } ReactDOM.render( <NameForm />, document.getElementById('root') ); 複製程式碼
設定表單元素的value屬性之後,其顯示值將由
this.state.value
決定,以滿足React狀態的同一資料理念。每次鍵盤敲擊之後會執行handleChange
方法以更新React狀態,顯示值也將隨著使用者的輸入改變。由於
value
屬性設定在我們的表單元素上,顯示的值總是this.state.value
,以滿足state
狀態的同一資料理念。由於handleChange
在每次敲擊鍵盤時執行,以更新 React state(狀態),顯示的值將更新為使用者的輸入。對於受控元件來說,每一次 state(狀態) 變化都會伴有相關聯的處理函式。這使得可以直接修改或驗證使用者的輸入。
-
處理多個輸入元素
當您需要處理多個受控的
input
元素時,您可以為每個元素新增一個name
屬性,並且讓處理函式根據event.target.name
的值來選擇要做什麼。而不必寫多個onChange
函式 。例如:
class Reservation extends React.Component { constructor(props) { super(props); this.state = { isGoing: true, numberOfGuests: 2 }; this.handleInputChange = this.handleInputChange.bind(this); } handleInputChange(event) { const target = event.target; const value = target.type === 'checkbox' ? target.checked : target.value; const name = target.name; this.setState({ [name]: value }); } render() { return ( <form> <label> Is going: <input name="isGoing" type="checkbox" checked={this.state.isGoing} onChange={this.handleInputChange} /> </label> <label> Number of guests: <input name="numberOfGuests" type="number" value={this.state.numberOfGuests} onChange={this.handleInputChange} /> </label> </form> ); } } ReactDOM.render( <Reservation />, document.getElementById('root') ); 複製程式碼
handleInputChange()
方法中,注意我們如何使用ES6計算的屬性名稱語法來更新與給定輸入名稱相對應的 state(狀態) 鍵:const name = target.name; this.setState({ [name]: value }); 複製程式碼
狀態提升(Lifting State Up)
-
在一個 React 應用中,對於任何可變的資料都應該循序“單一資料來源”原則。通常情況下,state 首先被新增到需要它進行渲染的元件。然後,如果其它的元件也需要它,你可以提升狀態到它們最近的祖先元件。你應該依賴 從上到下的資料流向 ,而不是試圖在不同的元件中同步狀態。
提升狀態相對於雙向繫結方法需要寫更多的“模板”程式碼,但是有一個好處,它可以更方便的找到和隔離 bugs。由於任何 state(狀態) 都 “存活” 在若干的元件中,而且可以分別對其獨立修改,所以發生錯誤的可能大大減少。另外,你可以實現任何定製的邏輯來拒絕或者轉換使用者輸入。
如果某個東西可以從 props(屬性) 或者 state(狀態) 得到,那麼它可能不應該在 state(狀態) 中。
(狀態提升:子元件要渲染的資料不定義在自身
state
中,而是定義在最近的公共父元件中。該資料由父元件通過props
傳遞給子元件。當子元件發生改變時,觸發父元件的onChange()
方法,父元件更改state
,引起子元件資料改變,從而達到A子元件資料變化時B子元件資料跟著變化的效果。)
組合 VS 繼承(Composition vs Inheritance)
- 如果要在元件之間重用非 U I功能,我們建議將其提取到單獨的 JavaScript 模組中。元件可以匯入它並使用該函式,物件或類,而不擴充套件它。
React 的程式設計思想
-
如何拆分元件呢?
其實只需要像拆分一個新方法或新物件一樣的方式即可。一個常用的技巧是 單一職責原則 ,即一個元件理想情況下只處理一件事。如果一個元件持續膨脹,就應該將其拆分為多個更小的元件中。
寫在最後:強烈建議自己讀一遍 React中文文件 (有些翻譯錯誤,不影響閱讀,有能力可以去讀原版。 O(∩_∩)O哈哈~)