## 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`组件在应用的最外面,包裹一层。