0%

React

React

一个用于构建用户界面的 JAVASCRIPT 库, 主要用于构建 UI, 很多人认为 React 是 MVC 中的 V

官网

菜鸟教程

因为某些原因, 最近突然有了段空档期, boss 让我学下前端的知识.

本文大量复制自菜鸟教程.

Hello World

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
</head>
<body>

<div id="example"></div>
<script type="text/babel">
const element = (
<h1>Hello, world!</h1>
)
ReactDOM.render(
element,
document.getElementById('example')

);
</script>

</body>
</html>

JSX 语法

JavaScript 的语法扩展, 允许 HTML 与 JavaScript 混写

1
2
3
4
5
6
7
8
9
10
11
12
var names = ['Alice', 'Emily', 'Kate'];

ReactDOM.render(
<div>
{
names.map(function (name) {
return <div key={name}>Hello, {name}!</div> // 这里加上属性 key 防止报错
})
}
</div>,
document.getElementById('example')
);

遇到 HTML 标签, 以 < 开头, 就用 HTML 规则解析;遇到代码块, 以 { 开头, 就用 JavaScript 规则解析.

1
2
3
4
5
6
7
8
var arr = [
<h1>Hello world!</h1>,
<h2>React is awesome</h2>,
];
ReactDOM.render(
<div>{arr}</div>,
document.getElementById('example')
);

允许直接在模板中插入 JavaScript 变量. 如果这个变量是一个数组, 则会展开这个数组的所有成员.

代码中有多个 HTML 标签时, 需要有一个标签包裹它们.

引用时 type 为 “text/babel”.

1
<script type="text/babel" src="helloworld_react.js"></script>

样式

React 推荐使用内联样式.

直接写在标签里的内联样式需要包裹两个花括号.

1
2
3
4
5
6
7
8
9
10
11
var myStyle = {
fontSize: 100,
color: '#FF0000'
};
ReactDOM.render(
<div>
<h1 style = {myStyle}>hello world</h1>
<h1 style = {{fontSize: 10, color: '#00FF00'}}>hello world</h1>
</div>,
document.getElementById('example')
);

注释

在标签内注释要写在花括号内.

在标签外注释不能使用花括号.

1
2
3
4
5
6
7
8
ReactDOM.render(
<div>
<h1>helloworld</h1>
{/*注释需要花括号...*/}
</div>,
/*注释不能在花括号内*/
document.getElementById('example')
);

花括号 {}

在花括号内, 不能出现 var, const, let 等关键字, 因此, 花括号内的变量需要现在花括号外定义.

花括号内的语句必须用逗号分隔开.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var ListItem = (props) => {       //es6中箭头函数
return <li>{props.value}</li>;
}

function NumberList(props) {
var numbers; //声明在外面是因为 {} 中不能出现var,const,let等这种关键字
return (
<ul>
{
numbers = props.numbers, //注意这里要加逗号

numbers.map((number) =>
<ListItem key={number}
value={number} />
)
}
</ul>
);
}

var arr = [1,2,3]; //要传递的参数
ReactDOM.render(
<NumberList numbers={arr}/>, //这里的numbers就是props下的numbers, 即props.numbers
document.all('example')
);

这个例子也可作为 [列表 与 Keys](#列表 与 Keys) 的参考.

组件

把代码封装成组件, 然后像插入普通 HTML 一样在页面中插入该组件.

组件可以用函数定义, 也可以用 ES6 class 定义.

组件可以嵌套使用.

组件无法直接使用 style 样式. 要给组件加上样式只能在组件内部里的标签中加入. 在组件 A 中嵌套 B, 然后给 A 中的 B 加上样式也是不起效的.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Clock extends React.Component {
render() {
return (
<div>
<h1 name="gny">Hello, world!</h1>
<h2>现在是 {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}

function tick() {
ReactDOM.render(
<Clock date={new Date()} name="时钟"/>,
document.getElementById('example')
);
}

setInterval(tick, 1000);

组件的属性 Props

组件可以任意加入属性, 组件的属性可以通过 props.属性名 获取.

与后面要提到的 state 不同, props 是不可变的, 而 state 可以根据与用户交互来改变.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function Name(props) {
return <h1 style={{fontSize: 10, color: "#FF0000"}}>web name: {props.name}</h1>;
}
function Url(props) {
return <h1>web address:{props.url}</h1>;
}
function Nickname(props) {
return <h1>web nickname:{props.nickname}</h1>;
}
function App(props) {
return (
<div>
<Name name="fm7077" />
<Url url="http://blog.fm7077.com" />
<Nickname nickname="gny" />
</div>
);
}

ReactDOM.render(
<App name="fm7077" />,
document.getElementById('example')
);

如果是组件类则通过 this.props.属性名 获取, 参考上面时钟代码.

Props 验证

用来控制传入组件的数据格式, 当向 props 传入无效数据时, 控制台会抛出警告

==React.PropTypes 在 React v15.5 版本后已经移到了 prop-types 库。==

1
<script src="https://cdn.bootcss.com/prop-types/15.6.1/prop-types.js"></script>

使用方法:

1
2
3
类名.propTypes = {
属性名: propTypes.类型
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// var title = "here is niurouwan";
var title = 123;
class MyTitle extends React.Component {
render() {
return (
<h1>Hello, {this.props.title}</h1>
);
}
}

MyTitle.propTypes = {
title: PropTypes.string
};
ReactDOM.render(
<MyTitle title={title} />,
document.getElementById('example')
);
/*
使用第一个 title 时, 正常.
使用第二个 title, 控制台报错
*/

this.props.children

官方文档

类组件与函数组件属性一一对应, 但有一个例外: this.props.children, 它表示组件的所有子节点.

1
2
3
4
5
6
7
8
9
10
11
12
<Clock date={new Date()} name="时钟">
<span>hello</span>
<span>world</span>
</Clock>,
/*---------------------------------------------------------------------*/
<ol>
{
React.Children.map(this.props.children, function (child){
return <li>{child}</li>
})
}
</ol>

如果当前组件:

  • 没有子节点: 返回 undefined
  • 一个子节点: 返回 object
  • 多个子节点: 返回 array

组件间的传参

参考链接

注意:

多属性传入用空格隔开, 而不是逗号或分号.

1
<App name={"abc"} url={"https://www.12.com"} nickname={"123"}/>

class 属性要写成 className, for 属性要写成 htmlFor, 因为这两个是 JavaScript 的保留字.

组件类首字母必须大写, 且只能包含一个顶层标签, 否则会报错.

constructor() 和 super()

react 中 constructor() 和 super() 到底是个啥

这里还涉及到 js 的原型链, 参考链接

constructor() 构造方法

ES6 对类的默认方法.

子类如果没有 constructor 方法, 该方法会被默认添加.

构造函数是唯一能初始化 this.state 的地方.

super() 继承

子类必须在 constructor() 中调用 super(), 这是因为子类没有自己的 this 对象, 它只能继承自父类的 this 对象, 然后对其进行加工, 而 super() 就是将父类中的 this 对象继承给子类.

React State (状态)

React 把组件看成一个状态机 (State Machines). 通过与用户的交互, 实现不同状态, 然后渲染 UI, 让用户界面和数据保持一致.

React 里, 只需要更新组件的 state, 然后根据新的 state 重新渲染用户界面.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}

componentDidMount() {
this.timerID = setInterval(
() => this.setState({date: new Date()}), 1000 // 这里是 ES6 中声明函数的一种方式, 叫做箭头函数
);
/*
等效于
let that = this
this.timerID = setInterval(function(){
return that.~~~;
----------------这里不能直接返回 this.~~~ 是因为这里面的 this 指代的是 function
}, 1000)
*/
}

componentWillUnmount() {
clearInterval(this.timerID);
}

render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>现在是 {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}

ReactDOM.render(
<Clock />,
document.getElementById('example')
);

箭头函数

每当 Clock 组件第一次加载到 DOM 中的时候, 生成定时器, 这在 React 中被称为挂载

每当 Clock 生成的这个 DOM 被移除的时候, 清楚定时器, 这在 React 中被称为卸载.

通过在组件类上声明特殊的方法, 当组件挂载或卸载时, 来运行一些代码.

如上面的代码中, componentDidMount() 与 componentWillUnmound() 两个方法被称为生命周期钩子.

组件输出到 DOM 后会执行 componentDidMount() 钩子, 我们就可以在这个钩子上设置一个定时器.

this.timerID 为定时器的 ID. 我们可以在 componentWillUnmount() 钩子中卸载定时器

代码执行顺序:

  1. 当 <Clock /> 被传递给 ReactDom.render() 时, React 调用 Clock 组件的构造函数, 并使用包含当前时间的对象来初始化 this.state.
  2. React 调用 Clock 组件的 render() 方法, 以了解屏幕上应该显示什么内容, 然后 React 更新 DOM 以匹配 Clock 的渲染输出.
  3. 当 Clock 的 h2 被插入到 DOM 时, React 调用 componentDidMount() 生命钩子. 在其中, 该钩子激活一个定时器, 每秒钟执行一起.
  4. 浏览器每秒钟调用定时器内的方法, 在其中 Clock 组件通过使用包含当前时间的对象调用 setState() 来调度 UI 更新. 通过调用 setState(), React 知道状态已经改变, 并再次调用 reander() 方法来确定屏幕上应该显示什么. 这一次, render() 方法中的 this.state.date 将不同, 所以渲染输出将包含更新的时间, 并相应地更新 DOM.
  5. 一旦 h2 从 DOM 中移除, React 会调用 componentWillUnmount() 这个钩子函数, 清除定时器.

数据自顶向下流动

无论父组件还是子组件都不能知道某个组件是有状态还是无状态, 并且它们不应该关心某组件是被定义为一个函数还是一个类.

任何状态始终由某些特定组件所有, 并且从该状态到处的任何数据或 UI 只能影响组件树下方的组件

事件处理

参考链接

React 元素的事件处理和 DOM 元素类似, 但有一点语法上的不同:

  • React 时间绑定属性的命名采用驼峰式写法, 而不是小写.
  • 如果采用 JSX 的语法, 需要传入要给函数作为事件处理函数, 而不是一个字符串
  • React 不能使用返回 false 的方式阻止默认行为, 必须使用 preventDefault

html:

1
2
3
<button onclick="toggle()">按钮</button>

<a href="#" onclick="console.log('点击链接')"; return false"></a>

React:

1
2
3
4
5
6
7
8
9
10
11
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('链接被点击');
}
return (
<a href="#" onClick={handleClick}>
点我
</a>
);
}

这里的 e 是一个合成事件.

使用 React 的时候通常不需要使用 addEventListener 为一个已创建的 DOM 元素添加监听器, 仅需要在这个元素初始渲染的时候提供一个监听器.

当使用 ES6 class 语法来定义一个组件时, 事件处理器会成为类的一个方法.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};

// 这边绑定是必要的,这样 `this` 才能在回调函数中使用
this.handleClick = this.handleClick.bind(this);
}

handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}

