—— 事件處理、
此文檔來自 React 官方文檔洞就,在英文原文的基礎(chǔ)上進(jìn)行了增刪改盆繁,用于我本人的研究與學(xué)習(xí),暫不支持轉(zhuǎn)載旬蟋。因?yàn)楸救说乃絾栴}油昂,來取經(jīng)的同學(xué)也請(qǐng)慎用。
事件處理
React 元素的事件處理與 DOM 元素的世界處理很相似倾贰,這里有幾點(diǎn)語法上的不同點(diǎn):
- React 事件使用駝峰法命名而不是全部小寫冕碟。
- 使用 JSX 時(shí)傳遞一個(gè)函數(shù)作為事件處理方法而不是一個(gè)字符串。
比如匆浙,使用 HTML:
<button onclick="activateLasers()">
Activate Lasers
</button>
與 React 有些許不同:
<button onClick={activateLasers}>
Activate Lasers
</button>
另一個(gè)不同點(diǎn)是安寺,你不可以使用返回 false 的方法來阻止默認(rèn)行為。需要調(diào)用 preventDefault() 方法首尼。比如挑庶,對(duì)于純 HTML 來說言秸,為了防止鏈接打開新頁面的默認(rèn)行為,你可以寫:
<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>
);
}
在此举畸,e 是一個(gè)合成事件。React 根據(jù) W3C spec 定義了這些合成事件凳枝,所以你不需要考慮跨瀏覽器兼容問題抄沮。
當(dāng)你使用 React 你基本不需要調(diào)用 addEventListener 來給 DOM 元素添加監(jiān)聽事件。取而代之的范舀,只需要在元素最開始被創(chuàng)建時(shí)提供一個(gè)監(jiān)聽即可合是。
當(dāng)你使用 ES6 定義一個(gè)組件了罪,通常的做法是將事件監(jiān)聽作為 class 的一個(gè)方法锭环。比如,下面這個(gè) Toggle 組件渲染了一個(gè)按鈕泊藕,可以讓用戶在 ON 和 OFF 之間轉(zhuǎn)換辅辩。
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// This binding is necessary to make 'this' work in the callback
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 回調(diào)函數(shù)中 this 的含義。在 JavaScript 中娃圆,類方法通常都不會(huì)自動(dòng)綁定玫锋。如果你忘了綁定 this.handleClick 而且將其傳遞給 onClick,當(dāng)函數(shù)被調(diào)用時(shí)讼呢,this 就會(huì)是 undefined撩鹿。
這不是 React 的特殊行為;這是 JavaScript 函數(shù)工作原理的一部分悦屏。通常情況下节沦,如果你更喜歡調(diào)用不帶()的方法,例如 onClick = {this.handleClick}础爬,你應(yīng)該綁定方法甫贯。
如果決定 bind 很煩的話,下面有兩個(gè)變通方法看蚜。如果你使用尚在實(shí)驗(yàn)的 public class fields syntax叫搁,你可以使用 類字段(class fields)來正確地綁定回調(diào)函數(shù):
class LoggingButton extends React.Component {
// This syntax ensures `this` is bound within handleClick.
// Warning: this is *experimental* syntax.
handleClick = () => {
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
這種格式在 Create React App中是默認(rèn)可用的。
如果你不想使用類字段格式供炎,你可以在回調(diào)函數(shù)中使用箭頭格式:
class LoggingButton extends React.Component {
handleClick() {
console.log ('this is"', this);
}
render () {
// 這種格式可以確保 this 與 handleClick 綁定
return (
<button onClick={(e) => this.handleClick(e)}>
Click me
</button>
);
}
}
這種格式帶來的問題是當(dāng)每次 LoggingButton 渲染時(shí)都會(huì)創(chuàng)建不同的回調(diào)函數(shù)渴逻。在大多數(shù)的場(chǎng)景里,這是沒問題的音诫。然而惨奕,如果這個(gè)回調(diào)函數(shù)是作為一個(gè) 屬性 被傳遞到下層的組件中,這些組件可能會(huì)額外做一次渲染纽竣。我們通常推薦前兩種方法墓贿,來避免問題蒂教。
向事件處理器傳遞參數(shù)
在循環(huán)中,通常都需要向每個(gè)事件處理方法中傳入一個(gè)額外的計(jì)數(shù)參數(shù)赤兴。如下所示腔稀,假設(shè) id 是行ID,下面兩種方法都可以正常運(yùn)行:
<button onClick={(e) => this.deleteRow(id. e)}> Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
上面這兩種是等價(jià)的幽勒,各自使用了箭頭函數(shù)和 Function.prototype.bind嗜侮。
在這兩種方法中,參數(shù) e 會(huì)作為在 ID 之后的第二個(gè)參數(shù)傳入 React 事件中啥容。在箭頭函數(shù)中锈颗,我們必須明確地傳給它,但是使用 bind 時(shí)咪惠,多余的參數(shù)會(huì)自動(dòng)傳入击吱。
條件渲染
在 React 中,你可以創(chuàng)建不同的組件來封裝你想要完成的行為遥昧。然后你可以渲染其中的部分覆醇,決定于你組件的 state。
React 中的條件渲染的工作原理和 JavaScript 中的條件判斷相同炭臭。使用 JavaScript 操作符類似 if 和 條件操作符來創(chuàng)建由當(dāng)前狀態(tài)決定的元素永脓,然后讓 React 更新 UI 來匹配它們。
查看下面兩個(gè)組件:
function UserGreeting (props) {
return <h1>Welcome back!</h1>;
}
function GusetGreeting (props) {
return <h1>Please sign up.</h1>
}
我們創(chuàng)建了一個(gè) Greeting 組件鞋仍,其最終展示的內(nèi)容決定于哪個(gè)用戶進(jìn)行了登錄:
function Greeting (props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <userGreeting />;
}
return <GuestGreeting />;
}
ReactDOM.render(
// Try changing to isLoggedIn = {true}
<Greeting isLoggedIn = {false} />,
document.getElementById('root')
);
在這個(gè)例子中常摧,渲染哪個(gè)問候語句取決于屬性 isLoggedIn 的值。
元素變量
你可以使用變量來存儲(chǔ)元素威创。這可以幫助你在其他部分的輸出不變的情況下條件渲染一部分組件落午。
來看下面兩個(gè)新組件,分別為登錄和登出按鈕那婉。
function LoginButton(props) {
return (
<button ocClick={props.onClick}
Login
</button>
);
}
function LogoutButton(props) {
return (
<button onClick={props.onClick}>
Logout
</button>
);
}
在下面這個(gè)例子中板甘,我們創(chuàng)建一個(gè)包含狀態(tài)的組件 LoginControl。
這個(gè)組件會(huì)根據(jù)當(dāng)前的狀態(tài)來渲染 <LoginButton /> 或者 <LogoutButton />详炬。同時(shí)也會(huì)渲染前面例子中的 <Greeting /> 組件盐类。
class LoginControl extends React.Component {
constructor(props) {
super(props);
this.handleLoginClick = this.handleLoginClick.bind(this);
this.handleLogoutClick = this.handleLogoutClick.bing(this);
this.state = {isLoggedIn: 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} />;
} else {
button = <LoginButton onClick = {this.handleLoginClick} />;
}
return (
<div>
<Greeting isLoggedIn = {isLoggedIn}
{button}
</div>
);
}
}
ReactDOM.render(
<LoginControl />,
document.getElementById('root')
);
定義一個(gè)變量,使用 if 條件判斷是條件渲染組件的一種很好的方法呛谜,有時(shí)你可能想要使用更簡(jiǎn)潔的格式在跳。下面有幾種在 JSX 中使用條件判斷的例子。
內(nèi)聯(lián) if 和 邏輯 && 運(yùn)算符
你可以在 JSX 中嵌入任何表達(dá)式隐岛,當(dāng)然也包括 JavaScript 的 邏輯運(yùn)算符 &&猫妙。可以方便地在一個(gè)元素中使用條件渲染:
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
</div>
);
}
const messages = ['React', 'Re: React', 'Re:Re: React'];
ReactDOM.render(
<Mailbox unreadMessages={messages} />,
document.getElementById('root')
);
在 JavaScript 中可以這樣用聚凹,因?yàn)?true && expression 的值總是決定于 expression割坠,false && expression 的值總是 false齐帚。
因此,如果條件判斷為 true彼哼,元素右邊 && 之后的會(huì)在輸出中顯示对妄。如果條件判斷為 false,React 會(huì)忽略后面的值敢朱。
內(nèi)聯(lián) If-Else 與條件運(yùn)算符
另一個(gè)內(nèi)聯(lián)的方法是使用 condition ? true : false 條件運(yùn)算符剪菱。
在下面這個(gè)例子中,我們使用這種方法渲染一小段文字:
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in.
</div>
);
}
它同樣可以在大一點(diǎn)的表達(dá)式中使用拴签,雖然它的可讀性差一些孝常。
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
{isLoggedIn ? (
<LogoutButton onClick={this.handleLogoutClick} />
) : (
<LoginButton onClick={this.handleLoginClick} />
)}
</div>
);
}
就像在 JavaScript 中一樣,你和你的團(tuán)隊(duì)可以根據(jù)可讀性的需求來選擇不同的方法蚓哩。同樣記住一點(diǎn)就是构灸,如果條件變得很復(fù)雜,就該分離組件了杖剪。
讓組件不被渲染
在有些情況下冻押,你可能想要組件被渲染后隱藏驰贷。想要實(shí)現(xiàn)只需要返回 null 而不是它的輸出盛嘿。
在下面這個(gè)例子中,<WarningBanner /> 是否渲染是由其屬性 warn 的值決定的括袒,如果這個(gè)屬性的值是 false的次兆,組件就不會(huì)渲染:
function WarningBanner (props) {
if(!props.warn) {
return null;
}
return (
<div className = "warning">
Warning!
</div>
);
}
class Page extends React.Component {
constructor() {
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')
);
在組件的 render 方法中返回 null 并不會(huì)影響組件的生命周期方法。例如锹锰,componentDidUpdate 和 componentWillUpdate 仍然會(huì)調(diào)用芥炭。
列表和值
首先我們來回顧一下在 JavaScript 中你是如何處理列表的。
查看下面的代碼恃慧,我們使用 map() 函數(shù)將 numbers 的
每個(gè)數(shù)字double园蝠。我們將 map() 的返回值賦值給 doubled 然后輸出:
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((number) => number * 2);
console.log (doubled);
這段代碼會(huì)在控制臺(tái)輸出 [1, 4, 6, 8, 10];
在 React 中,將數(shù)組轉(zhuǎn)換為元素列表也是與之類似的痢士。
渲染多個(gè)組件
你可以創(chuàng)建多個(gè)元素并且用 {} 將他們放在 JSX 中彪薛。
在下面,我們使用 JavaScript 的 map() 函數(shù)來循環(huán) numbers 數(shù)組怠蹂,每項(xiàng)返回一個(gè) <li> 元素善延,并且將其放入 listItems中:
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li>{number}</li>
);
我們把整個(gè) listItems 數(shù)組放在一個(gè) <ul> 元素中,并且將其渲染到 DOM 中:
ReactDOM.render(
<ul>{listItems}</ul>,
document.getElementById('root')
);
這段代碼會(huì)展示從1到5的列表城侧。
基本的列表組件
通常情況下易遣,你要在組件里渲染列表。
我們可以在組件中重構(gòu)上面的代碼嫌佑,組件接受一個(gè)數(shù)字?jǐn)?shù)組豆茫,然后輸出一個(gè)無序列表元素侨歉。
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li>{number}</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render (
<NumberList numbers = {number} />,
documents.getElementById('root')
);
當(dāng)你運(yùn)行這段代碼,你會(huì)得到一個(gè)警告:你應(yīng)該給列表中的每一項(xiàng)賦予一個(gè) 鍵(key)揩魂。鍵 是一個(gè)特殊的字符串屬性为肮,當(dāng)你創(chuàng)建一個(gè)元素列表時(shí),每一個(gè)元素都應(yīng)該擁有肤京。在下一節(jié)中我們會(huì)討論鍵為什么這么重要颊艳。
我們給 numbers.map() 中的每一個(gè)元素分配一個(gè) key 來解決 key 的問題:
function NumberList (props) {
const numbers = props.numbers;
const listItems = number.map((number) =>
<li key = {number.toString()}>
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render (
<NumberList numbers = {numbers} />,
document.getElementById('root')
);
鍵 Key
Key 可以幫著 React 識(shí)別哪些項(xiàng)發(fā)證了改變,被添加忘分,或被移除棋枕。每個(gè)數(shù)組中的元素都應(yīng)該賦予一個(gè) key 來給予他們一個(gè)穩(wěn)定的標(biāo)志:
const numbers = [1, 2, 3, 4, 5];
const listItens = numbers.map((number) =>
<li key = {number.toString()}>
{number}
</li>
);
選取 key 值的最好方法是使用一個(gè)不同于其兄弟節(jié)點(diǎn)的字符串來進(jìn)行標(biāo)識(shí)。通常會(huì)使用數(shù)據(jù)的 ID 值妒峦。
const todoItems = todos.map((todo) =>
<li key = {todo.id}>
{todo.text}
</li>
);
如果你要渲染的項(xiàng)沒有一個(gè)穩(wěn)定的 ID 值重斑,那么你可以使用項(xiàng)的索引來當(dāng)做 key:
const todoItems = todos.map((todo, index) =>
// 只用在項(xiàng)沒有穩(wěn)定的 ID 情況下
<li key = {index}>
{todo.text}
</li>
);
我們不推薦在項(xiàng)可能被重新排序的情況下使用索引來作為 key 值,因?yàn)槟菢訒?huì)降低效率肯骇。如果感興趣的話你也可以查看 key 值重要的深層次原因窥浪。
** To Be Continued...**