## JSX
### 實驗目的
1. 掌握 JSX 基本語法
### 操作步驟
1. 瀏覽器打開`demos/jsx-demo/index.html`,仔細查看源碼。
### 注意事項
1. `ReactDOM.render`方法接受兩個參數:一個虛擬 DOM 節點和一個真實 DOM 節點,作用是將虛擬 DOM 掛載到真實 DOM。
### 練習
1. 修改源碼,將顯示文字變為 ”Hello React!“。
## React 組件語法
### 實驗目的
1. 掌握 React 組件的基本寫法
### 操作步驟
1. 瀏覽器打開`demos/react-component-demo/index1.html`,仔細查看源碼。
### 注意事項
1. `class MyTitle extends React.Component`是 ES6 語法,表示自定義一個`MyTitle`類,該類繼承了基類`React.Component`的所有屬性和方法。
2. React 規定,自定義組件的第一個字母必須大寫,比如`MyTitle`不能寫成`myTitle`,以便與內置的原生類相區分。
3. 每個組件都必須有`render`方法,定義輸出的樣式。
4. `<MyTitle/>`表示生成一個組件類的實例,每個實例一定要有閉合標簽,寫成`<MyTilte></MyTitle>`也可。
## React 組件的參數
### 實驗目的
1. 學會向 React 組件傳參數
### 操作步驟
1. 瀏覽器打開`demos/react-component-demo/index2.html`,仔細查看源碼。
### 注意事項
1. 組件內部通過`this.props`對象獲取參數。
### 練習
1. 將組件的顏色,從紅色(`red`)換成黃色(`yellow`)。
## React 組件的狀態
### 實驗目的
1. 學會通過狀態變動,引發組件的重新渲染。
### 操作步驟
1. 瀏覽器打開`demos/react-component-demo/index3.html`,仔細查看源碼。
### 注意事項
~~~
class MyTitle extends React.Component {
constructor(...args) {
super(...args);
this.state = {
name: '訪問者'
};
}
// ...
~~~
`constructor`是組件的構造函數,會在創建實例時自動調用。`...args`表示組件參數,`super(...args)`是 ES6 規定的寫法。`this.state`對象用來存放內部狀態,這里是定義初始狀態。
~~~
<div>
<input
type="text"
onChange={this.handleChange.bind(this)}
/>
<p>你好,{this.state.name}</p>
</div>;
~~~
`this.state.name`表示讀取`this.state`的`name`屬性。每當輸入框有變動,就會自動調用`onChange`指定的監聽函數,這里是`this.handleChange`,`.bind(this)`表示該方法內部的`this`,綁定當前組件。
~~~
handleChange(e) {
let name = e.target.value;
this.setState({
name: name
});
}
~~~
`this.setState`方法用來重置`this.state`,每次調用這個方法,就會引發組件的重新渲染。
## React 組件實戰
### 實驗目的
1. 學會自己寫簡單的 React 組件。
### 操作步驟
1. 瀏覽器打開`demos/react-component-demo/index4.html`。
2. 點擊`Hello World`,看看會發生什么。
### 練習
1. 修改源碼,使得點擊`Hello World`后,會顯示當前的日期,比如`Hello 2016年1月1日`。
2. 請在上一步練習的基礎上,進一步修改。現在`Hello World`點擊一次,會改變內容,再點擊就不會有反應了。請將其改成,再點擊一次變回原樣。
### 提示
練習一、下面的代碼可以得到當前日期。
~~~
var d = new Date();
d.getFullYear() // 當前年份
d.getMonth() + 1 // 當前月份
d.getDate() // 當前是每個月的幾號
~~~
練習二、可以在`this.state`里面設置一個開關變量`isClicked`。
~~~
this.state = {
text: 'World',
isClicked: false
};
~~~
然后,在`this.handleClick`方法里面,做一個`toggle`效果。
~~~
let isClicked = !this.state.isClicked;
this.setState({
isClicked: isClicked,
text: isClicked ? 'Clicked' : 'World'
});
~~~
## React 組件的生命周期
### 實驗目的
1. 掌握鉤子方法的基本用法
2. 掌握組件如何通過 Ajax 請求獲取數據,并對數據進行處理
### 操作步驟
1. 打開`demos/react-lifecycle-demo/index.html`,仔細查看源碼。
### 注意事項
~~~
componentDidMount() {
const url = '...';
$.getJSON(url)
.done()
.fail();
}
~~~
* `componentDidMount`方法在組件加載后執行,只執行一次。本例在這個方法里向服務器請求數據,操作結束前,組件都顯示`Loading`。
* `$.getJSON`方法用于向服務器請求 JSON 數據。本例的數據從 Github API 獲取,可以打開源碼里面的鏈接,看看原始的數據結構。
### 練習
1. 本例的 JSON 數據是 Github 上面最受歡迎的 JavaScript 項目。請在網頁上顯示一個列表,列出這些項目。
### 提示
(1)?`this.state.loading`記錄數據加載是否結束。只要數據請求沒有結束,`this.state.loading`就一直是`true`,網頁上顯示`loading`。
(2)?`this.state.error`保存數據請求失敗時的錯誤信息。如果請求失敗,`this.state.error`就是返回的錯誤對象,網頁上顯示報錯信息。
(3)?`this.state.data`保存從服務器獲取的數據。如果請求成功,可以先用`console.log`方法,將它在控制臺里打印出來,看看數據結構。
~~~
render() {
// 加一行打印命令,看看數據結構
console.log(this.state.data);
return {
// ...
~~~
(4)?`this.state.data`里面的`this.state.data.items`應該是一個數組,保存著每個項目的具體信息。可以使用`forEach`方法進行遍歷處理。
~~~
var projects = this.state.data.items;
var results = [];
projects.forEach(p => {
var item = <li>{p.name}</li>;
results.push(item);
});
~~~
(5)然后,將上一步的`results`插入網頁即可。
~~~
<div>
<ul>{results}</ul>
</div>
~~~
## ReCharts
### 實驗目的
1. 了解如何使用第三方組件庫。
### 操作步驟
1. 瀏覽器打開`demos/recharts-demo/index.html`,查看效果。
## MobX
### 實驗目的
1. 理解 MobX 框架
### 操作步驟
(1) 命令行進入`demos/mobx-demo/`目錄,執行如下的命令。
~~~
$ npm install
$ npm start
~~~
(2) 打開瀏覽器,訪問 http://localhost:8080,查看結果,并仔細研究代碼。
### 注意事項
~~~
@observer
class App extends React.Component {
render() {
// ...
}
}
~~~
`@observer`是一種新的語法,表示對整個類執行指定的函數。
數據保存在`Store`里面。`Store`的屬性分成兩種:被觀察的屬性(`@observable`),和自動計算得到的屬性`@computed`。
~~~
class Store {
@observable name = 'Bartek';
@computed get decorated() {
return `${this.name} is awesome!`;
}
}
~~~
`Store`的變化由用戶引發。組件觀察到`Store`的變化,自動重新渲染。
~~~
<p>
{this.props.store.decorated}
</p>
<input
defaultValue={this.props.store.name}
onChange={
(event) =>
this.props.store.name = event.currentTarget.value
}
/>
~~~
## Redux
### 實驗目的
1. 理解 Redux 架構
### 操作步驟
(1) 命令行下進入`demos/redux-demo`目錄,執行如下的命令。
~~~
$ npm install
$ npm start
~~~
(2)打開瀏覽器,訪問 http://localhost:8080,查看結果,并仔細研究代碼。
### 注意事項
(1) Redux 要求 UI 的渲染組件都是純組件,即不包含任何狀態(`this.state`)的組件。
~~~
<div className="index">
<p>{this.props.text}</p>
<input
defaultValue={this.props.name}
onChange={this.props.onChange}
/>
</div>
~~~
(2) 進行數據處理、并包含狀態的組件,稱為”容器組件“。Redux 使用`connect`方法,自動生成 UI 組件對應的”容器組件“。
~~~
// MyComponent 是純的 UI 組件
const App = connect(
mapStateToProps,
mapDispatchToProps
)(MyComponent);
~~~
(3)?`mapStateToProps`函數返回一個對象,表示一種映射關系,將 UI 組件的參數映射到`state`。
~~~
function mapStateToProps(state) {
return {
text: state.text,
name: state.name
};
}
~~~
(4)?`mapDispatchToProps`函數也是返回一個對象,表示一種映射關系,但定義的是哪些用戶的操作應該當作`Action`,傳給`Store`。
~~~
function mapDispatchToProps(dispatch) {
return {
onChange: (e) => dispatch({
type: 'change',
payload: e.target.value
})
}
}
~~~
(5)?`reducer`函數用來接收`action`,算出新的`state`。
~~~
function reducer(state = {
text: '你好,訪問者',
name: '訪問者'
}, action) {
switch (action.type) {
case 'change':
return {
name: action.payload,
text: '你好,' + action.payload
};
}
}
~~~
`Store`由 Redux 提供的`createStore`方法生成,該方法接受`reducer`作為參數。
~~~
const store = createStore(reducer);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.body.appendChild(document.createElement('div'))
);
~~~
為了把`Store`傳入組件,必須使用 Redux 提供的`Provider`組件在應用的最外面,包裹一層。