爲了幫助你們快速上手React Native開發,在這本節中將向你們介紹開發React Native所須要的一些React必備基礎知識。javascript
本節課將從React的特色、如何使用React、JSX語法,而後會對組件(Component)以及組件的屬性(props)、狀態(state)、生命週期等方面進行講解。html
React 是 Facebook 推出的開源 JavaScript Library,它是一個用於組建用戶界面的JavaScript庫,讓你以更簡單的方式來建立交互式用戶界面,它的出現讓許多革新性的 Web 觀念開始流行起來,例如:Virtual DOM、Component,聲明式渲染等。java
聲明式與命令式node
命令式編程:命令「機器」如何去作事情(how),這樣無論你想要的是什麼(what),它都會按照你的命令實現。
聲明式編程:告訴「機器」你想要的是什麼(what),讓機器想出如何去作(how)。
複製代碼
演示react
構建一個新的 React 單頁應用,能夠經過Create React App來完成。它能夠幫助你配置開發環境,以便你可使用最新的 JavaScript 特性,還能提供一個友好的開發體驗,併爲生產環境優化你的應用。git
npm install -g create-react-app
create-react-app my-app
cd my-app
npm start
複製代碼
"dependencies": {
"react": "^16.6.3",//是 React 的核心庫
"react-dom": "^16.6.3",//提供與 DOM 相關的功能
"react-scripts": "2.1.1"//create-react-app 的一個核心包,一些腳本和工具的默認配置都集成在裏面
},
複製代碼
ReactDOM.render(element, container[, callback])github
渲染一個 React 元素到由 container 提供的 DOM 中,而且返回組件的一個 引用(reference) (或者對於 無狀態組件 返回 null )。算法
JSX 是一個看起來很像 XML 的 JavaScript 語法擴展。 每個XML標籤都會被JSX轉換工具轉換成純JavaScript代碼,使用JSX,組件的結構和組件之間的關係看上去更加清晰。 JSX並非React必須使用的,但React官方建議咱們使用 JSX , 由於它能定義簡潔且咱們熟知的包含屬性的樹狀結構語法。npm
Usage:編程
React.render(//使用JSX
<div>
<div>
<div>content</div>
</div>
</div>,
document.getElementById('example')
);
React.render(//不使用JSX
React.createElement('div', null,
React.createElement('div', null,
React.createElement('div', null, 'content')
)
),
document.getElementById('example')
);
複製代碼
React.createElement(
type,
[props],
[...children]
)
複製代碼
根據給定的類型建立並返回新的 React element 。參數type既能夠是一個html標籤名稱字符串(例如'div' 或 'span' ),也能夠是一個 React component 類型(一個類或一個函數)。
React.createElement(Hello, {toWhat: 'World'}, 'hello'),
//等價於 <Hello toWhat="World">hello</Hello>,
複製代碼
React 能夠渲染 HTML 標籤 (strings) 或 React 組件 (classes)。 要渲染 HTML 標籤,只需在 JSX 裏使用小寫字母開頭的標籤名。
var myDivElement = <div className="foo" />;
React.render(myDivElement, document.root);
複製代碼
要渲染 React 組件,只需建立一個大寫字母開頭的本地變量。
var MyComponent = ...;
var myElement = <MyComponent someProperty={true} />;
React.render(myElement, document.body);
複製代碼
提示:
- React 的 JSX 里約定分別使用首字母大、小寫來區分本地組件的類和 HTML 標籤。
- 因爲 JSX 就是 JavaScript,一些標識符像 class 和 for 不建議做爲 XML 屬性名。做爲替代, React DOM 使用 className 和 htmlFor 來作對應的屬性。
要使用 JavaScript 表達式做爲屬性值,只需把這個表達式用一對大括號 {}
包起來,不要用引號 ""
。
// 輸入 (JSX):
var person = <Person name={window.isLoggedIn ? window.name : ''} />;
// 輸出 (JS):
var person = React.createElement(
Person,
{name: window.isLoggedIn ? window.name : ''}
);
複製代碼
一樣地,JavaScript 表達式可用於描述子結點:
// 輸入 (JSX):
var content = <Container>{window.isLoggedIn ? <Nav /> : <Login />}</Container>;
// 輸出 (JS):
var content = React.createElement(
Container,
null,
window.isLoggedIn ? React.createElement(Nav) : React.createElement(Login)
);
複製代碼
JSX 裏添加註釋很容易,它們只是 JS 表達式而已。你只須要在一個標籤的子節點內(非最外層)用 {} 包圍要註釋的部分。
class ReactDemo extends Component {
render() {
return (
<View style={styles.container}>
{/*標籤子節點的註釋*/}
<Text style={styles.welcome} //textAlign='right' textShadowColor='yellow' /*color='red' textShadowRadius='1'*/ >
React Native!
</Text>
</View>
);
}
}
複製代碼
心得:在標籤節點之外註釋,和一般的註釋是同樣的,多行用「/**/」 單行用「//」;
不推薦作法:
var component = <Component />;
component.props.foo = x; // 不推薦
component.props.bar = y; // 不推薦
複製代碼
這樣修改組件的屬性,會致使React不會對組件的屬性類型(propTypes)進行的檢查。從而引起一些預料以外的問題。
推薦作法:
var component = <Component foo={x} bar={y} />;
複製代碼
你可使用 JSX 的新特性 - 延展屬性:
var props = {};
props.foo = x;
props.bar = y;
var component = <Component {...props} />;
複製代碼
傳入對象的屬性會被複制到組件內。
它能被屢次使用,也能夠和其它屬性一塊兒用。注意順序很重要,後面的會覆蓋掉前面的。
var props = { foo: 'default' };
var component = <Component {...props} foo={'override'} />;
console.log(component.props.foo); // 'override'
複製代碼
上文出現的...
標記被叫作延展操做符(spread operator)已經被 ES6 數組 支持。
React 容許將代碼封裝成組件(component),而後像插入普通 HTML 標籤同樣,在網頁中插入這個組件。
class Hello extends React.Component{
render() {
return <h1>Hello {this.props.name}</h1>;
}
}
ReactDOM.render(
<Hello name="John" />,
document.getElementById('example')
);
複製代碼
上面代碼中,變量 HelloMessage 就是一個組件類。模板插入 <HelloMessage />
時,會自動生成 HelloMessage 的一個實例。全部組件類都必須有本身的 render 方法,用於輸出組件。
注意
咱們能夠經過this.props.xx
的形式獲取組件對象的屬性,對象的屬性能夠任意定義,但要避免與JavaScript關鍵字衝突。
this.props.children
會返回組件對象的全部屬性。 React 提供一個工具方法 React.Children 來處理 this.props.children 。咱們能夠用 React.Children.map
或React.Children.forEach
來遍歷子節點。
React.Children.map
React.Children.map(children, function[(thisArg)])
複製代碼
在包含在 children 裏的每一個子級上調用函數,調用的函數的 this 設置爲 thisArg 。若是 children 是一個嵌套的對象或數組,它將被遍歷。若是 children 是 null 或 undefined ,返回 null 或 undefined 而不是一個空數組。
React.Children.forEach
React.Children.forEach(children, function[(thisArg)])
複製代碼
Usage:
class NotesList extends React.Component{
render(){
return (
<ol> { React.Children.map(this.props.children,(child)=> { return <h1>{child}</h1>; }) } </ol>
);
}
}
ReactDOM.render(<NotesList> <span>hello</span> <span>world</span> </NotesList>, document.getElementById('root'));
複製代碼
組件的屬性能夠接受任意值,字符串、對象、函數等等均可以。有時,咱們須要一種機制,驗證別人使用組件時,提供的參數是否符合要求。 組件類的PropTypes屬性,就是用來驗證組件實例的屬性是否符合要求。
React.PropTypes 從 React v15.5開始被移入了
prop-types
,使用時須要留意;
import PropTypes from 'prop-types'
class MyTitle extends React.Component{
static propTypes={
title: PropTypes.string.isRequired,
};
render() {
return <h1> title:{this.props.title} </h1>;
}
}
ReactDOM.render(<MyTitle />, document.getElementById('root'));
複製代碼
上面的Mytitle組件有一個title屬性。PropTypes 告訴 React,這個 title 屬性是必須的,並且它的值必須是字符串。如今,咱們設置 title 屬性的值是一個數值。
var data = 123;
ReactDOM.render(
<MyTitle title={data} />,
document.body
);
複製代碼
這樣一來,title屬性就通不過驗證了。控制檯會顯示一行錯誤信息。
Warning: Failed propType: Invalid prop `title` of type `number` supplied to `MyTitle`, expected `string`.
更多的PropTypes設置,能夠查看官方文檔。
此外,能夠經過defaultProps用來設置組件屬性的默認值。
class MyTitle extends React.Component{
static defaultProps={
shortName:'MyTitle'
};
render() {
return <h1> {this.props.shortName}</h1>;
}
}
ReactDOM.render(<MyTitle/>, document.getElementById('root'));
複製代碼
上面代碼會輸出"MyTitle"
。
組件並非真實的 DOM 節點,而是存在於內存之中的一種數據結構,叫作虛擬 DOM (virtual DOM)。只有當它插入文檔之後,纔會變成真實的 DOM 。根據 React 的設計,全部的 DOM 變更,都先在虛擬 DOM 上發生,而後再將實際發生變更的部分,反映在真實 DOM上,這種算法叫作 DOM diff ,它能夠極大提升網頁的性能表現。
可是,有時須要從組件獲取真實 DOM 的節點,這時就要用到 ref 屬性。
class Alert extends React.Component {
showAlert(message) {
alert(`Debug:${message}`);
}
render() {
return null;
}
}
class MyTitle extends React.Component {
onClick = () => {
this.refs.alert.showAlert('MyTitle');
};
render() {
return <div>
<h1 onClick={this.onClick}>Click me</h1>
<Alert ref='alert'/>
</div>;
}
}
ReactDOM.render(<MyTitle/>, document.getElementById('root'));
複製代碼
上面代碼中,組件 MyTitle 的子節點有一個Alert組件,爲了調用這個組件提供的方法,這時就必須獲取真實的 DOM 節點,虛擬 DOM 是拿不到用戶輸入的。爲了作到這一點,咱們在使用這個組件的時候必須爲其設置一個ref屬性,而後 this.refs.[refName] 就會返回這個真實的 DOM 節點。
須要注意的是,因爲 this.refs.[refName] 屬性獲取的是真實 DOM ,因此必須等到虛擬 DOM 插入文檔之後,才能使用這個屬性,不然會報錯。上面代碼中,經過爲組件指定 Click 事件的回調函數,確保了只有等到真實 DOM 發生 Click 事件以後,纔會讀取 this.refs.[refName] 屬性。
React 組件支持不少事件,除了 Click 事件之外,還有 KeyDown 、Copy、Scroll 等,完整的事件清單請查看官方文檔。
心得:ref屬性在開發中使用頻率很高,使用它你能夠獲取到任何你想要獲取的組件的對象,有了這個對象你就能夠靈活地作不少事情,好比:讀寫對象的變量,甚至調用對象的函數。
上文講到了props,組件會根據props的變化來進行渲染,但組件沒法改變自身的props,那麼組件爲了實現交互,可使用組件的 state 。state 是組件私有的,能夠經過state={}方式初始化,經過調用 this.setState()
來改變它。當 state 更新以後,組件就會從新渲染本身。
render() 方法依賴於 this.props 和 this.state ,框架會確保渲染出來的 UI 界面老是與輸入( this.props 和 this.state )保持一致。
能夠經過一下兩種方式來初始化state,在組件的生命週期中僅執行一次,用於設置組件的初始化 state 。
constructor(props){
super(props);
this.state={
name:''
}
}
//or
state={
name:''
}
複製代碼
經過this.setState()
方法來更新state,調用該方法後,React會從新渲染相關的UI。 this.setState({favorite:!this.state.favorite});
Usage:
class FavoriteButton extends React.Component{
state={
favorite:false
};
handleClick=()=>{
this.setState({favorite:!this.state.favorite});
};
render(){
const text=this.state.favorite? 'favorite':'un favorite';
return (
<h1 onClick={this.handleClick}> You {text} this. Click to toggle. </h1>
);
}
}
複製代碼
上面代碼是一個 FavoriteButton 組件,它的經過 state={}初始狀態,也就是一個對象,這個對象能夠經過 this.state 屬性讀取。當用戶點擊組件,致使狀態變化,this.setState 方法就修改狀態值,每次修改之後,自動調用 this.render 方法,再次渲染組件。
心得:因爲 this.props 和 this.state 都用於描述組件的特性,可能會產生混淆。一個簡單的區分方法是,this.props 表示那些本組件沒法改變的特性,而 this.state 是會隨着用戶互動而產生變化的特性。
在iOS中UIViewController
提供了(void)viewWillAppear:(BOOL)animated
, - (void)viewDidLoad
,(void)viewWillDisappear:(BOOL)animated
等生命週期方法,在Android中Activity
則提供了onCreate()
,onStart()
,onResume()
,onPause()
,onStop()
,onDestroy()
等生命週期方法,這些生命週期方法描述了一個界面從建立到銷燬的一輩子。
那麼在React 中組件(Component)也是有本身的生命週期方法的。
使用這些生命週期方法一般會致使錯誤和不一致,所以未來會被棄用。在新的React版本中他們被標記爲UNSAFE。
constructor(props)
複製代碼
React組件的構造函數將會在裝配以前被調用。當爲一個React.Component子類定義構造函數時,你應該在任何其餘的表達式以前調用super(props)。不然,this.props在構造函數中將是未定義,並可能引起異常。
構造函數是初始化狀態的合適位置。若你不初始化狀態且不綁定方法,那你也不須要爲你的React組件定義一個構造函數。
static getDerivedStateFromProps(nextProps, prevState)
複製代碼
組件實例化後和接受新屬性時將會調用getDerivedStateFromProps。它應該返回一個對象來更新狀態,或者返回null來代表新屬性不須要更新任何狀態。
注意,若是父組件致使了組件的從新渲染,即便屬性沒有更新,這一方法也會被調用。若是你只想處理變化,那麼能夠經過比較新舊值來完成。
調用this.setState() 一般不會觸發 getDerivedStateFromProps()。
ReactComponent render()
複製代碼
render()
方法是必須的。
當被調用時,其會檢查this.props 和 this.state並返回如下類型中的一個:
返回null 或 false時,ReactDOM.findDOMNode(this) 將返回 null。
render()
函數應該是純粹的,也就是說該函數不修改組件的state
,每次調用都返回相同的結果,不讀寫 DOM 信息,也不和瀏覽器交互(例如經過使用setTimeout
)。若是須要和瀏覽器交互,在componentDidMount()
中或者其它生命週期方法中作這件事。保持render()
純粹,可使服務器端渲染更加切實可行,也使組件更容易被理解。
提示:若 shouldComponentUpdate()返回false,render()函數將不會被調用。
componentDidMount()
複製代碼
componentDidMount()在組件被裝配後當即調用,一般在該方法中進行一些初始化操做。·初始化時須要DOM節點的操做能夠放到這裏進行`。若你須要從遠端加載數據,這是一個適合實現網絡請求的地方。在該方法裏設置狀態將會觸發重渲。
這一方法是一個發起任何訂閱的好地方。若是你這麼作了,別忘了在componentWillUnmount()退訂。
另外,在這個方法中調用setState()將會觸發一次額外的渲染,可是它將在瀏覽器刷新屏幕以前發生。這保證了即便render()將會調用兩次,但用戶不會看到中間狀態。
shouldComponentUpdate(nextProps, nextState)
複製代碼
在接收到新的 props 或者 state,將要渲染以前調用,以讓React知道當前狀態或屬性的改變是否不影響組件的輸出。
該方法在初始化渲染的時候不會調用,在使用 forceUpdate 方法的時候也不會。若是肯定新的 props 和 state 不須要從新渲染,則此處應該 返回 false。
心得:重寫次方你能夠根據實際狀況,來靈活的控制組件當 props 和 state 發生變化時是否要從新渲染組件。
getSnapshotBeforeUpdate(prevProps, prevState)
複製代碼
getSnapshotBeforeUpdate()在最新的渲染輸出提交給DOM前將會當即調用。它讓你的組件能在當前的值可能要改變前得到它們。這一輩子命週期返回的任何值將會 做爲參數被傳遞給componentDidUpdate()。
componentDidUpdate(prevProps, prevState, snapshot)
複製代碼
在組件的更新已經同步到 DOM 中以後馬上被調用。
該方法不會在初始化渲染的時候調用。使用該方法能夠在組件更新以後操做 DOM 元素。
componentWillUnmount()
複製代碼
在組件從 DOM 中移除的時候馬上被調用。
在該方法中執行任何須要的清理,好比無效的定時器,或者清除在 componentDidMount 中建立的 DOM 元素。