render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}

ReactDOM.render(
<Toggle />,
document.getElementById('example')
);

向事件处理程序传递参数

eg: 若 id 是你要删除那一行的 id, 以下两种方式都可以向事件处理程序传递参数

1
2
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

上面两种方式是等价的.参数 e 作为 React 事件对象将会被作为第二个参数进行传递.

通过箭头函数的方式, 事件对象必须显式的进行传递.

通过 bind 的方式, 事件对象以及更多的参数将会被隐式的进行传递.


React 点击事件的 bind(this) 如何传参:

需要通过 bind 方法来绑定参数, 第一个参数指向 this, 第二个参数开始才是事件函数接收的参数.

事件: this.handleclick.bind(this, 要传的参数)

函数: handleclick(传过来的参数, event)

1
2
3
4
5
<button onClick={this.handleClick.bind(this, props0, props1, ...}></button>

handleClick(porps0, props1, ..., event) {
..........
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Popper extends React.Component{
constructor(){
super();
this.state = {name:'Hello world!'};
}


preventPop(name, e){ //事件对象e要放在最后
e.preventDefault();
alert(name);
}
one(para){
console.log('paramter in on fun ', para)
}

render(){
return (
<div>
<p>hello</p>
{/* 通过 bind() 方法传递参数。 */}
<a href="https://reactjs.org" onClick={this.preventPop.bind(this,this.state.name)}>Click</a>
<button onClick={this.one.bind(this, this.state.name)}>test</button>
</div>
);
}
}

条件渲染

与运算符 &&

在 js 中, true && expression 总是返回 expression, 而 false && expression 总是返回 false.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 &&
<h2>
您有 {unreadMessages.length} 条未读信息。
</h2>
}
</div>
);
}

