一喧兄、JSX語法
1无畔、花括號(hào) { } 把任意的 [JavaScript 表達(dá)式]嵌入到 JSX 中
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')
);
2、JSX可作表達(dá)式
可以在 if 語句或者是 for 循環(huán)中使用 JSX吠冤,用它給變量賦值浑彰,當(dāng)做參數(shù)接收,或者作為函數(shù)的返回值
function getGreeting(user) {
if (user) {
return <h1>Hello, {formatName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}
3拯辙、用 JSX 指定屬性值
(1)用雙引號(hào) “” 來指定字符串字面量作為屬性值
(2)用花括號(hào) {} 嵌入一個(gè) JavaScript 表達(dá)式作為屬性值
在屬性中嵌入 JavaScript 表達(dá)式時(shí)郭变,不要使用引號(hào)來包裹大括號(hào)。否則涯保,JSX 將該屬性視為字符串字面量而不是表達(dá)式诉濒。對(duì)于字符串值你應(yīng)該使用引號(hào),對(duì)于表達(dá)式你應(yīng)該使用大括號(hào)夕春,但兩者不能同時(shí)用于同一屬性未荒。
const element = <div tabIndex="0"></div>;
const element = <img src={user.avatarUrl}></img>;
4、用 JSX 指定子元素
(1)如果是空標(biāo)簽及志,您應(yīng)該像 XML 一樣片排,使用 />立即閉合它
(2)JSX 標(biāo)簽可能包含子元素
const element = <img src={user.avatarUrl} />;
const element = (
<div>
<h1>Hello!</h1>
<h2>Good to see you here.</h2>
</div>
);
5寨腔、JSX 表示對(duì)象
Babel 將JSX編譯成 React.createElement() 調(diào)用。
//兩種形式等價(jià)
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
二率寡、元素渲染
元素(Elements)是 React 應(yīng)用中最小的構(gòu)建部件(或者說構(gòu)建塊脆侮,building blocks),不同于組件勇劣。
const element = <h1>Hello, world</h1>;
1、渲染一個(gè)元素到 DOM
要渲染一個(gè) React 元素到一個(gè) root DOM 節(jié)點(diǎn)潭枣,把它們傳遞給 ReactDOM.render() 方法:
const element = <h1>Hello, world</h1>;
ReactDOM.render(
element,
document.getElementById('root')
);
2 比默、更新已渲染的元素
React 元素是不可突變的. 一旦你創(chuàng)建了一個(gè)元素, 就不能再修改其子元素或任何屬性。一個(gè)元素就像電影里的一幀: 它表示在某一特定時(shí)間點(diǎn)的 UI 盆犁。更新 UI 的唯一方法是創(chuàng)建一個(gè)新的元素, 并將其傳入 ReactDOM.render()方法.
3命咐、React 只更新必需要更新的部分
React DOM 會(huì)將元素及其子元素與之前版本逐一對(duì)比, 并只對(duì)有必要更新的 DOM 進(jìn)行更新, 以達(dá)到 DOM 所需的狀態(tài)。
三谐岁、組件(Components) 和 屬性(Props)
組件使你可以將 UI 劃分為一個(gè)一個(gè)獨(dú)立醋奠,可復(fù)用的小部件,并可以對(duì)每個(gè)部件進(jìn)行單獨(dú)的設(shè)計(jì)伊佃。
1窜司、函數(shù)式組件和類組件
//函數(shù)式組件
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
//類組件
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
2、渲染組件
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
- 我們調(diào)用了 ReactDOM.render() 方法并向其中傳入了 <Welcome name="Sara" /> 元素航揉。
- React 調(diào)用 Welcome 組件塞祈,并向其中傳入了 {name: 'Sara'} 作為 props 對(duì)象。
- Welcome 組件返回 <h1>Hello, Sara</h1>帅涂。
- React DOM 迅速更新 DOM 议薪,使其顯示為 <h1>Hello, Sara</h1>。
注意:
-
組件名稱總是以大寫字母開始媳友。
<div /> 代表一個(gè) DOM 標(biāo)簽斯议,而 <Welcome /> 則代表一個(gè)組件,并且需要在作用域中有一個(gè) Welcome 組件醇锚。 - 組件必須返回一個(gè)單獨(dú)的根元素哼御。這就是為什么我們添加一個(gè) <div> 來包含所有 <Welcome /> 元素的原因。
function App() {
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
3焊唬、提取組件
提取組件可能看起來是一個(gè)繁瑣的工作艇搀,但是在大型的 Apps 中可以回報(bào)給我們的是大量的可復(fù)用組件。一個(gè)好的經(jīng)驗(yàn)準(zhǔn)則是如果你 UI 的一部分需要用多次 (Button求晶,Panel焰雕,Avatar),或者本身足夠復(fù)雜(App芳杏,F(xiàn)eedStory矩屁,Comment)辟宗,最好的做法是使其成為可復(fù)用組件。
function Comment(props) {
return (
<div className="Comment">
<UserInfo user={props.author} />
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
4吝秕、所有 React 組件都必須是純函數(shù)泊脐,并禁止修改其自身 props
四、狀態(tài)(State) 和 生命周期
1烁峭、把函數(shù)式組件轉(zhuǎn)化為類組件
遵從以下5步, 把一個(gè)類似 Clock這樣的函數(shù)式組件轉(zhuǎn)化為類組件:
- 創(chuàng)建一個(gè)繼承自 React.Component類的 ES6 class 同名類容客。
- 添加一個(gè)名為 render() 的空方法。
- 把原函數(shù)中的所有內(nèi)容移至 render() 中约郁。
- 在 render() 方法中使用 this.props 替代 props缩挑。
- 刪除保留的空函數(shù)聲明。
Clock 現(xiàn)在被定為類組件鬓梅,而不是函數(shù)式組件供置。
類允許我們?cè)谄渲刑砑颖镜貭顟B(tài)(state)和生命周期鉤子。
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
2绽快、在類組件中添加本地狀態(tài)(state)
我們現(xiàn)在通過以下3步, 把date從屬性(props) 改為 狀態(tài)(state):
class Clock extends React.Component {
2芥丧、添加一個(gè) 類構(gòu)造函數(shù)(class constructor)初始化 this
constructor(props) {
super(props);
this.state = {date: new Date()};
}
1、替換 render() 方法中的 this.props.date 為 this.state.date
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
3坊罢、移除 <Clock /> 元素中的 date 屬性
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
3续担、在類中添加生命周期方法
在一個(gè)具有許多組件的應(yīng)用程序中,在組件被銷毀時(shí)釋放所占用的資源是非常重要的活孩。
當(dāng) Clock 第一次渲染到DOM時(shí)赤拒,我們要設(shè)置一個(gè)定時(shí)器。 這在 React 中稱為 “掛載(mounting)” 诱鞠。
當(dāng) Clock 產(chǎn)生的 DOM 被銷毀時(shí)挎挖,我們也想 清除該計(jì)時(shí)器 。 這在 React 中稱為 “卸載(unmounting)” 航夺。
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
1蕉朵、this.props 由 React 本身設(shè)定, 而 this.state 具有特殊的含義,但如果需要存儲(chǔ)一些不用于視覺輸出的內(nèi)容阳掐,則可以手動(dòng)向類中添加額外的字段始衅。
如果在 render() 方法中沒有被引用, 它不應(yīng)該出現(xiàn)在 state 中。
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
2缭保、我們?cè)赾omponentWillUnmount()生命周期鉤子中取消這個(gè)計(jì)時(shí)器
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')
);
回顧一下該過程汛闸,以及調(diào)用方法的順序:
- 當(dāng) <Clock /> 被傳入 ReactDOM.render() 時(shí), React 會(huì)調(diào)用 Clock組件的構(gòu)造函數(shù)。 因?yàn)?Clock 要顯示的是當(dāng)前時(shí)間艺骂,所以它將使用包含當(dāng)前時(shí)間的對(duì)象來初始化 this.state 诸老。我們稍后會(huì)更新此狀態(tài)。
- 然后 React 調(diào)用了 Clock 組件的 render() 方法钳恕。 React 從該方法返回內(nèi)容中得到要顯示在屏幕上的內(nèi)容别伏。然后蹄衷,React 然后更新 DOM 以匹配 Clock 的渲染輸出。
- 當(dāng) Clock 輸出被插入到 DOM 中時(shí)厘肮,React 調(diào)用 componentDidMount() 生命周期鉤子愧口。在該方法中,Clock 組件請(qǐng)求瀏覽器設(shè)置一個(gè)定時(shí)器來一次調(diào)用 tick()类茂。
- 瀏覽器會(huì)每隔一秒調(diào)用一次 tick()方法耍属。在該方法中, Clock 組件通過 setState() 方法并傳遞一個(gè)包含當(dāng)前時(shí)間的對(duì)象來安排一個(gè) UI 的更新巩检。通過 setState(), React 得知了組件 state(狀態(tài))的變化, 隨即再次調(diào)用 render() 方法厚骗,獲取了當(dāng)前應(yīng)該顯示的內(nèi)容。 這次碴巾,render() 方法中的 this.state.date 的值已經(jīng)發(fā)生了改變, 從而丑搔,其輸出的內(nèi)容也隨之改變厦瓢。React 于是據(jù)此對(duì) DOM 進(jìn)行更新。
- 如果通過其他操作將 Clock 組件從 DOM 中移除了, React 會(huì)調(diào)用 componentWillUnmount() 生命周期鉤子, 所以計(jì)時(shí)器也會(huì)被停止啤月。
4煮仇、正確地使用 State(狀態(tài))
setState() 有三件事是你應(yīng)該知道的:
(1)用 setState() 設(shè)置狀態(tài):
this.setState({comment: 'Hello'});
(2)state(狀態(tài)) 更新可能是異步的:
React 為了優(yōu)化性能,有可能會(huì)將多個(gè) setState() 調(diào)用合并為一次更新谎仲。
因?yàn)?this.props 和 this.state 可能是異步更新的浙垫,你不能依賴他們的值計(jì)算下一個(gè)state(狀態(tài))。
例如, 以下代碼可能導(dǎo)致 counter(計(jì)數(shù)器)更新失斨E怠:
// 錯(cuò)誤
this.setState({
counter: this.state.counter + this.props.increment,
});
要解決這個(gè)問題夹姥,應(yīng)該使用第 2 種 setState() 的格式,它接收一個(gè)函數(shù)辙诞,而不是一個(gè)對(duì)象辙售。該函數(shù)接收前一個(gè)狀態(tài)值作為第 1 個(gè)參數(shù), 并將更新后的值作為第 2個(gè)參數(shù):
要彌補(bǔ)這個(gè)問題飞涂,使用另一種 setState() 的形式旦部,它接受一個(gè)函數(shù)而不是一個(gè)對(duì)象。這個(gè)函數(shù)將接收前一個(gè)狀態(tài)作為第一個(gè)參數(shù)较店,應(yīng)用更新時(shí)的 props 作為第二個(gè)參數(shù):
// 正確
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
// 正確
this.setState(function(prevState, props) {
return {
counter: prevState.counter + props.increment
};
});
(3)state(狀態(tài))更新會(huì)被合并
當(dāng)你調(diào)用 setState()士八, React 將合并你提供的對(duì)象到當(dāng)前的狀態(tài)中。
例如梁呈,你的狀態(tài)可能包含幾個(gè)獨(dú)立的變量:
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
然后通過調(diào)用獨(dú)立的 setState() 調(diào)用分別更新它們:
合并是淺合并婚度,所以 this.setState({comments}) 不會(huì)改變 this.state.posts 的值,但會(huì)完全替換this.state.comments 的值官卡。
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
5陕见、數(shù)據(jù)向下流動(dòng)
無論作為父組件還是子組件秘血,它都無法獲悉一個(gè)組件是否有狀態(tài),同時(shí)也不需要關(guān)心另一個(gè)組件是定義為函數(shù)組件還是類組件评甜。
這就是 state(狀態(tài)) 經(jīng)常被稱為 本地狀態(tài) 或 封裝狀態(tài)的原因灰粮。 它不能被擁有并設(shè)置它的組件 以外的任何組件訪問。
一個(gè)組件可以選擇將 state(狀態(tài)) 向下傳遞忍坷,作為其子組件的 props(屬性):
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
同樣適用于用戶定義組件:
<FormattedDate date={this.state.date} />
FormattedDate 組件通過 props(屬性) 接收了 date 的值粘舟,但它仍然不能獲知該值是來自于 Clock的 state(狀態(tài)) ,還是 Clock 的 props(屬性)佩研,或者是直接手動(dòng)創(chuàng)建的:
function FormattedDate(props) {
return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}
這通常稱為一個(gè)“從上到下”柑肴,或者“單向”的數(shù)據(jù)流。任何 state(狀態(tài)) 始終由某個(gè)特定組件所有旬薯,并且從該 state(狀態(tài)) 導(dǎo)出的任何數(shù)據(jù) 或 UI 只能影響樹中 “下方” 的組件晰骑。
如果把組件樹想像為 props(屬性) 的瀑布,所有組件的 state(狀態(tài)) 就如同一個(gè)額外的水源匯入主流绊序,且只能隨著主流的方向向下流動(dòng)硕舆。
五、處理事件
1骤公、與普通HTML區(qū)別
(1) React 事件使用駝峰命名抚官,而不是全部小寫。
(2)通過 JSX , 你傳遞一個(gè)函數(shù)作為事件處理程序阶捆,而不是一個(gè)字符串凌节。
<button onClick={activateLasers}>
Activate Lasers
</button>
(3)在 React 中你不能通過返回 false 來阻止默認(rèn)行為。必須明確調(diào)用 preventDefault 洒试。
例如倍奢,對(duì)于純 HTML ,要阻止鏈接打開一個(gè)新頁面的默認(rèn)行為垒棋,可以這樣寫:
<a href="#" onclick="console.log('The link was clicked.'); return false">
Click me
</a>
在 React 中, 應(yīng)該這么寫:
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
(4)當(dāng)使用一個(gè) ES6 類定義一個(gè)組件時(shí)娱挨,通常的一個(gè)事件處理程序是類上的一個(gè)方法。
Toggle 組件渲染一個(gè)按鈕捕犬,讓用戶在 “ON” 和 “OFF” 狀態(tài)之間切換:
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// 這個(gè)綁定是必要的跷坝,使`this`在回調(diào)中起作用
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')
);
2、將參數(shù)傳遞給事件處理程序
在循環(huán)內(nèi)部碉碉,通常需要將一個(gè)額外的參數(shù)傳遞給事件處理程序柴钻。 例如,如果 id 是一個(gè)內(nèi)聯(lián) ID垢粮,則以下任一方式都可以正常工作:
等價(jià)的贴届,分別使用 arrow functions 和 Function.prototype.bind
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
上面兩個(gè)例子中,參數(shù) e 作為 React 事件對(duì)象將會(huì)被作為第二個(gè)參數(shù)進(jìn)行傳遞。通過箭頭函數(shù)的方式毫蚓,事件對(duì)象必須顯式的進(jìn)行傳遞占键,但是通過 bind 的方式,事件對(duì)象以及更多的參數(shù)將會(huì)被隱式的進(jìn)行傳遞元潘。
六畔乙、條件渲染
1、在函數(shù)式組件中用if判斷
我們需要?jiǎng)?chuàng)建一個(gè) Greeting 組件, 用來根據(jù)用戶是否登錄, 判斷并顯示上述兩個(gè)組件之一:
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
ReactDOM.render(
// 修改為 isLoggedIn={true} 試試:
<Greeting isLoggedIn={false} />,
document.getElementById('root')
);
2翩概、元素變量
在接下來的例子中牲距,我們將會(huì)創(chuàng)建一個(gè)有狀態(tài)組件,叫做 LoginControl 钥庇。
它將渲染 <LoginButton /> 或者 <LogoutButton /> 牍鞠,取決于當(dāng)前狀態(tài)。同時(shí)渲染前面提到的 <Greeting /> 組件:
class LoginControl extends React.Component {
constructor(props) {
super(props);
this.handleLoginClick = this.handleLoginClick.bind(this);
this.handleLogoutClick = this.handleLogoutClick.bind(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')
);
3评姨、使用邏輯 && 操作符的內(nèi)聯(lián) if 用法
(1)可以在JSX中嵌入任何表達(dá)式难述,方法是將其包裹在花括號(hào)中
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')
);
(2)條件操作符 condition ? true : false
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in.
</div>
);
}
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
{isLoggedIn ? (
<LogoutButton onClick={this.handleLogoutClick} />
) : (
<LoginButton onClick={this.handleLoginClick} />
)}
</div>
);
}
七、列表(Lists) 和 鍵(Keys)
1吐句、基本列表組件
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={numbers} />,
document.getElementById('root')
);
2胁后、鍵(Keys)
鍵(Keys) 幫助 React 標(biāo)識(shí)哪個(gè)項(xiàng)被修改、添加或者移除了蕴侧。數(shù)組中的每一個(gè)元素都應(yīng)該有一個(gè)唯一不變的鍵(Keys)來標(biāo)識(shí):
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);
(1)使用 keys 提取組件
keys 只在數(shù)組的上下文中存在意義择同。
例如两入,如果你提取一個(gè) ListItem 組件净宵,應(yīng)該把 key 放置在數(shù)組處理的 <ListItem /> 元素中,不能放在 ListItem 組件自身中的 <li> 根元素上裹纳。
function ListItem(props) {
// 正確择葡!這里不需要指定 key :
return <li>{props.value}</li>;
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// 正確!key 應(yīng)該在這里被指定
<ListItem key={number.toString()}
value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
(2)keys 在同輩元素中必須是唯一的
在數(shù)組中使用的 keys 必須在它們的同輩之間唯一剃氧。然而它們并不需要全局唯一敏储。我們可以在操作兩個(gè)不同數(shù)組的時(shí)候使用相同的 keys
(3)鍵是React的一個(gè)內(nèi)部映射,但其不會(huì)傳遞給組件的內(nèi)部朋鞍。
如果你需要在組件中使用相同的值已添,可以明確使用一個(gè)不同名字的 prop 傳入。