React---基礎1
react安裝
通過 npm 使用 React
國內使用 npm 速度很慢,你可以使用淘寶定製的 cnpm (gzip 壓縮支援) 命令列工具代替預設的 npm:
npm install -g cnpm --registry=https://registry.npm.taobao.org
npm config set registry https://registry.npm.taobao.org
這樣就可以使用 cnpm 命令來安裝模組了:
cnpm install [name]
使用 create-react-app 快速構建 React 開發環境
create-react-app 是來自於 Facebook,通過該命令我們無需配置就能快速構建 React 開發環境。
create-react-app 自動建立的專案是基於 Webpack + ES6 。
執行以下命令建立專案:
cnpm install -g create-react-app create-react-app my-app cd my-app npm start
一、什麼是React
React 是一個用於構建使用者介面的 JAVASCRIPT 庫。
React 主要用於構建UI,很多人認為 React 是 MVC 中的 V(檢視)。
React 起源於 Facebook 的內部專案,用來架設 Instagram 的網站,並於 2013 年 5 月開源。
React 擁有較高的效能,程式碼邏輯非常簡單,越來越多的人已開始關注和使用它。
React 特點:
-
1.宣告式設計 −React採用宣告正規化,可以輕鬆描述應用。
-
2.高效 −React通過對DOM的模擬,最大限度地減少與DOM的互動。
-
3.靈活 −React可以與已知的庫或框架很好地配合。
-
4.JSX − JSX 是 JavaScript 語法的擴充套件。React 開發不一定使用 JSX ,但我們建議使用它。
-
5.元件 − 通過 React 構建元件,使得程式碼更加容易得到複用,能夠很好的應用在大專案的開發中。
-
6.單向響應的資料流 − React 實現了單向響應的資料流,從而減少了重複程式碼,這也是它為什麼比傳統資料繫結更簡單。
一個最簡單的React例子:
React 元素渲染
元素是構成 React 應用的最小單位,它用於描述螢幕上輸出的內容。
const element = <h1>Hello, world!</h1>;
與瀏覽器的 DOM 元素不同,React 當中的元素事實上是普通的物件,React DOM 可以確保 瀏覽器 DOM 的資料內容與 React 元素保持一致。
const element = <h1>Hello, world!</h1>;
ReactDom.render(
element,
document.getElementById('root')
)
要將React元素渲染到根DOM節點中,我們通過把它們都傳遞給 ReactDOM.render() 的方法來將其渲染到頁面上;
ReactDom.render()
接受兩個引數,第一個是要被插入的內容,第二個是插入到DOM或者說index.html
的位置
一個與Html對比的簡單元件:
class ShoppingList extends React.Componnet {
render() {
return (
<div className="shopping-list">
<h1>Shoping List for {this.props.name}</h1>
<ul>
<li>Instagram</li>
<li>WhatApp</li>
<li>Oculus</li>
</ul>
</div>
)
}
}
// Example usage: <ShoppingList name="muzidigbig" />
在這裡,ShoppingList是一個 React元件類,或 React元件型別。元件接受引數,稱為屬性 props
, 並通過 render
方法返回一個現實的檢視層次結構。
render
方法返回您要渲染的內容描述,然後React接受該描述並將其渲染到螢幕上,特別是,render
返回一個React 元素,這是一個渲染內容的輕量級的描述。大多數
React 開發人員使用 JSX
語法,也是上述案例寫到的語法。
JSX
語法的轉換規則為: <div />
語法在構建是被轉換為 React.createElement('div')
。因此,上面的例子等價於:
return React.createElement('div', {className: 'shopping-list'},
React.createElement('h1', /* h1 children ... */),
React.createElement('ul', /* ul children ... */)
);
既然 JSX
在 React 開發者中這麼流行,那 JSX
又是什麼呢?
二、JSX 語法
JSX
:看起來像HTML的 Javascript
一種擴充語法,能夠讓你的 Javascript
中和正常描述 HTML一樣編寫 HTML。
元素是構成 React 應用的最小單位,JSX 就是用來宣告 React 當中的元素。
我們不需要一定使用 JSX,但它有以下優點:
- JSX 執行更快,因為它在編譯為 JavaScript 程式碼後進行了優化。
- 它是型別安全的,在編譯過程中就能發現錯誤。
- 使用 JSX 編寫模板更加簡單快速。
你可以用 花括號
將任意 Javascript
表示式嵌入到 JSX
中。例如:表示式 1 + 2
, 變數 user.firstName
, 和函式 formatName(User)
等都可以嵌入使用
function formatName(user) {
return user.firstName + ' ' + user.lastName;
}
const user = {
firstName: 'harper',
lastName: 'Perez'
}
const element = {
<h1> Hello, {formatName(user)}! </h1>
}
ReactDOM.render (
element,
document.getElementById('root')
)
請注意,為了方便閱讀開發者們常將 JSX
分割成多行包裹起來,因為這可以避免分號自動插入的陷阱,如
{ 1
2 } 3
// is transformed to
{ 1
;2 ;} 3;
用 JSX 指定屬性值
你可以用花括號嵌入一個 JavaScript 表示式作為屬性值
// 用引號形式
const element = <div tableIndex="0"></div>;
// 用表示式,並且表示式用花括號包裹
const element = <img src={user.avatarUrl}></img>;
用 JSX 指定子元素
如果是空標籤,可以直接用 />
閉合
const element = <img src={user.avatarUrl} />
如果包含子標籤(外面包裹一個<div>)
<div>
<h1>Hello!</h1>
<h2>Good to see you here.</h2>
</div>
比起
HTML
,JSX
更接近於Javascript
,所以React DOM
規範使用駝峰(camelCase)屬性命名約定,而不是HTML屬性名稱,當然,html的部分屬性名稱也作為保留字,不可使用,例如class
;
因此,class
在 JSX 中 變為className
,tableindex
變為tableIndex
。
用 JSX 防止注入攻擊
在JSX 中嵌入使用者輸入是安全的:
const title = response.potentiallyMaliciousInput;
// 這樣是安全的
const element = <h1>{title}</h1>
預設情況下, 在渲染之前, React DOM 會格式化(escapes) JSX中的所有值. 從而保證使用者無法注入任何應用之外的程式碼. 在被渲染之前,所有的資料都被轉義成為了字串處理。 以避免 XSS(跨站指令碼) 攻擊。
元素渲染到DOM
正常情況下,你的 index.html
檔案下會有這麼一個div
<div id='root'></div>
這個root
DOM 節點掛在所有React DOM的位置。正常情況下,對於一個React單頁面應用構建,只需要一個單獨的根DOM節點即可。但如果要把React整合到現有的APP中,則可能會使用到多個DOM節點。
React利用render
方法將React元素渲染到DOM上,一旦元素被渲染到頁面了之後,就不能在修改器子元素或任何元素的屬性,就像電影裡的一幀,在某以特定的時間點的UI效果,那元素的更新呢?沒錯,就是重新 render
function tick() {
cosnt element = {
<div>
<h1>Hello, world</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
};
ReactDom.render (
element,
document.getElementById('root')
)
}
setInterval(tick, 1000);
我們可以在 JSX 中使用 JavaScript 表示式。表示式寫在花括號 {} 中。
實際上,大多數 React 應用只會呼叫一次ReactDom.render(),而實現元件更新的辦法就是將程式碼封裝在有狀態的元件中。
React 只更新必須更新的部分
這正是 React 的強大之處。React DOM 會將元素及其子元素與之前版本逐一對比,並只對有必要更新的 DOM 進行更新, 以達到 DOM 所需的狀態。
開發過程中,更應該每個時間點UI的表現, 而不是關注隨著時間不斷更新UI的狀態, 可以減少很多奇怪的 bug
三、元件和屬性
元件 components
和屬性 props
,其中,屬性是單詞 property
的程式碼簡寫。
定義元件的兩種辦法
定義元件有兩種方式
- 函式式元件定義
- 類元件定義
最簡單的定義元件的方法就是寫一個Javascript
函式
function Welcome(props) {
return <h1>Hello, props.name</h1>
}
這就是一個有效的元件,它接首了一個 props
引數,並返回了一個React元素,這是一個函式式元件,表面上看,他就是一個 Javascript
函式。
類元件的定義則依賴ES6 的 class
來定義,下面這種定義方法和上方是等效的;
important React from 'react';
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
渲染一個元件
// DOM標籤作為元件
const element = <div />;
// React 元素作為元件
const element = <Welcome name="muzidigbig" />;
當React 遇到一個代表使用者定義元件的元素時,它將 JSX
屬性以一個單獨物件即props物件
的形式傳遞給相應的元件,例如
function Welcome(props) {
return <h1>Hello, {props.mname] </h1>;
}
const element = <Wlecome name="muzidigbig" />;
ReactDOM.render(
element,
document.getElementById('root')
)
【理解】
- 呼叫
ReactDOM.render()
方法並向其傳入了<Welcome name="muzidigbig" />
元素 - Raect 呼叫
Welcome
元件,並向其傳入了{name: 'muzidigbig'}
作為props物件
Welcome
元件返回<h1>Hello, muzidigbig</h1>
- React DOM 迅速更新 DOM,使其顯示為
<h1>Hello, muzidigbig</h1>
注意,原生 HTML 元素名以小寫字母開頭,而自定義的 React 類名以大寫字母開頭,比如 HelloMessage 不能寫成 helloMessage。除此之外還需要注意組件類只能包含一個頂層標籤,否則也會報錯。
構成元件
既然元件是單獨的一個React元素,那他能單獨工作,因此我們能在一個React 元素中多次引用到相同的元件, 舉個例子:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>
}
function App() {
return (
<Welcome name="Sara" />
<Welcome name="Lucy" />
<Welcome name="Edite" />
)
}
ReactDOM.render(
<App />,
document.getElementBuId('root')
)
通常情況下, React apps 都有一個單獨的頂層的 App
元件。如果是在已有的應用中整合React,也需要由下至上的從小的元件開始逐步整合到檢視頂層的元件中。
元件必須返回一個單獨的根元素,這就是為什麼我們要新增一個
<div>
來包裹所有的<Welcome />
元素的原因
提取元件
對於一個React 元素,如果其中含有可複用或可能會重複使用的內容,不要害怕把它單拿出來多個更小的元件。
提取元件可能看起來是一個繁瑣的工作,但是在大型的 App
中可以回報給我們的是大量的可複用元件。一個好的經驗準則是如果你 UI 的一部分需要用多次 (Button
,Panel
,Avatar
),或者本身足夠複雜(App
,FeedStory
,Comment
),最好的做法是使其成為可複用元件。
Props 是隻讀的
無論你用函式或類的方法來宣告元件,
雖然 React 很靈活,但是它有一條嚴格的規則:**所有 React 元件都必須是純函式,並禁止修改其自身 props **。所謂的純函式就是:傳入函式引數不會在函式執行過程中發生改變,比如自增操作 a++
。
如果props
是隻讀的,那傳遞給子元素(子元件)的引數豈不是不能修改了?那子元素如何與父元素做互動呢?React還給我們提供了狀態屬性 state
供我們在子元件內部修改值
四、狀態和生命週期
狀態state
, 生命週期 liftcircle
.
之前說過,一旦元素被渲染了之後就不可改變了,但我們可以通過重新渲染的方法使頁面得以重新整理,同樣我們提到過最常用的方法是編寫一個可複用的具有狀態的元件,這裡的狀態,就是我們將要說的 state
我們對上述提過的計時器tick
中的計時功能封裝成一個函式式元件如下:
function Clock(props) {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {props.date.toLocaleTimeString()}.</h2>
</div>
)
}
在這個例子中,我們將計時功能程式碼封裝成了一個獨立的可複用的元件,並通過屬性date的方式將引數傳入,但還不能到達我們想要的結果,那就是不能再元件內部修改引數值,元件中顯示的資料依舊受控於父元件中date
屬性傳遞過來的值,那如果我們把這個date
屬性也新增到Clock
內部呢?來看下
ReactDOM.render(
<Clock />,
document.getElementById('root')
)
這時父元件中只保留了對計時元件Clock
的一個單純的引用。剩下的事情全部依託以元件Clock
自己去實現。要怎麼實現這個需求?這裡React提出了另一個資料物件,即state
,它用來儲存元件內部的資料,與props
類似,不同的是state
是元件私有的,並且由元件本身完全控制。它能實現資料在元件內部的修改和更新。怎麼使用這個state
?繼續往下講之前我們先擴充一個知識
我們知道元件有兩種定義方式,即函式式元件和類元件,雖然函式式元件更加簡潔更加接近原生 javascript
,但類元件卻擁有一些額外的屬性,這個類元件專有特性,就是狀態和生命週期鉤子,到這裡也能清楚知道狀態的關鍵作用,然而函式式元件沒有這兩個特性,因此,在需要使用到狀態state
情況下,我們需要將函式式元件轉換成類元件
函式式元件轉化成類元件
嘗試把一個函式式元件轉化成類元件,官網給出了以下步驟,以Clock
元件為例
- 建立一個繼承自
React.Component
類的ES6 class
同名類 - 新增一個名為
render()
的空方法 - 把原函式中的所有內容移至
render()
中 - 在
render()
方法中使用this.props
替代props
- 刪除保留的空函式宣告
class Clock extents React.Component {
render() {
return (
<div>
<h1>Hello, world</h1>
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
</div>
)
}
}
到此,Clock
元件已經成功被我們修改成了一個類元件,我們便可以在其中新增本地狀態state
和生命週期鉤子
class Clock extends React.Component {
// 用類建構函式constructor初始化 this.state
constructor(props) {
// 使用super()將props傳遞給基礎建構函式
super(props);
// 設定初始state
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
)
}
}
這樣,我們的類元件Clock
就擁有了自己的屬性 this.state.date
,也就不需要引用元件向其傳遞值了,因此,我們可以把元件引用中的date
屬性刪掉,最終,我們將其渲染到DOM上,只使用元件引用,其他都交給元件Clock
自己去實現
ReactDOM.render(
<Clock />,
document.getElementById('root')
)
到這裡就結束了?細心的你會發現,元件Clock
只是實現了當前時間的顯示,而我們要改裝的功能是一個計時器,計時功能去哪裡了?沒實現啊?我們需要在元件Clock
中找到一個合適的時機去實現這個功能,為此,React團隊引入了 宣告週期方法,也叫生命週期鉤子
在類元件中新增生命週期方法
在一個具有許多元件的應用程式中,在元件被銷燬時釋放所佔用的資源是非常重要的。就像瀏覽器的垃圾回收機制,近期內不需要再用的資源,應該及時清除。
當 Clock
第一次渲染到DOM時,我們要設定一個定時器 。 這在 React 中稱為 “掛載(mounting)” 。它有一個生命鉤子componentDidMount()
當 Clock
產生的 DOM 被銷燬時,我們也想清除該計時器。 這在 React 中稱為 “解除安裝(unmounting)” 。它的生命鉤子是componentWillUnmount()
我們的計時器是在頁面載入之後,頁面生成初始化狀態,然後由計時器去觸發狀態的重新整理,因此,在掛載完成是去設定計時器是個非常不錯的選擇
componentDidMount() {
this.timerID = setInterval(
() => this.tick(), 1000
)
}
掛載時我們宣告瞭一個tick()
方法,接下來我們就要實現這個方法,是用來觸發UI更新。嗯哼?UI更新?我們的頁面狀態state
不是已經更新了嗎?為啥還要UI更新?
這裡有一個非常重要的方法:setState()
。我們先把程式碼補充完整再說明
componentDidMount() {
// ...
}
tick() {
this.setState({
date: new Date()
})
}
componentWillUnmount() {
// ...
}
setState()
是React觸發頁面更新的第二個辦法,第一個辦法開篇即說過,即render()
方法。setState
作用就是通知React檢查帶狀態的元件中是否含有差值。此時react會生成一個虛擬DOM與之前的版本進行對比,只有有必要更新時才會更新。關於 state 與 setState過程 在我的另一篇文章中有詳細說明,有興趣的可以翻過去看看。
為什麼不把tick()
方法寫到componentDidMount()
中?因為tick()
只是一個普通方法,他不需要在生命週期中觸發,也不用自動觸發。只要誰呼叫了觸發即可。因此不需要也不能放在生命週期鉤子函式中。
現在這個時鐘每秒都會走了。整理一下,我們整個計時器程式碼如下:
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 得知了元件state
(狀態)的變化, 隨即再次呼叫render()
方法,獲取了當前應該顯示的內容。 這次,render()
方法中的this.state.date
的值已經發生了改變, 從而,其輸出的內容也隨之改變。React 於是據此對 DOM 進行更新。 -
如果通過其他操作將
Clock
元件從 DOM 中移除了, React 會呼叫componentWillUnmount()
生命週期鉤子, 所以計時器也會被停止。
正確的使用State(狀態)
對於setState()
有三件事情是我們應該要知道的
(1)不要直接修改state
真正觸發React對比不同版本的虛擬DOM是setState()
方法,直接修改state
頁面不會重新整理,這一點與原生javascript
區別較大,需要理解。
// 這麼做不會觸發React更新頁面
this.state.comment = 'hello';
// 使用 setState() 代替
this.setState({ comment: 'hello' });
【注意】在元件中,唯一可以初始化分配this.state
的地方就是建構函式constructor(){}
(2)state(狀態)更新可能是非同步的
React為了優化效能,有可能會將多個setState()
呼叫合併為一次更新。這就導致 this.props
和this.state
可能是非同步更新的,你不能依賴他們的值計算下一個state(狀態)
// counter 計數更新會失敗
this.setState({
counter: this.state.counter this.props.increment
})
如果我們有這種需求,可以使用以下setState()
辦法:
// ES6 箭頭函式法
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
// 常規函式法
this.setState(function(prevState, props) {
return {
counter: prevState.counter + props.increment
};
})
(3)state(狀態)更新會被合併
當你呼叫setState()
, React將合併你提供的物件到當前狀態中。例如,你的狀態可能包含幾個獨立的變數,然後你用幾個獨立的setState方法去呼叫更新,如下
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
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
的值。
資料向下流動
無論是作為父元件還是子元件,它都無法或者一個元件是否有狀體,同時也不需要關心另一個元件是定義為函式元件還是類元件。這就是為什麼state
經常被稱為 本地狀態 或 封裝狀態 的原因, 他不能被擁有並設定它的元件以外的任何元件訪問。那如果需要訪問怎麼處理?
(1)作為其子元件的props(屬性)
// 在元件中使用
<h2>It is {this.state.date.toLocaleTimeString()}</h2>
// 傳遞給子元件作為props
<FormattedDate date={this.state.date} />
雖然FormattedDate
元件通過props
接收了date
的值,但它仍然不能獲知該值是來自於Clock
的state
, 還是 Clock
的props
, 或者一個手動建立的變數.
這種資料關係,一般稱為"從上到下"或"單向"的資料流。任何state(狀態)
始終由某個特定元件所有,並且從該state
匯出的任何資料 或 UI 只能影響樹"下方"的元件
如果把元件樹想像為 props(屬性)
的瀑布,所有元件的 state(狀態)
就如同一個額外的水源匯入主流,且只能隨著主流的方向向下流動。
各元件完全獨立
借用上文的Clock
元件,我們建立一個App
元件,並在其中渲染三個Clock
:
function App() {
return (
// 之前說過元件只能返回一個根節點,所以用<div>包起來
<div>
<Clock />
<Clock />
<Clock />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
每個Clock
都設立它自己的計時器並獨立更新,如果App
中有一個資料變數,也能被三個Clock
相互獨立修改。
至於何時使用有狀態元件,何時使用無狀態元件,被認為是元件的一個實現細節,取決於你當時的需求,你可以在有狀態元件中使用無狀態元件,也可以在無狀態元件中使用有狀態元件
五、事件處理
通過 React 元素處理事件跟在 DOM 元素上處理事件非常相似。但是有一些語法上的區別:
- React 事件使用駝峰命名,而不是全部小寫
- 通過 JSX , 傳遞一個函式作為事件處理程式,而不是一個字串
// html usage
<button onclick="todo()">click me</button>
// React usage
<button onClick={todo}>click me></button>
3.在React中不能通過返回false
來阻止預設行為。必須明確的呼叫preventDefault
// html usage
<a href="#" onclick="console.log('clicked'); return false">
Click me
</a>
// React usage
class ActionLink extends React.Component {
function handleClick(e) {
e.preventDefault();
console.log('clicked.');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
)
}
在這裡,React團隊幫Coder們實現了e事件的跨瀏覽器相容問題。當使用React時,我們也不需要呼叫addEventListener
在DOM 元素被建立後新增事件監聽器。相反,只要當元素被初始渲染的時候提供一個監聽器就可以了。
當使用ES6類定義一個元件時,通常的一個事件處理程式就是類上的一個方法,看個例子,Toggle
元件渲染一個按鈕,讓使用者在 “ON” 和 “OFF” 狀態之間切換:
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// 這個繫結是必要的,使`this`在回撥中起作用
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
繫結類方法
在JSX回撥中你必須注意 this
的指向。 在 JavaScript 中,類方法預設沒有 繫結 的。如果你忘記繫結 this.handleClick
並將其傳遞給onClick
,那麼在直接呼叫該函式時,this
會是 undefined
。
這不是 React 特有的行為;這是 JavaScript
中的函式如何工作的一部分,可以使用屬性初始值設定來正確地 繫結(bind
) 回撥,但這是實驗性做法,不建議使用,以後有可能會廢棄,如果你沒有使用屬性初始化語法
(1)可以在回撥中使用一個 arrow functions
class LoginButton extends React.Component {
handleClick() {
console.log('this is: ', this)
}
render() {
// 這個語法確保 `this` 被繫結在 handleClick 中
return (
<button onClick={(e) => this.handleClick(e)}>
Click me
</button>
);
}
}
(2)使用Function.prototype.bind
方法,相對簡潔方便
<button onClick={this.handleClick.bind(this)}>
Click me
</button>
傳遞引數給事件處理程式
在迴圈內部,通常需要將一個額外的引數傳遞給事件處理程式,常用的有一下兩種方案;
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this.id)}>Delete Row</button>
上面兩個例子中,引數 e
作為 React 事件物件將會被作為第二個引數進行傳遞。通過箭頭函式的方式,事件物件必須顯式的進行傳遞,但是通過 bind
的方式,事件物件以及更多的引數將會被隱式的進行傳遞。
六、條件渲染
在 React 中,你可以建立不同的元件封裝你所需要的行為。然後,只渲染它們之中的一些,取決於你的應用的狀態。
整個元件的條件渲染
React 中的條件渲染就可在JS中的條件語句一樣,使用JS操作符如if
或者條件控制符來建立渲染當前的元素,並且讓React更新匹配的UI。比如我們有一個需求,需要判斷使用者是否登入,來顯示不同元件
function UserGreeting(props) {
return <h1>Welcome back!</h1>
}
function GustGrreeting(props) {
return <h1>Please sign up.</h1>
}
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />
}
return <GuestGreeting />
}
ReactDOM.render(
<Greeting isLoggedIn={false} />,
document.getElementById('root')
);
使用元素變數條件渲染部分內容
你可以用變數來儲存元素。這可以幫助您有條件地渲染元件的一部分,而輸出的其餘部分不會更改。下方兩個元件用於顯示登出和登入按鈕
function LoginButton() {
return(
<button onClick={props.onClick}>Login</button>
)
}
function LogoutButton(props) {
return (
<button onClick={props.onclick}>Logout</button>
)
}
登入登出按鈕已做好,接下來需要實現有切換功能的一個有狀態的元件,為了更系統化學習,我們把前面的Greeting
元件一起加進來
class LoginControl extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoginedIn: false
}
}
handleLoginClick() {
this.setState({ isLoggedIn: true });
}
handleLogoutClick() {
this.setState({ isLoggedIn: false });
}
render() {
const isLoggedIn = this.state.isLoggedIn;
let button = null;
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick.bind(this)} />
} else {
button = <LoginButton onclick={this.handleLoginClick.bind(this)} />
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />{button}</div>
</div>
)
}
}
reactDOM.render(
<LoginControl />,
document.getElementById('root')
)
使用if
是很常見的一種做法,當然也有一些更簡短的語。JSX
中有幾種內聯條件的方法,
(1)使用邏輯與&&操作符的內聯if用法
我們可以在 JSX
中嵌入任何表示式,方法是將其包裹在花括號中,同樣適用於JS的邏輯與&&運算子
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
{ unreadMeaasges.length > 0 &&
<h2> You have {unreadMessages.length} unread messages.
}
</div>
)
}
cosnt message = ['React', 'Re: React', 'Re:Re: React'];
ReactDOM.render(
<Mailbox unreadMessages={messages} />,
document.getElementById('root')
);
該案例是可以正常執行的,因為在 JavaScript
中, true && expression
總是會評估為 expression
,而 false && expression
總是執行為 false
。並且我們可以在表示式中嵌入表示式
(2)使用條件操作符的內聯If-Else(三目運算子)
條件操作符 即三目表示式:condition ? trueExpression : falseExpression
// 條件渲染字串
<div>The user is {isLoggedIn ? 'currently' : 'not'} logged in.</div>
// 條件渲染元件
<div>
{isLoggedIn ? (
<LogoutButton onClick={this.handleLogoutClick} />
) : (
<LoginButton onClick={this.handleLoginClick} />
)}
</div>
總之,遵循一個原則,哪種方式易於閱讀,就選擇哪種寫法。並且,但條件變得越來越複雜時,可能是提取元件的好時機。
阻止元件渲染
在極少數情況下,您可能希望元件隱藏自身,即使它是由另一個元件渲染的。為此,返回 null
而不是其渲染輸出。注意這裡是不渲染,不是不顯示。
在下面的例子中,根據名為warn
的 props
值,呈現 <WarningBanner />
。如果 props
值為 false
,則該元件不渲染:
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 }
}
handleToggleClick() {
this.setState(prevState => ({
showWarning: !prevState.showWarning
}));
}
render() {
return (
<div>
<Warningbanner warn={this.state.showWarning} />
<button onClick={this.handleToggleClick.bind(this)}>
{ this.state.showWarning ? 'Hide' : 'Show'}
</button>
</div>
)
}
}
ReactDOM.render(
<Page />,
document.getElementById('root')
)
從元件的 render
方法返回 null
不會影響元件生命週期方法的觸發。 例如, componentWillUpdate
和componentDidUpdate
仍將被呼叫。因此需要元件剛載入時就要判斷執行返回null
相關文章
- 基礎1
- ETH基礎(1)
- Pygame基礎(1)GAM
- 1、Lua 基礎
- 【PHP基礎】1PHP
- Java基礎(1)Java
- java基礎1Java
- Docker 基礎 - 1Docker
- lua 基礎 1
- flex基礎(1)Flex
- css基礎1CSS
- JavaScript基礎1JavaScript
- React基礎篇1React
- 1章 基礎概念
- 分散式基礎(1)分散式
- java 基礎概念 (1)Java
- java基礎整理1Java
- Django-基礎-1Django
- 爬蟲基礎---1爬蟲
- CSS——CSS基礎(1)CSS
- python 基礎1Python
- 1、數倉基礎
- 1、基礎知識
- 【WEB基礎】HTML & CSS 基礎入門(1)初識WebHTMLCSS
- 《前端運維》一、Linux基礎--基礎命令(1)前端運維Linux
- 0基礎學GUI,先從基礎開始1GUI
- python 基礎習題1--基礎語法Python
- HTML 基礎重點(1)HTML
- PHP 基礎知識-1PHP
- JavaFX基礎:1: 簡介Java
- Linux shell基礎1Linux
- DDD基礎知識1
- 機器學習基礎知識1機器學習
- Elasticsearch(1):基礎入門Elasticsearch
- Java基礎(1)——ThreadLocalJavathread
- rust 基礎 (1) ——概覽Rust
- Java基礎語法1Java
- VUE的基礎案例1Vue