const messages = ['React', 'Re: React', 'Re:Re: React'];
ReactDOM.render(
<Mailbox unreadMessages={messages} />,
document.getElementById('example')
);

或运算符效果正好相反.

阻止组件渲染

不想要某个组件渲染, 让那个组件返回 null 即可.

1
2
3
4
5
6
7
8
9
10
function WarningBanner(props) {
if (!props.warn) {
return null;
}
return (
<div className="warning">
警告!
</div>
);
}

列表 与 Keys

使用 js 的 map() 方法来创建列表.

1
2
3
4
5
6
7
8
9
10
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number, index) =>
<li key={number.toString()}>{numbers}</li>
// 使用索引作 key: <li key={index}>{number}</li>
);

ReactDOM.render(
<ul>{listItems}</ul>,
document.getElementById('example')
);

js 中的 map() 就是遍历一个数组, 然后对每个元素执行指定操作, 再返回新数组.

map() 不会更改原始数组, 不会对空数组进行检测.

key 可以再 DOM 中的某些元素被增加或删除时, 帮助 React 识别哪些元素发生了变化. 一个元素的 key 最好是这个元素在列表中拥有的独一无二的字符串. 当元素没有确定的 id 时, 也可以使用它的索引 index 作为 key.

元素的 key 只有在它和它的兄弟节点对比时才有意义. 也就是在 map() 内部调用元素时, 为每个元素加上 key.

