學和使用react有一年多了,最近想在梳理一下react基礎知識,夯實基礎,激流勇進~ 關於reacr-router,redux,redux-saga後續都會慢慢輸出,但願各位看官老爺持續關注~~要是能給個贊鼓勵一下就更贊了~react
提醒一下: 看完以後抓緊時間趁熱打鐵,redux,react-redux,redux-sagaredux
一篇文章總結redux、react-redux、redux-saga數組
一個JSX語法的示例,以下所示瀏覽器
const element = <h1>Hello, world!</h1>;
複製代碼
這種語法形式,既不是HTML,也不是字符串,而是稱之爲JSX,是React裏用來描述UI和樣式的語法,JSX最終會被編譯爲合法的JS語句調用(編譯器在遇到{
時採用JS語法進行解析,遇到<
就採用HTML規則進行解析)安全
JSX中,可使用花括號{}
嵌入任意的JavaScript合法表達式,如:2 + 2
、user.firstName
、formatName(user)
都是合法的。示例如:bash
const user = {
firstName: 'Zhang',
lastName : 'Busong'
};
const elem = (
<h1>Hello, {formatName(user)}</h1>
);
/*
這裏的(),其實是可選的,可是React推薦加入(),這樣子就會被視爲一個表達式,而不會致使
自動插入分號的問題
*/
ReactDOM.render(
element,
document.getElementById('app')
)
複製代碼
JSX自己也是一種表達式,因此它能夠像其餘表達式同樣,用於給一個變量賦值、做爲函數實參、做爲函數返回值,等等。如:babel
function getGreeting(user) {
if (user) {
return <h1>Hello, {formatName(user)}</h1>
}
return <h1>Hello, Guest!</h1>;
}
複製代碼
注意: 一、在JSX中,聲明屬性時不要使用引號,若是聲明屬性的時候使用引號,那麼將被做爲字符串解析,而不會被做爲一個表達式解析,如:app
<div firstName="{user.firstName}" lastName={user.lastName}></div>
複製代碼
解析後,能夠獲得:異步
<div firstName="{user.firstName}" lastName="Lau"></div>
複製代碼
所以,當咱們須要使用一個字符串字面量的時候,可使用引號,可是若是要做爲表達式解析的時候,則不該當使用引號 二、在JSX中,有些屬性名稱須要進行特殊處理。如class
應該用className
代替,tabindex
則用tabIndex
代替。這是由於JSX本質上更接近於JavaScript,而class
是JavaScript中的保留字。同時,應該使用camelCase
來命名一個屬性,而不是使用HTML的屬性命名方式 三、JSX自己已經作了防注入處理,對於那些不是明確編寫的HTML代碼,是不會被解析爲HTML DOM的,ReactDOM會將他們一概視爲字符串,在渲染完成前就轉化爲字符串,因此能夠防止XSS攻擊 四、若是JSX標籤是閉合的,那麼結尾須要用/>
,另外,JSX標籤是能夠互相嵌套的,這和HTML裏是同樣的編輯器
JSX經過babel編譯,而babel實際上把JSX編譯給React.createElement()
調用。以下JSX代碼:
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
複製代碼
是等同於如下的語句的:
const elem = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
複製代碼
React.createElement()
方法會首先進行一些避免BUG的檢查,而後返回相似如下例子的對象:
const element = {
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world'
}
}
複製代碼
這樣的對象,則稱爲React元素
,表明全部呈如今屏幕上的東西。React正是經過讀取這些對象來構建DOM,而且保持數據和UI同步的
元素(elements
)是構成React應用的最小單元,元素描述了想要在屏幕中看到的內容,如:
const element = <h1>Hello, world</h1>;
複製代碼
和DOM元素不一樣的是,React元素是純對象,建立的代價低。而且React會進行優化處理,只把有必要的變化更新到DOM上。此外,元素和組件的概念,是不同的,組件是由元素組成的。
在React中,使用ReactDOM.render()
方法來將React元素渲染進一個DOM中。如:
ReactDOM.render(
element,
document.getElementById('root')
)
複製代碼
React元素是不可變的,因此一旦一個元素建立完成後,咱們是沒法改變其內容或者屬性的。一個元素就像是動畫裏的一幀,它表明UI在某一時間點的樣子。若是非要使用元素來構成可變化的UI界面,就須要使用setInterval
了,如:
function tick() {
const element = (
<div>Now is {new Date().toLocaleTimeString()}</div>
);
ReactDOM.render(
element,
document.getElementById('root')
);
}
setInterval(tick, 1000);
複製代碼
在實際開發中,大多數React應用只會調用一次ReactDOM.render()
,因此更好的方式是使用有狀態組件
組件(component
)可以將UI劃分爲獨立的、可複用的部分,這樣咱們就只需專一於構建每個單獨的部件。 從概念上看,組件就像是函數:接受任意的輸入(稱爲屬性,Props
),返回React元素。React中有兩種定義組件的方式:函數定義
和類定義
這種方式是最簡單的定義組件的方式,就像寫一個JS函數同樣,如:
function Welcome (props) {
return <h1>Hello, {props.name}</h1>;;
}
複製代碼
還可使用ES6裏的類來定義一個組件,以下所示:
class Welcome extends React.Component {
render () {
return <h1>Hello, {this.props.name}<h1>;
}
}
複製代碼
這種方式比起函數定義
方式則更加靈活
先前,咱們遇到的React元素只是呈現一個DOM標籤,如:
const element = <div />
複製代碼
然而,React元素也能夠是用戶自定義的組件
,如:
const element = <Welcome name="Tom" />
複製代碼
Welcome組件中聲明瞭一個屬性name="Tom"
,而這個屬性,將以props.name
的方式傳遞給組件,以下方式:
function Welcome (props) {
return <h1>Hello, {props.name}</h1>;
}
複製代碼
此時,對於如下的代碼:
ReactDOM.render(
<Welcome name="張不慫" />,
document.getElementById('root')
)
複製代碼
最終就會以<h1>Hello, 張不慫</h1>
的方式呈現。在這個過程當中,發生了以下的事情:
<Welcome name="張不慫" />
元素調用了ReactDOM.render()
豐富{ name: '張不慫' }
做爲props實參來調用Welcome組件<h1>Hello, 張不慫</h1>
元素組件是能夠組合的。即組件內部能夠引用其餘組件,如:
function Welcome (props) {
return <h1>Hello, {props.name}</h1>;
}
function App () {
return (
<div>
<Welcome name="Tom" />
<Welcome name="Jack" />
<Welcome name="Mike" />
</div>
)
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
複製代碼
注意: 在React中,組件必須返回單一
的根元素,這也是爲何App組件中須要用<div>
標籤包裹的緣由。如如下的方式,是錯誤的(由於它有3個根元素):
function App () {
return (
<Welcome name="Tom" />
<Welcome name="Jack" />
<Welcome name="Mike" />
)
}
複製代碼
考慮如下這種狀況:
function sum (a, b) {
return a + b;
}
複製代碼
這種函數稱爲純函數
:它不改變本身的輸入值,且老是對相同的輸入返回相同的結果。 與之對立的,則是非純函數
,如:
function withdraw (account, amount) {
account.total -= amount;
}
複製代碼
非純函數
在函數內改變了輸入的參數。在React中,不管是經過function
仍是class
聲明組件,咱們都不該該修改它自身的屬性(props
)。雖然React至關靈活,可是它也有一個嚴格的規定:全部的React組件都必須像純函數那樣來使用它們的props
使用類定義組件
有一些額外的好處,如擁有本地狀態
這一特性。 如下是一個類定義組件
class Clock extends React.Component {
render () {
return (
<div>
<h1>Hello, world!</h1>
<h2>Now is {this.props.date.toLocaleTimeString()}</h2>
</div>
);
}
}
複製代碼
須要注意的有:
類名
即爲組件名
(不管是函數定義組件仍是類定義組件,組件名稱的首字母都必須大寫,而且繼承自React.Component
)render()
方法,用來返回須要呈現的內容state是屬於一個組件自身的。咱們能夠在類的構造函數constructor
中來初始化狀態,如:
constructor (props) {
super(props)
this.state = {
date: new Date()
}
}
複製代碼
如此一來,咱們就能夠在render()
函數中使用this.state.xxx
來引用一個狀態
在應用裏,每每都會有許許多多的組件。在組件銷燬後,回收和釋放它們所佔據的資源很是重要。 在時鐘應用
的例子裏,咱們須要在第一次渲染到DOM的時候設置一個定時器,而且須要在相應的DOM銷燬後,清除這個定時器。那麼,這種狀況下,React爲咱們提供了生命週期的鉤子函數,方便咱們進行使用。在React中,生命週期分爲: 1)Mount
已插入真實DOM 2)Update
正在從新渲染 3)Unmount
已移出真實DOM 而相應的,生命週期鉤子函數有:
componentWillMount
componentDidMount
componentWillUpdate(newProps, nextState)
componentDidUpdate(prevProps, prevState)
componentWillUnmount()
此外,還有兩種特殊狀態的處理函數:
componentWillReceiveProps(nextProps)
已加載的組件收到新的參數時調動shouldComponentUpdate(nextProps, nextState)
組件判斷是否從新渲染時調用所以,基於生命週期鉤子函數,咱們能夠實現一個時鐘應用以下:
class Clock extends React.Component {
constructor (props) {
super(props);
this.state = {
date: new Date()
}
}
tick () {
this.setState({
date: new Date()
});
}
componentDidMount () {
this.timerId = setInterval(() => {
this.tick()
}, 1000);
}
componentWillUnmount () {
clearInterval(this.timerId);
}
render () {
return (
<div>Now is {this.state.date.toLocaleTimeString()}</div>
);
}
}
複製代碼
須要注意的是: 1)render()
裏用不到的state
,不該該聲明在state
裏 2)不能直接使用this.state.xxx = xxx
的方式來改變一個state
的值,應該使用this.setState()
。如:
setName () {
this.setState({
name: '張不慫'
})
}
複製代碼
this.setState()
會自動覆蓋this.state
裏相應的屬性,並觸發render()
從新渲染。 3)狀態更新多是異步的 React能夠將多個setState()
調用合併成一個調用來提高性能。且因爲this.props
和this.state
多是異步更新的,因此不該該依靠它們的值來計算下一個狀態。這種狀況下,能夠給setState
傳入一個函數,如:
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
複製代碼
React元素的事件與DOM元素相似,不過也有一些區別,如: 1)React事件使用camelCase
命名(onClick
),而不是全小寫的形式(onclick
) 2)使用JSX,傳入的是事件的句柄,而不是一個字符串 如如下的HTML:
<button onclick="increment()">ADD</button>
複製代碼
使用React的方式描述如:
<button onClick={increment}>ADD</button>
複製代碼
還有一個不一樣在於,在原生DOM中,咱們能夠經過返回false
來阻止默認行爲,可是這在React中是行不通的,在React中須要明確使用preventDefault()
來阻止默認行爲。如:
function ActionLink () {
function handleClick (e) {
e.preventDefault();
alert('Hello, world!');
}
return (
<a href="#" onClick={handleClick}>Click Me</a>
);
}
複製代碼
這裏,事件回調函數裏的event
是通過React特殊處理過的(遵循W3C標準),因此咱們能夠放心地使用它,而不用擔憂跨瀏覽器的兼容性問題。 注意: 在使用事件回調函數的時候,咱們須要特別注意this
的指向問題,由於在React裏,除了構造函數和生命週期鉤子函數裏會自動綁定this爲當前組件外,其餘的都不會自動綁定this
的指向爲當前組件,所以須要咱們本身注意好this的綁定問題, 一般而言,在一個類方式聲明的組件裏使用事件回調,咱們需要在組件的constructor
裏綁定回調方法的this指向,如:
class Counter extends React.Component {
constructor (props) {
super(props);
this.state = {
counter: 0
}
// 在這裏綁定指向
this.increment = this.increment.bind(this);
}
increment () {
this.setState({
counter: this.state.counter + 1
});
}
render () {
return (
<div>
The counter now is: {this.state.counter}
<button onClick={this.increment}>+1</button>
</div>
);
}
}
複製代碼
固然,咱們還有另一種方法來使用箭頭函數綁定指向,就是使用實驗性
的屬性初始化語法,如:
class Counter extends React.Component {
increment: () => {
this.setState({
counter: this.state.counter + 1
});
}
// ...
}
複製代碼
3)像事件處理程序傳遞參數 咱們能夠爲事件處理程序傳遞額外的參數,方式有如下兩種:
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
複製代碼
須要注意的是,使用箭頭函數的狀況下,參數e
要顯式傳遞,而使用bind的狀況下,則無需顯式傳遞(參數e
會做爲最後一個參數傳遞給事件處理程序)
在React裏,咱們能夠建立不一樣的組件來封裝咱們須要的功能。咱們也能夠根據組件的狀態,只渲染組件中的一部份內容,而條件渲染就是爲此而準備的。在React中,咱們能夠像在JavaScript中寫條件語句同樣地寫條件渲染語句,如:
function Greet(props) {
const isLogined = props.isLogined;
if (isLogined) {
return <div>Hello !</div>;
}
return <div>Please sign in</div>;
}
ReactDOM.render(
<Greet isLogined={true} />,
document.getElementById('root')
);
複製代碼
這將渲染出:
<div>Hello !</div>
複製代碼
咱們也可使用變量來存儲元素,如:
function LogBtn(props) {
var button;
const isLogined = props.isLogined;
if (isLogined) {
button = <button>退出</button>
} else {
button = <button>登錄</button>
}
return <div>You can {button}</div>;
}
ReactDOM.render(
<LogBtn isLogined={false} />,
document.getElementById('root')
);
複製代碼
因爲JavaScript語法對待&&
運算符的性質,咱們也可使用&&運算符來完成條件渲染,如:
function LogBtn(props) {
var button;
const isLogined = props.isLogined;
return (
<div>Hello
{!isLogined && (
<button>請登錄</button>
)}
</div>
)
}
複製代碼
當props.isLogined
爲false的時候,就會渲染出:
<div>Hello <button>請登陸</button></div>
複製代碼
咱們可能已經發現了,其實JSX能夠像一個表達式那樣子靈活使用,因此,咱們天然也可使用三目運算符進行渲染,如:
function LogBtn (props) {
const isLogined = props.isLogined;
return (
<div>You can
<button>{isLogined ? '退出' : '登錄'}</button>
</div>
)
}
複製代碼
有時候,咱們但願是整個組件都不渲染,而不只僅是局部不渲染,那麼這種狀況下,咱們就能夠在render()
函數裏返回一個null
,來實現咱們想要的效果,如:
function LogBtn (props) {
const isLogined = props.isLogined;
const isShow = props.isShow;
if (isShow) {
return (
<div>You can
<button>{isLogined ? '退出' : '登錄'}</button>
</div>
)
}
return null;
}
複製代碼
注意: 組件裏返回null
不會影響組件生命週期的觸發,如componentWillUpdate
和componentDidUpdate
仍然會被調用
在JavaScript中,咱們可使用map()
函數來對一個數組列表進行操做,如:
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(number => number*2);
console.log(doubled); // 獲得[2, 4, 6, 8, 10]
複製代碼
一樣的,在React裏,咱們也可使用map()
來進行列表渲染,如:
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map(number => {
return (
<li>{number}</li>
)
});
ReactDOM.render(
<ul>{listItems}</ul>,
document.getElementById('root')
)
複製代碼
這將獲得:
<ul><li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
複製代碼
固然,咱們還能夠進行更好的封裝,如:
function NumberList (props) {
const numbers = props.numbers;
const listItems = numbers.map(number => {
return (
<li>{number}</li>
)
});
return <ul>{listItems}</ul>
}
複製代碼
當咱們運行以上的代碼的時候,會發現控制檯提示:Each child in an array or iterator should have a unique "key" prop
,所以,咱們須要爲列表項的每個項分配一個key
,來解決這個問題,一般而言,咱們可使用如下幾種方式來提供key
:
<li key={item.itemId}>
index
),如:const listItems = numbers.map((number, index) => {
<li key={index}>{number}</li>
});
複製代碼
可是React不推薦在須要從新排序的列表裏使用索引下標,由於會致使變得很慢。
注意: 只有在一個項的同胞裏區分彼此的時候,才須要使用到key,key不須要全局惟一,只須要在一個數組內部區分彼此時惟一即可。key的做用是給React一個提示,而不會傳遞給組件。若是咱們在組件內須要一樣的一個值,能夠換個名字傳遞,如:
const content = posts.map(post => (
<Post key={post.id} id={post.id} title={post.title} />
));
複製代碼
表單和其餘的React中的DOM元素有所不一樣,由於表單元素生來就是爲了保存一些內部狀態。在React中,表單和HTML中的表單略有不一樣
HTML中,<input>
、<textarea>
、<select>
這類表單元素會維持自身狀態,並根據用戶輸入進行更新。不過React中,可變的狀態一般保存在組件的this.state
中,且只能用setState()
方法進行更新,如:
class NameForm extends React.Component {
constructor (props) {
super(props);
this.state = {
value: ''
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange (event) {
this.setState({
value: event.target.value
});
}
handleSubmit (event) {
alert('Your name is '+this.state.value);
event.preventDefault();
}
render () {
return (
<form onSubmit={this.handleSubmit}>
Name: <input type="text" value={this.state.value} onChange={this.handleChange} />
<input type="submit" value="Submit" />
</form>
)
}
}
複製代碼
和HTML中不一樣的是,React中的textarea
並不須要寫成<textarea></textarea>
的形式,而是寫成<textarea value="" ... />
的形式即可。而對於HTML中的select
標籤,一般作法是:
<select>
<option value="A">A</option>
<option value="B" selected>B</option>
<option value="C">C</option>
</select>
複製代碼
可是React中,不須要在須要選中的option
處加入selected
,而只須要傳入一個value,就會自動根據value來選中相應的選項,如:
<select value="C">
<option value="A">A</option>
<option value="B">B</option>
<option value="C">C</option>
</select>
複製代碼
那麼如上述例子,C所在的這個option
就會被選中
一般一個表單都有多個輸入,若是咱們爲每個輸入添加處理事件,那麼將會很是繁瑣。好的一個解決辦法是,使用name,而後根據event.target.name
來選擇作什麼。如:
class Form extends React.Component {
constructor (props) {
super(props);
this.state = {
name: '',
gender: '男',
attend: false,
profile: ''
};
this.handleInputChange = this.handleInputChange.bind(this);
this.handleSubmit = this.handleSubmit.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
});
}
handleSubmit (event) {
this.setState({
profile: `姓名:${this.state.name},${this.state.gender},${this.state.attend ? '參加' : '不參加'}活動`
});
event.preventDefault();
}
render () {
return (
<form>
<p>姓名:<input name="name" value={this.state.name} onChange={this.handleInputChange} /></p>
<p>性別:
<select name="gender" value={this.state.gender} onChange={this.handleInputChange}>
<option value="男">男</option>
<option value="女">女</option>
</select>
</p>
<p>是否參加:<input name="attend" type="checkbox" onChange={this.handleInputChange} checked={this.state.attend} /></p>
<input type="submit" value="Submit" onClick={this.handleSubmit} />
<p>您的報名信息:{this.state.profile}</p>
</form>
)
}
}
複製代碼
大多數狀況下,使用受控組件
實現表單是首選,在受控組件中,表單數據是交由React組件處理的。若是想要讓表單數據由DOM處理(即數據不保存在React的狀態裏,而是保存在DOM中),那麼可使用非受控組件
,使用非受控組件
,能夠無需爲每一個狀態更新編寫事件處理程序,使用ref
便可實現,如:
class NameForm extends React.Component {
constrcutor(props) {
super(props)
}
handleSubmit(event) {
console.log('A name was submitted: ', this.input.value)
event.preventDefault()
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name: <input type="text" ref={input => this.input = input} />
</label>
<input type="submit" value="submit" />
</form>
)
}
}
複製代碼
對於非受控組件
,若是要指定默認值,那麼可使用defaultValue
,如:
<input type="text" defaultValue="Hello" ref={input => this.input = input} />
複製代碼
相應的,type="checkbox"
和type="radio"
,則使用defaultChecked
當須要幾個組件共用狀態數據的時候,可使用狀態提高技術。核心思想在於:把數據抽離到最近的共同父組件,父組件管理狀態(state),而後經過屬性(props)傳遞給子組件。如實現一個貨幣轉換的組件,能夠以下:
function USD2RMB (amount) {
return amount * 6.7925;
}
function RMB2USD (amount) {
return amount * 0.1472;
}
function convert (amount, typeFn) {
return typeFn(amount);
}
複製代碼
咱們但願在RMB的輸入表單上上輸入的時候,USD的輸入表單上的數值也同步更新,這種狀況下,若是RMB組件本身管理本身的狀態,是很難以實現的,所以,咱們須要讓這個狀態提高自父組件進行管理。以下:
class CurrencyInput extends React.Component {
constructor (props) {
super(props)
this.handleChange = this.handleChange.bind(this)
}
handleChange (event) {
this.props.onInputChange(event.target.value)
}
render () {
const value = this.props.value
const type = this.props.type
return (
<p>{type}: <input type="text" value={value} onChange={this.handleChange} /></p>
);
}
}
複製代碼
最後定義一個共同的父組件,以下:
class CurrencyConvert extends Component {
constructor (props) {
super(props);
this.state = {
type: 'RMB',
amount: 0
}
this.handleRMBChange = this.handleRMBChange.bind(this);
this.handleUSDChange = this.handleUSDChange.bind(this);
}
handleRMBChange (amount) {
this.setState({
type: 'RMB',
amount
});
}
handleUSDChange (amount) {
this.setState({
type: 'USD',
amount
});
}
render () {
const type = this.state.type;
const amount = this.state.amount;
const RMB = type==='RMB' ? amount : convert(amount, USB2RMB);
const USD = type==='USD' ? amount : convert(amount, RMB2USB);
return (
<div>
<p>Please Input:</p>
<CurrencyInput type="RMB" value={RMB} onInputChange={this.handleRMBChange} />
<CurrencyInput type="USD" value={USD} onInputChange={this.handleUSDChange} />
</div>
);
}
}
複製代碼
React推崇更多的是使用組合,而非使用繼承。對於一些使用場景,React給出的建議以下:
當父組件不知道子組件可能的內容是什麼的時候,可使用props.children
,如:
function Article (props) {
return (
<section>
<aside>側邊欄</aside>
<article>{props.children}</article>
</section>
);
}
function App () {
return (
<Article>這是一篇文章</Article>
);
}
複製代碼
這將渲染獲得:
<section>
<aside>側邊欄</aside>
<article>這是一篇文章</article>
</section>
複製代碼
咱們還能夠自定義名稱,由於JSX實際上會被轉化爲合法的JS表達式,因此,還能夠有:
function Article (props) {
return (
<section>
<aside>{props.aside}</aside>
<article>{props.children}</article>
</section>
);
}
function App () {
return (
<Article aside={
<h1>這是一個側欄</h1>
}>這是一篇文章</Article>
);
}
複製代碼
這將渲染獲得:
<section>
<aside><h1>這是一個側欄</h1></aside>
<article>這是一篇文章</article>
</section>
複製代碼
在Facebook的網站上,使用了數以千計的組件,可是實踐證實尚未發現須要使用繼承才能解決的狀況。 屬性和組合爲咱們提供了清晰的、安全的方式來自定義組件的樣式和行爲,組件能夠接受任意元素,包括:基本數據類型、React元素、函數。 若是要在組件之間複用UI無關的功能,那麼應該將其提取到單獨的JavaScript模塊中,這樣子能夠在不對組件進行擴展的前提下導入並使用函數、對象、類
以爲對你有幫助,不妨點個
不妨再點個關注,不迷路,下一篇關於redux的明天就發!!~