
JSX
在 JSX 語法中,你可以在大括號內(nèi)放置任何有效的 JavaScript 表達式。
例如,2 + 2,user.firstName 或 formatName(user) 都是有效的 JavaScript 表達式。
為了便于閱讀,我們會將 JSX 拆分為多行,建議將內(nèi)容包裹在括號中
你可以在 if 語句和 for 循環(huán)的代碼塊中使用 JSX,將 JSX 賦值給變量,把 JSX 當作參數(shù)傳入,以及從函數(shù)中返回 JSX
你可以通過使用引號,來將屬性值指定為字符串字面量:
const element = <div tabIndex="0"></div>;
也可以使用大括號,來在屬性值中插入一個 JavaScript 表達式:
const element = <img src={user.avatarUrl}></img>;
在屬性中嵌入 JavaScript 表達式時,不要在大括號外面加上引號。你應該僅使用引號(對于字符串值)或大括號(對于表達式)中的一個,對于同一屬性不能同時使用這兩種符號。
注意:
因為 JSX 語法上更接近 JavaScript 而不是 HTML,所以 React DOM 使用 camelCase(小駝峰命名)來定義屬性的名稱,而不使用 HTML 屬性名稱的命名約定。
JSX 防止注入攻擊
React DOM 在渲染所有輸入內(nèi)容之前,默認會進行轉(zhuǎn)義。它可以確保在你的應用中,永遠不會注入那些并非自己明確編寫的內(nèi)容。所有的內(nèi)容在渲染之前都被轉(zhuǎn)換成了字符串。這樣可以有效地防止 XSS(cross-site-scripting, 跨站腳本)攻擊。
JSX 表示對象
Babel 會把 JSX 轉(zhuǎn)譯成一個名為 React.createElement() 函數(shù)調(diào)用。
const element = (
<h1 className="greeting">
Hello, world!
</h1>
)
//兩種代碼完全等效
const element = React.createElement(
//接受三個參數(shù),標簽名,屬性參數(shù)對象,子元素
'h1',
{className: 'greeting'},
'Hello, world!'
)
這些對象被稱為 “React 元素”。它們描述了你希望在屏幕上看到的內(nèi)容。React 通過讀取這些對象,然后使用它們來構(gòu)建 DOM 以及保持隨時更新。
元素渲染
元素是構(gòu)成 React 應用的最小磚塊
與瀏覽器的 DOM 元素不同,React 元素是創(chuàng)建開銷極小的普通對象。React DOM 會負責更新 DOM 來與 React 元素保持一致。
僅使用 React 構(gòu)建的應用通常只有單一的根 DOM 節(jié)點
<div id="root"></div>
想要將一個 React 元素渲染到根 DOM 節(jié)點中,只需把它們一起傳入 ReactDOM.render():
const element = <h1>Hello, world</h1>;
ReactDOM.render(element, document.getElementById('root'));
在實踐中,大多數(shù) React 應用只會調(diào)用一次 ReactDOM.render()
React 只更新它需要更新的部分.
React DOM 會將元素和它的子元素與它們之前的狀態(tài)進行比較,并只會進行必要的更新來使 DOM 達到預期的狀態(tài)。
考慮 UI 在任意給定時刻的狀態(tài),而不是隨時間變化的過程,能夠消滅一整類的 bug
組件&Props
獨立,可復用,單獨構(gòu)思
組件,從概念上類似于 JavaScript 函數(shù)。它接受任意的入?yún)ⅲ?“props”),并返回用于描述頁面展示內(nèi)容的 React 元素。
函數(shù)組件與class組件
用函數(shù)定義組件
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;}
用ES6語法的class定義組件:
class Welcome extends React.Component{
render(){
return <h1>Hello, {this.props.name}</h1>
}
}
上面兩者等效
如何將函數(shù)組件轉(zhuǎn)換成class組件:
- 創(chuàng)建一個同名的 ES6 class,并且繼承于 React.Component。
- 添加一個空的 render() 方法。
- 將函數(shù)體移動到 render() 方法之中。
- 在 render() 方法中使用 this.props 替換 props。
- 刪除剩余的空函數(shù)聲明。
當 React 元素為用戶自定義組件時,它會將 JSX 所接收的屬性(attributes)轉(zhuǎn)換為單個對象傳遞給組件,這個對象被稱之為 “props”。
關(guān)鍵詞:props
組合組件
組件可以在其輸出中引用其他組件。
用同一組件來抽象出任意層次的細節(jié)。
通常來說,每個新的 React 應用程序的頂層組件都是 App 組件。
Props的只讀性
組件無論是使用函數(shù)聲明還是通過 class 聲明,都決不能修改自身的 props。
純函數(shù):不會嘗試更改入?yún)ⅲ叶啻握{(diào)用下相同的入?yún)⑹冀K返回相同的結(jié)果。
React 非常靈活,但它也有一個嚴格的規(guī)則:所有 React 組件都必須像純函數(shù)一樣保護它們的 props 不被更改。
State&生命周期
State 與 props 類似,但是 state 是私有的,并且完全受控于當前組件。
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 組件應該始終使用 props 參數(shù)來調(diào)用父類的構(gòu)造函數(shù)。
class 組件聲明一些特殊的方法,當組件掛載或卸載時就會去執(zhí)行這些方法,叫做“生命周期方法”
componentDidMount和componentWillUnmount
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')
);
正確地使用State
- 不要直接修改State,而是使用setState();
構(gòu)造函數(shù)是唯一可以給 this.state 賦值的地方 - State 的更新可能是異步的
- State 的更新會被合并
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
讓setState()接受一個函數(shù)而不是一個對象。
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
//普通函數(shù)寫法
this.setState(function(state, props) {
return {
counter: state.counter + props.increment
};
});
數(shù)據(jù)是向下流動的
組件可以選擇把它的 state 作為 props 向下傳遞到它的子組件中。
任何的 state 總是所屬于特定的組件,而且從該 state 派生的任何數(shù)據(jù)或 UI 只能影響樹中“低于”它們的組件。
如果你把一個以組件構(gòu)成的樹想象成一個 props 的數(shù)據(jù)瀑布的話,那么每一個組件的 state 就像是在任意一點上給瀑布增加額外的水源,但是它只能向下流動。
事件處理
- React 事件的命名采用小駝峰式(camelCase),而不是純小寫
- 使用 JSX 語法時你需要傳入一個函數(shù)作為事件處理函數(shù),而不是一個字符串
在 React 中另一個不同點是你不能通過返回 false 的方式阻止默認行為。你必須顯式的使用 preventDefault。
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// 為了在回調(diào)中使用 `this`,這個綁定是必不可少的 this.handleClick = this.handleClick.bind(this); }
handleClick() { this.setState(state => ({ isToggleOn: !state.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 中,class 的方法默認不會綁定 this。如果你忘記綁定 this.handleClick 并把它傳入了 onClick,當你調(diào)用這個函數(shù)的時候 this 的值為 undefined.
使用 class fields 正確的綁定回調(diào)函數(shù):
class LoggingButton extends React.Component { // 此語法確保 `handleClick` 內(nèi)的 `this` 已被綁定。 // 注意: 這是 *實驗性* 語法。
handleClick = () => { console.log('this is:', this); }
render() {
return (
<button onClick={this.handleClick}> Click me </button>
);
}}
或者在回調(diào)中使用箭頭函數(shù):
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// 此語法確保 `handleClick` 內(nèi)的 `this` 已被綁定。
return (
<button onClick={(e) => this.handleClick(e)}> Click me
</button>
);
}}
向事件處理程序傳遞參數(shù)
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
上述兩種方式是等價的,分別通過箭頭函數(shù)和 Function.prototype.bind 來實現(xiàn)。