数组元素中使用的 key 在其兄弟之间应该是独一无二的, 但不需要全局唯一.

React 组件 API

官方文档

回看下 React state (状态)

React 组件 API 有 7 个方法:

设置状态 setState

合并nextState和当前state,并重新渲染组件。setState是React事件处理函数中和请求回调函数中触发UI更新的主要方法。

1
setState(object newState, [function callback])
  • newState: 将要设置的新状态, 该状态会和当前的 state 合并.

  • callback: 可选参数, 回调函数. 在 setState 设置成功, 且组件重新渲染后调用.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Counter extends React.Component{
constructor(props) {
super(props);
this.state = {clickCount: 0};
this.handleClick = this.handleClick.bind(this);
}

handleClick() {
this.setState(function(state) {
return {clickCount: state.clickCount + 1};
}, function(){console.log('here is call back')});
}
render () {
return (<h2 onClick={this.handleClick}>点我!点击次数为: {this.state.clickCount}</h2>);
}
}
ReactDOM.render(
<Counter />,
document.getElementById('example')
);

不能再组件内部通过 this.state 修改状态, 因为该状态会在调 setState() 后被替换.

setState() 并不会立即改变 this.state, 而是创建一个即将处理的 state. setState() 并不一定时同步的, 为了提升性能, React 会批量执行 state 和 DOM 渲染.

setState() 总是会触发一次组件重绘, 除非在 shouldComponentUpdate() 中实现了一些条件渲染逻辑.

替换状态 replaceState

这个方法再从 React.Component 扩展的 ES6 class 组件里不可用, 也许会在为了完全移除.

1
replaceState(object nextState, [function callback])
  • nextState, 将要设置的新状态, 该状态会替换当前的 state.
  • callback, 可选参数, 回调函数. 会在 replaceState 设置成功, 且组件重新渲染后调用.

replaceState() 与 setState() 类似, 但 replaceState() 指挥保留 nextState 中状态, 原 state 不再 nextState 中的状态都会被删除.

设置属性 setProps

设置组件属性, 并重新渲染组件

1
setProps(object nextProps, [function callback])
  • nextProps, 将要设置的新属性, 该状态会和当前的 props 合并
  • callback, 可选参数, 回调函数, 该函数会在 setProps 设置成功, 且组件重新渲染后调用.

替换属性 replaceProps

这个方法再从 React.Component 扩展的 ES6 class 组件里不可用, 也许会在为了完全移除.

1
replaceProps(object nextProps, [function callback])
  • nextProps, 将要设置的新属性, 该属性会替换当前的 props.

  • callback, 可选参数, 回调函数. 该函数会在 replaceProps 设置成功, 且组件重新渲染后调用.

replaceProps() 与 setProps() 类似, 但它会删除原有的 props.

强制更新 forceUpdate

1
forceUpdate([function callback])
  • callback, 可选参数, 回调函数. 会在组件 render() 方法调用后调用

forceUpdate() 会使组件调用自身的 render() 方法重新渲染组件, 组件的子组件也会调用自己的 render(). 但, 组件重新渲染时, 依然会读取 this.props 和 this.state, 如果状态没有改变, 那么 React 只会更新 DOM.

