# React 核心概念
# JSX
# 什么是 JSX
- Facebook 起草的 JS 扩展语法
- 本质是一个 JS 对象,会被 babel 编译,最终会被转换为 React.createElement
- 每个 JSX 表达式,有且仅有一个根节点
<></>等同于<React.Fragment></React.Fragment>` 不会渲染在页面上
- 每个 JSX 元素必须结束(XML 规范)
# 在 JSX 中嵌入表达式
在 JSX 中使用注释
const div = ( <div> {a} * {b} = {a * b} <p> {/* 以下不会产生任何输出 */} {null} {undefined} {false} </p> <p> {/* 普通对象无法放置 */} {/* {obj} */} </p> <p> {/* React元素对象没问题 */} {obj} </p> </div> );1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19将表达式作为内容的一部分
- null、undefined、false 不会显示
- 普通对象,不可以作为子元素
- 可以放置 React 元素对象
将表达式作为元素属性
属性使用小驼峰命名法
防止注入攻击
- 自动编码
- dangerouslySetInnerHTML
import React from "react";
import ReactDOM from "react-dom";
const content = "<h1>afasfasfd</h1><p>阿斯顿法定发送</p>";
const div = (
<div
dangerouslySetInnerHTML={{
__html: content,
}}
></div>
);
ReactDOM.render(div, document.getElementById("root"));
2
3
4
5
6
7
8
9
10
11
12
13
# 元素的不可变性
- 虽然 JSX 元素是一个对象,但是该对象中的所有属性不可更改
- 如果确实需要更改元素的属性,需要重新创建 JSX 元素
# 组件和组件属性
组件:包含内容、样式和功能的 UI 单元
# 创建一个组件
特别注意:组件的名称首字母必须大写
- 函数组件
返回一个 React 元素
- 类组件
必须继承 React.Component
必须提供 render 函数,用于渲染组件
# 组件的属性
- 对于函数组件,属性会作为一个对象的属性,传递给函数的参数
- 对于类组件,属性会作为一个对象的属性,传递给构造函数的参数
注意:组件的属性,应该使用小驼峰命名法
组件无法改变自身的属性。
之前学习的 React 元素,本质上,就是一个组件(内置组件)
React 中的哲学:数据属于谁,谁才有权力改动
React 中的数据,自顶而下流动
# 组件状态
组件状态:组件可以自行维护的数据
组件状态仅在类组件中有效
状态(state),本质上是类组件的一个属性,是一个对象
# 状态初始化
//计时器,用作倒计时
import React, { Component } from "react";
export default class Tick extends Component {
//初始化状态1,JS Next 语法,目前处于实验阶段
state = {
left: this.props.number,
n: 123,
};
constructor(props) {
super(props);
//初始化状态2
// this.state = {
// left: this.props.number
// };
}
render() {
return (
<>
<h1>倒计时剩余时间:{this.state.left}</h1>
<p>{this.state.n}</p>
</>
);
}
}
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
# 状态的变化
不能直接改变状态:因为 React 无法监控到状态发生了变化
必须使用 this.setState({})改变状态
一旦调用了 this.setState,会导致当前组件重新渲染
//计时器,用作倒计时
import React, { Component } from "react";
export default class Tick extends Component {
//初始化状态1,JS Next 语法,目前处于实验阶段
state = {
left: this.props.number,
};
constructor(props) {
super(props);
this.timer = setInterval(() => {
//重新设置状态,触发自动的重新渲染
this.setState({
left: this.state.left - 1,
});
if (this.state.left === 0) {
//停止计时器
clearInterval(this.timer);
}
}, 1000);
}
render() {
return (
<>
<h1>倒计时剩余时间:{this.state.left}</h1>
<p>{this.state.n}</p>
</>
);
}
}
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
# 组件中的数据
- props:该数据是由组件的使用者传递的数据,所有权不属于组件自身,因此组件无法改变该数组
- state:该数组是由组件自身创建的,所有权属于组件自身,因此组件有权改变该数据
# 事件
在 React 中,组件的事件,本质上就是一个属性
按照之前 React 对组件的约定,由于事件本质上是一个属性,因此也需要使用小驼峰命名法
如果没有特殊处理,在事件处理函数中,this 指向 undefined
- 使用 bind 函数,绑定 this
- 使用箭头函数
import React, { Component } from "react";
import Tick from "./Tick";
export default class TickControl extends Component {
state = {
isOver: false, //倒计时是否完成
};
// constructor(props){
// super(props);
// // 使用bind来绑定this
// this.handleClick = this.handleClick.bind(this);
// this.handleOver = this.handleOver.bind(this);
// }
// 在原型上,this指向undefined
// handleClick() {
// console.log(this) // undefined
// console.log("点击了")
// }
// handleOver() {
// this.setState({
// isOver: true
// })
// }
// 结果:handleClick不在原型上,而在对象上
handleClick = () => {
console.log(this);
console.log("点击了");
};
handleOver = () => {
this.setState({
isOver: true,
});
};
render() {
let status = "正在倒计时";
if (this.state.isOver) {
status = "倒计时完成";
}
return (
<div>
<Tick onClick={this.handleClick} onOver={this.handleClick} number={10} />
<h2>{status}</h2>
</div>
);
}
}
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
# 深入认识 setState
setState,它对状态的改变,可能是异步的
如果改变状态的代码处于某个 HTML 元素的事件中,则其是异步的,否则是同步
import React, { Component } from "react";
export default class Comp extends Component {
state = {
n: 0,
};
constructor(props) {
super(props);
setInterval(() => {
//同步的
this.setState({
n: this.state.n + 1,
});
console.log(this.state.n); // 1
this.setState({
n: this.state.n + 1,
});
console.log(this.state.n); // 2
this.setState({
n: this.state.n + 1,
});
console.log(this.state.n); // 3
}, 1000);
}
handleClick = () => {
// 异步的的
this.setState({
n: this.state.n + 1,
});
console.log(this.state.n); // 0
this.setState({
n: this.state.n + 1,
});
console.log(this.state.n); // 0
this.setState({
n: this.state.n + 1,
});
console.log(this.state.n); // 0
};
render() {
console.log("render");
return (
<div>
<h1>{this.state.n}</h1>
<p>
<button onClick={this.handleClick}>+</button>
</p>
</div>
);
}
}
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
如果遇到某个事件中,需要同步调用多次,需要使用函数的方式得到最新状态
import React, { Component } from "react";
export default class Comp extends Component {
state = {
n: 0,
};
handleClick = () => {
// 异步的
this.setState(
(cur) => {
console.log(cur.n) // 0
// 参数cur表示当前的状态
// 该函数的返回结果,会混合(覆盖)掉之前的状态
// 该函数是异步执行
return {
n: cur.n + 1,
};
},
() => {
// 所有状态全部更新完成,并且重新渲染后执行
console.log("state更新完成", this.state.n);
}
);
this.setState((cur) =>
console.log(cur.n) // 1
return {
n: cur.n + 1,
});
this.setState((cur) =>
console.log(cur.n) // 2
return {
n: cur.n + 1,
});
};
render() {
console.log("render");
return (
<div>
<h1>{this.state.n}</h1>
<p>
<button onClick={this.handleClick}>+</button>
</p>
</div>
);
}
}
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
最佳实践:
- 把所有的 setState 当作是异步的
- 永远不要信任 setState 调用之后的状态
- 如果要使用改变之后的状态,需要使用回调函数(setState 的第二个参数)
- 如果新的状态要根据之前的状态进行运算,使用函数的方式改变状态(setState 的第一个函数)
React 会对异步的 setState 进行优化,将多次 setState 进行合并(将多次状态改变完成后,再统一对 state 进行改变,然后触发 render)
# 生命周期
生命周期:组件从诞生到销毁会经历一系列的过程,该过程就叫做生命周期。React 在组件的生命周期中提供了一系列的钩子函数(类似于事件),可以让开发者在函数中注入代码,这些代码会在适当的时候运行。
生命周期仅存在于类组件中,函数组件每次调用都是重新运行函数,旧的组件即刻被销毁
# 旧版生命周期
React < 16.0.0
constructor
- 同一个组件对象只会创建一次
- 不能在第一次挂载到页面之前,调用 setState,为了避免问题,构造函数中严禁使用 setState
componentWillMount
- 正常情况下,和构造函数一样,它只会运行一次
- 可以使用 setState,但是为了避免 bug,不允许使用,因为在某些特殊情况下,该函数可能被调用多次
render
- 返回一个虚拟 DOM,会被挂载到虚拟 DOM 树中,最终渲染到页面的真实 DOM 中
- render 可能不只运行一次,只要需要重新渲染,就会重新运行
- 严禁使用 setState,因为可能会导致无限递归渲染
componentDidMount
- 只会执行一次
- 可以使用 setState
- 通常情况下,会将网络请求、启动计时器等一开始需要的操作,书写到该函数中
组件进入活跃状态
componentWillReceiveProps
- 即将接收新的属性值
- 参数为新的属性对象
- 该函数可能会导致一些 bug,所以不推荐使用
shouldComponentUpdate
- 指示 React 是否要重新渲染该组件,通过返回 true 和 false 来指定
- 默认情况下,会直接返回 true
componentWillUpdate
- 组件即将被重新渲染
componentDidUpdate
- 往往在该函数中使用 dom 操作,改变元素
componentWillUnmount 1. 通常在该函数中销毁一些组件依赖的资源,比如计时器

# 新版生命周期
React >= 16.0.0
React 官方认为,某个数据的来源必须是单一的
getDerivedStateFromProps
- 通过参数可以获取新的属性和状态
- 该函数是静态的
- 该函数的返回值会覆盖掉组件状态
- 该函数几乎是没有什么用
getSnapshotBeforeUpdate
真实的 DOM 构建完成,但还未实际渲染到页面中。
在该函数中,通常用于实现一些附加的 dom 操作
该函数的返回值,会作为 componentDidUpdate 的第三个参数

# 表单
受控组件和非受控组件
受控组件:组件的使用者,有能力完全控制该组件的行为和内容。通常情况下,受控组件往往没有自身的状态,其内容完全收到属性的控制。
非受控组件:组件的使用者,没有能力控制该组件的行为和内容,组件的行为和内容完全自行控制。
表单组件,默认情况下是非受控组件,一旦设置了表单组件的 value 属性,则其变为受控组件(单选和多选框需要设置 checked)
import React, { Component } from "react";
// import NumberInput from "./components/NumberInput"
export default class App extends Component {
state = {
val: "123",
// checked: true
loves: ["足球", "篮球", "音乐", "其他"],
chooseLoves: ["篮球", "音乐"],
selVal: "beijing",
};
render() {
// const checkboxes = this.state.loves.map(it => (<label key={it}>
// <input
// type="checkbox"
// checked={this.state.chooseLoves.includes(it)}
// onChange={e => {
// if (e.target.checked) {
// this.setState({
// chooseLoves: [...this.state.chooseLoves, it]
// })
// }
// else {
// this.setState({
// chooseLoves: this.state.chooseLoves.filter(l => l !== it)
// })
// }
// }}
// />
// {it}
// </label>))
return (
<div>
{/* 默认情况下,它是一个非受控组件 */}
{/* <input type="text" /> */}
{/* <input type="text" value={this.state.val} onChange={e => {
this.setState({
val: e.target.value
})
}} /> */}
{/* <button onClick={() => {
console.log(this.state.val);
}}>获取文本框的值</button> */}
{/* <NumberInput /> */}
{/* {checkboxes} */}
{/* <button onClick={() => {
console.log(this.state.chooseLoves)
}}>获取选中的值</button> */}
<select
value={this.state.selVal}
onChange={(e) => {
this.setState({
selVal: e.target.value,
});
}}
>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="shenzheng">深证</option>
</select>
</div>
);
}
}
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
59
60
61
62
63
64
65
66
67
68
69
70
71