forceUpdate() 适用于 this.props 和 this.state 之外的组件重绘 (eg, 修改了 this.state 后), 通过该方法通知 React 需要调用 render().

通常要尽量避免使用 forceUpdate(), 而仅从 this.props 和 this.state 中读取状态并由 React 触发 render() 调用.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Counter extends React.Component{
constructor(props) {
super(props);
this.state = {clickCount: 0};
this.handleClick = this.handleClick.bind(this);
}

handleClick() {
this.forceUpdate(function(){
console.log("here is forceUpdate")
})
this.setState(function(state) {
return {clickCount: state.clickCount + 1};
}, function(){console.log('here is call back')});
}
render () {
console.log('这里是 render')
return (<h2 onClick={this.handleClick}>点我!点击次数为: {this.state.clickCount}</h2>);
}
}
ReactDOM.render(
<Counter />,
document.getElementById('example')
);
/*运行, 并点击两次, 控制台输出:
这里是 render
这里是 render
here is forceUpdate
here is call back
这里是 render
here is forceUpdate
here is call back
*/

获取 DOM 节点 findDOMNode

1
DOMElement findDOMNode()
  • 返回真正的 DOM 元素, 以便对 DOM 节点进行操作.

如果组件已经挂载到 DOM 中, 该方法返回对应的浏览器原生 DOM 元素, 也就是说该方法返回的是一个真正的 DOM 元素. 当 render 返回 null 或 false 时, this.findDOMNode() 也会返回 null.

从 DOM 中读取值的时候, 该方法很有用. eg: 获取表单字段的值和做一些 DOM 操作.

这个方法改了好几次了, 最新用法如下:

1
ReactDOM.findDOMNode(this.refs.ref名)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Counter extends React.Component{
constructor(props) {
super(props);
this.state = {clickCount: 0};
this.handleClick = this.handleClick.bind(this);
}

handleClick() {
ReactDOM.findDOMNode(this.refs.myInput).focus();
this.setState(function(state) {
return {clickCount: state.clickCount + 1};
});
}
render () {
return (
<div>
<h2 onClick={this.handleClick} ref="myH2">click!click num: {this.state.clickCount}</h2>
<input ref="myInput"></input>
</div>
);
}
}
ReactDOM.render(
<Counter />,
document.getElementById('example')
);

判断组件挂载状态 isMounted

这个方法再从 React.Component 扩展的 ES6 class 组件里不可用, 也许会在为了完全移除.

1
bool isMounted()
  • 返回 true 或 false, 表示组件是否已挂载到 DOM 中.

组件生命周期

组件的生命周期可分为三个状态:

  • Mounting: 已插入真实 DOM
  • Upadting: 正在被重新渲染
  • Unmounting: 已移出真实 DOM

生命周期的方法:

  • componentWillMount: 在渲染前调用, 在客户端也在服务端

  • componentDidMount: 在第一次渲染后调用, 只在客户端. 之后组件已经生成了对应 DOM 结构, 可以通过 this.findDOMNode() 来进行访问.

    在这个方法中可以执行 setTimeout, setInterval 或者发送 ajax 请求等操作, 而不用担心异步操作阻塞 UI.

  • componentWillReceiveProps: 在组件接收到一个新的 prop (更新后) 时被调用. 在初始化 render 时不会被调用.

  • shouldComponentUpdate: 返回一个 bool, 在组件接收到新的 props 或 state 时被调用, 在初始化时或者使用 forceUpdate 时不被调用.

    可以在确认不需要更新组件时使用.

  • componentWillUpdate: 组件接收到新的 props 或 state 但还没有 render 时被调用, 初始化时不会吧调用

  • componentDidUpdate: 组件完成更新后立即被调用, 初始化时不会被调用.

  • componentWillUnmount: 组件从 DOM 中移除前立即被调用.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
class Button extends React.Component {
constructor(props) {
super(props);
this.state = {data: 0};
this.setNewNumber = this.setNewNumber.bind(this);
}

setNewNumber() {
this.setState({data: this.state.data + 1})
}
render() {
return (
<div>
<button onClick = {this.setNewNumber}>INCREMENT</button>
<Content myNumber = {this.state.data}></Content>
</div>
);
}
}


class Content extends React.Component {
componentWillMount() {
console.log('Component WILL MOUNT!')
}
componentDidMount() {
console.log('Component DID MOUNT!')
}
componentWillReceiveProps(newProps) {
console.log('Component WILL RECEIVE PROPS!')
}
shouldComponentUpdate(newProps, newState) {
return true;
}
componentWillUpdate(nextProps, nextState) {
console.log('Component WILL UPDATE!');
}
componentDidUpdate(prevProps, prevState) {
console.log('Component DID UPDATE!')
}
componentWillUnmount() {
console.log('Component WILL UNMOUNT!')
}

render() {
return (
<div>
<h3>{this.props.myNumber}</h3>
</div>
);
}
}
ReactDOM.render(
<div>
<Button />
</div>,
document.getElementById('example')
);

AJAX

react 组件的数据可以在 componentDidMount 方法中通过 ajax 来获取, 当从服务端获取数据时可以将数据存储在 state 中, 再用 this.setState 方法重新渲染 UI.

表单与事件

在 HTML 中, 像 <input>, <textarea> 和 <select> 这类表单元素会维持自身状态, 并根据用户输入进行更新. 但在 React 中, 可变的状态通常存在组件的状态属性中, 并且只能用 setState() 方法进行更新.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//这段代码通过 onChange 事件响应更新用户输入的值
class HelloMessage extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'Hello Runoob!'};
this.handleChange = this.handleChange.bind(this);
}

handleChange(event) {
this.setState({value: event.target.value});
}
render() {
var value = this.state.value;
return <div>
<input type="text" value={value} onChange={this.handleChange} />
<h4 >{value}</h4>
</div>;
}
}
ReactDOM.render(
<HelloMessage />,
document.getElementById('example')
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/*  这段代码实现的效果与上面的代码一样
在子组件上使用表单, onChange 方法将触发 state 的更新, 并将更新的值传递到子组件的输入框 value 上来重新渲染界面.
在父组件通过创建事件句柄 (handleChange), 并作为 prop (updateStateProp) 传递到子组件上.
*/
class Content extends React.Component {
render() {
return <div>
<input type="text" value={this.props.myDataProp} onChange={this.props.updateStateProp} />
<h4>{this.props.myDataProp}</h4>
</div>;
}
}
class HelloMessage extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'Hello Runoob!'};
this.handleChange = this.handleChange.bind(this);
}

handleChange(event) {
this.setState({value: event.target.value});
}
render() {
var value = this.state.value;
return <div>
<Content myDataProp = {value}
updateStateProp = {this.handleChange}></Content>
</div>;
}
}
ReactDOM.render(
<HelloMessage />,
document.getElementById('example')
);

好神奇的做法

当要处理多个 input 元素时, 可以给每个元素添加 name 属性, 处理函数根据 event.target.name 的值来选择做什么.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/*  代码效果就是两个输入框公用一个处理函数, 但不互相影响. 
1
*/
class Reservation extends React.Component {
constructor(props) {
super(props);
this.state = {
isGoing: true,
numberOfGuests: 2
};

this.handleInputChange = this.handleInputChange.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
});
}

render() {
return (
<form>
<label>
是否离开:
<input
name="isGoing"
type="checkbox"
checked={this.state.isGoing}
onChange={this.handleInputChange} />
</label>
<br />
<label>
访客数:
<input
name="numberOfGuests"
type="number"
value={this.state.numberOfGuests}
onChange={this.handleInputChange} />
</label>
</form>
);
}
}
ReactDOM.render(
<Reservation />,
document.getElementById('example')
);

Ref

React 非常特殊的一种属性, 可以用来绑定到 render() 输出的任何组件上.

使用方法

1
2
3
4
5
<input ref='myInput'>

let input = this.refs.myInput
let inputValue = input.value;
let inputRect = input.getBoundingClientRect();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class MyComponent extends React.Component {
handleClick() {
// 使用原生的 DOM API 获取焦点
this.refs.myInput.focus();
}
render() {
// 当组件插入到 DOM 后,ref 属性添加一个组件的引用于到 this.refs
return (
<div>
<input type="text" ref="myInput" />
<input
type="button"
value="点我输入框获取焦点"
onClick={this.handleClick.bind(this)}
/>
</div>
);
}
}

ReactDOM.render(
<MyComponent />,
document.getElementById('example')
);

可以看到, refs 和 findDOMNode 非常相似