# React简介
- React 是一个用于构建用户界面的 JAVASCRIPT 库。
- React 主要用于构建 UI,很多人认为 React 是 MVC 中的 V(视图)。
- React 起源于 Facebook 的内部项目,用来架设 Instagram 的网站,并于 2013 年 5 月开源。
- React 拥有较高的性能,代码逻辑非常简单,越来越多的人已开始关注和使用它。
# React特点
- 声明式设计 −React采用声明范式,可以轻松描述应用。
- 高效 −React通过对DOM的模拟,最大限度地减少与DOM的交互。
- 灵活 −React可以与已知的库或框架很好地配合。
- JSX − JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但我们建议使用它。
- 组件 − 通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。
- 单向响应的数据流 − React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。
# React元素渲染
元素是构成React应用的最小单元,它用于描述屏幕上输出的内容。
const element = <h1>Hello, world</h1>;
与浏览器的DOM元素不同,React当中的元素事实上是普通的对象,React DOM可以确保浏览器DOM的数据内容与React元素保持一致。
# 将元素渲染到DOM中
首先在页面中添加带id的div标签::
<div id="root"></div>
用React开发应用时,一般只会定义一个根节点,但如果是在已有项目中引入React的话,可能会需要在不同的部分单独定义React根节点。
要将React元素渲染到根DOM节点中,我们可以通过ReactDOM.reader()
方法来渲染:
cosnt element = <h1>hello, world</h1>;
ReactDOM.render({
element,
document.getElementById('root')
});
2
3
4
5
# 更新元素渲染
React元素都是不可变的。当元素被创建之后,是无法改变内容或属性的。
目前更新界面的唯一办法是创建一个新的元素,然后将它传入ReactDOM.render()
方法中,大多数 React 应用只会调用一次ReactDOM.render()
:
function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
ReactDOM.render(element, document.getElementById('root'));
}
setInterval(tick, 1000);
2
3
4
5
6
7
8
9
10
11
==注意==:React 只会更新必要的部分,React DOM 首先会比较元素内容先后的不同,而在渲染过程中只会更新改变了的部分。
# React JSX
React 使用JSX来替代常规的JavaScript。
const element = <h1>Hello, world!</h1>;
这种看起来可能有些奇怪的标签语法既不是字符串也不是 HTML。
它被称为 JSX, 一种 JavaScript 的语法扩展。 我们推荐在 React 中使用 JSX 来描述用户界面。
JSX 是在 JavaScript 内部实现的。
我们知道元素是构成 React 应用的最小单位,JSX 就是用来声明 React 当中的元素。
JSX是一个看起来很像XML的JavaScript语法扩展。
我们不需要一定使用JSX,但它有以下优点:
- JSX执行更快,因为它在编译为JavaScript代码后进行了优化。
- 它是类型安全的,在编译过程中就能发现错误。
- 使用JSX编写模板更加简单快速。
==注意==:由于 JSX 就是 JavaScript,一些标识符像 class 和 for 不建议作为 XML 属性名。作为替代,React DOM 使用 className 和 htmlFor 来做对应的属性。
# 在JSX中迁入表达式
我们可以在 JSX 中使用变量名、表达式、函数或者三元运算符。都写在花括号 {} 中。
const name = 'Josh Perez';
const element = <h1>Hello, {name}, {i == 1 ? 'True!' : 'False'}</h1>;
ReactDOM.render(
element,
document.getElementById('root')
);
2
3
4
5
6
7
在 JSX 语法中,你可以在大括号内放置任何有效的 JavaScript 表达式。
# 样式
React推荐使用内联样式,React会在指定元素数字后自动添加px。
const myStyle = {
fontSize: 100,
color: '#fff'
};
ReactDOM.render(
<h1 style = {myStyle}>hello word</h1>,
document.getElementById('root')
);
2
3
4
5
6
7
8
# 注释
注释需要写在花括号中。
ReactDOM.render(
/*标签外注释 */
<div>
<h1>hello world</h1>
{/*标签内注释...*/}
</div>,
document.getElementById('root')
);
2
3
4
5
6
7
8
# 数组
JSX允许在模板中插入数组,数组会自动展开所有成员。
const arr = [
<h1>hello</h1>,
<h2>world</h2>,
];
ReactDOM.render(
arr,
document.getElementById('root')
);
2
3
4
5
6
7
8
# JSX 防止注入攻击
我们可以安全地在 JSX 当中插入用户输入内容:
const title = response.potentiallyMaliciousInput;
// 直接使用是安全的:
const element = <h1>{title}</h1>;
2
3
React DOM 在渲染所有输入内容之前,默认会进行转义。它可以确保在你的应用中,永远不会注入那些并非自己明确编写的内容。所有的内容在渲染之前都被转换成了字符串。这样可以有效地防止 XSS 攻击。
# JSX表示对象
Babel 会把 JSX 转译成一个名为 React.createElement() 函数调用。以下两种示例代码完全等效:
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
2
3
4
5
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
2
3
4
5
# React组件
# 函数组件和class组件
函数组件:
function Welcome(props) {
return <h1>hello, {props.name}</h1>;
}
2
3
还可以使用ES6的class来定义组件:
class Welcome extends React.Component {
render() {
return <h1>hello, {this.props.name}</h1>;
}
}
2
3
4
5
上述两个组件在 React 里是等效的。
# 函数组件转换成class组件
通过以下五步将 Clock 的函数组件转成 class 组件:
- 创建一个同名的 ES6 class,并且继承于 React.Component。
- 添加一个空的 render() 方法。
- 将函数体移动到 render() 方法之中。
- 在 render() 方法中使用 this.props 替换 props。
- 删除剩余的空函数声明。
# 渲染组件
const element = <Welcome name="weekdawn"/>;
当React元素为用户自定义组件时,它会将JSX所接收的属性以及子组件转换为props
传递给组件。
function Welcome(props) {
return <h1>hello, {props.name}</h1>;
}
const element = <Welcome name="weekdawn"/>;
ReactDOM.render(
element,
document.getElementById('root')
);
2
3
4
5
6
7
8
以上实例中 name 属性通过 props.name 来获取。
==注意,在添加属性时, class 属性需要写成 className ,for 属性需要写成 htmlFor ,这是因为 class 和 for 是 JavaScript 的保留字。
# 复合组件
function Name(props) {
return <h1>网站名称: {props.name}</h1>;
}
function Url(props) {
return <h1>地址: {props.url}</h1>;
}
function App(props) {
return (
<div>
<Name name='weekdawn' />
<Url url='http://weekdawn.github.io' />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root');
);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Props 的只读性
State 与 props 类似,但是 state 是私有的,并且完全受控于当前组件。
组件无论是使用函数声明还是通过 class 声明,都决不能修改自身的 props。
React 非常灵活,但它也有一个严格的规则:
所有 React 组件都必须像纯函数一样保护它们的 props 不被更改。
# React State
React里,只需要更新组件的state,然后根据新的state从新渲染用户界面(不需要操作DOM)。
# 向class组件中添加局部的state
以下示例创建一个名称扩展为React.Componen
t的ES6类,在render()
方法中使用this.state
来修改当前的时间。添加一个类构造函数来初始化状态this.state
,class组件应始终使用props
调用基础构造函数。
class Clock extends React.Component {
// 类构造函数
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>hello, world</h1>
<h2>现在是 {this.state.date.toLocaleTimeString()}</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 将生命周期方法添加到class中
在具有许多组件的应用程序中,在销毁时释放组件所占用的资源非常重要。
每当 Clock 组件第一次加载到 DOM 中的时候,我们都想生成定时器,这在 React 中被称为挂载。
同样,每当 Clock 生成的这个 DOM 被移除的时候,我们也会想要清除定时器,这在 React 中被称为卸载。
我们可以在组件类上声明特殊的方法,当组件挂载或卸载时,来运行一些代码:
class Clock extends React.Component {
// 类构造函数
constructor(props) {
super(props);
this.state = {date: new Date()};
}
// 生命周期钩子:挂载
componentDidMount() {
this.timerID = setInterval(() => this.tick(), 100);
}
// 生命周期钩子:卸载
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>hello, world</h1>
<h2>现在是 {this.state.date.toLocaleTimeString()}</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
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
# 正确使用State
关于setState()
:
- 不要直接修改
state
,而是应该使用setstate()
。
// Wrong
this.state.comment = 'Hello';
// Correct
this.setState({comment: 'Hello'});
2
3
4
- 构造函数是唯一可以给
this.state
赋值的地方。 - state的更新可能是异步的,最好让
setstate()
接收一个函数而不是对象。
出于性能考虑,React
可能会把多个 setState()
调用合并成一个调用。
因为 this.props
和 this.state
可能会异步更新,所以你不要依赖他们的值来更新下一个状态。
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
2
3
4
5
6
7
8
- state的更新会被合并,可以分别调用
setState()
来单独的更新子属性。
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
这里的合并是浅合并,所以 this.setState({comments})
完整保留了 this.state.posts
, 但是完全替换了 this.state.comments
。
# 数据自顶向下流动
父组件或子组件都不能知道某个组件是有状态还是无状态,并且它们不应该关心某组件是被定义为一个函数还是一个类。
这就是为什么称 state
为局部的或是封装的的原因。除了拥有并设置了它的组件,其他组件都无法访问。
组件可以选择把它的state
作为props
向下传递到它的子组件中:
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
这对于自定义组件同样适用:
<FormattedDate date={this.state.date} />
FormattedDate
组件会在其props中接收参数date
,但是组件本身无法知道它是来自于Clock
的state
,或是Clock的props
,还是手动输入的:
function FromattedDate(props) {
return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}
2
3
如果你把一个以组件构成的树想象成一个 props
的数据瀑布的话,那么每一个组件的 state
就像是在任意一点上给瀑布增加额外的水源,但是它只能向下流动。
在 React
应用程序中,组件是有状态还是无状态被认为是可能随时间而变化的组件的实现细节。
我们可以在有状态组件中使用无状态组件,也可以在无状态组件中使用有状态组件。
# React Props
state
和 props
主要的区别在于 props
是不可变的,而 state
可以根据与用户交互来改变。这就是为什么有些容器组件需要定义 state
来更新和修改数据。 而子组件只能通过 props
来传递数据。
# 使用Props
以下实例演示了如何再组件中使用props:
function HelloMsg(props) {
return <h1>hello, {props.name}</h1>;
}
const element = <HelloMsg name="weekdawn" />;
ReactDOM.render(
element,
document.getElementById('root')
);
2
3
4
5
6
7
8
9
实例中那么属性通过props.name来获取。
# 默认props
还可以通过组件类的 defaultProps 属性为props设置默认值,如下:
class HelloMsg extends React.Component {
render() {
return (
<h1>hello {this.props.name}</h1>
);
}
}
HelloMsg.defaultProps = {
name: 'weekdawn'
};
const element = <HelloMsg />
ReactDOM.render(
element,
document.getElementById('roos')
);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# State和Props
以下实例演示了如何在应用中组合使用state
和props
。我们可以在父组件中设置state
,并通过在子组件上使用props
将其传递到子组件上。
class WebSite extends React.Component {
constructor() {
super();
this.state = {
name: 'weekdawn',
site: 'http://weekdawn.github.io'
};
}
render() {
return (
<div>
<Name name={this.state.name} />
<Link site={this.state.site} />
</div>
);
}
}
class Name extends React.Component {
render() {
return (
<h1>{this.props.name}</h1>
);
}
}
class Link extends React.Component {
render() {
return (
<a href={this.props.site}>
{this.props.site}
</a>
);
}
}
ReactDOM.render(
<WelSite />,
document.getElementById('root');
);
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
# React 事件处理
React元素的时间处理和DOM类似,但是有一点语法上的不同。
- React 事件的命名采用==小驼峰命名(camelCase)==,而不是纯小写。
- 使用JSX语法时,需要传入一个==函数==作为事件处理函数,而不是一个字符串。 传统HTML:
<button onclick="activateLasers()">
激活按钮
</button>
2
3
React:
<button onClick={activateLasers}>
激活按钮
</button>
2
3
在React中另一个不同点是,不能通过返回false
的方式阻止默认行为。必须显式的使用preventDefault
。比如在传统HTML中阻止链接默认打开一个新页面,比如:
<a href='#' onclick="console.log('clicked'); return false">
点击
</a>
2
3
在React的写法为:
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('clicked');
}
return (
<a href='#' onClick={handleClick}>
点击
</a>
);
}
2
3
4
5
6
7
8
9
10
11
实例中e是一个合成事件。
使用 React 的时候通常你不需要使用 addEventListener
为一个已创建的 DOM 元素添加监听器。你仅仅需要在这个元素初始渲染的时候提供一个监听器。
当你使用 ES6 class 语法定义一个组件的时候,通常的做法是将事件处理函数声明为 class 中的方法。例如,下面的 Toggle 组件会渲染一个让用户切换开关状态的按钮:
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {
isToggleOn: true
};
}
handleClick = () => {
this.setState({
isToggleOn: !this.state.isToggleOn
})
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root');
);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# React 条件渲染
在 React 中,你可以创建不同的组件来封装各种你需要的行为。然后,依据应用的不同状态,你可以只渲染对应状态下的部分内容。
React 中的条件渲染和 JavaScript 中的一样,使用 JavaScript 运算符 if 或者条件运算符去创建元素来表现当前的状态,然后让 React 根据它们来更新 UI。
function UserGreeting(props) {
return <h1>Welcome back</h1>;
}
function GuestGreeting(props) {
return <h1>Please sign up</h1>;
}
function Greeting(props) {
const isLogIn = props.isLogIn;
// 根据条件渲染
if(isLogIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
ReactDOM.render(
<Greeting isLogIn={false} />,
document.getElementById('root')
);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 元素变量
你可以使用变量来储存元素。 它可以帮助你有条件地渲染组件的一部分,而其他的渲染部分并不会因此而改变。
class LoginControl extends React.Component {
constructor(props) {
super(props);
this.state = {
isLogIn: false
}
}
handleLoginClick = () => {
this.setState({
isLogIn: true
});
}
handleLogoutClick = () => {
this.setState({
isLogIn: false
});
}
render() {
const isLogIn = this.state.isLogIn;
// button为元素变量
let button;
if (isLogIn) {
button = <LogoutBtn onClick={this.handleLogoutClick} />
} else {
button = <LoginBtn onClick={this.handleLoginClick} />
}
}
}
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
# 与运算符 &&
通过花括号包裹代码,可以在JSX中迁入任何表达式。包括与运算符
function Mailbox(props) {
const unreadMsg = props.Msg;
const len = unreadMsg.length;
return (
<div>
<h1>hello</h1>
{
len > 0 &&
<h2>
u have {len} unread Msg.
</h2>
}
</div>
);
}
const message = ['React', "Re: React", "Re:Rf: React"];
ReactDOM.render(
<Mailbox unreadMsg={message} />,
document.getElementById('root')
);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
在JavaScript
中,true && expression
总是返回 expression
,而false && expression
总是返回false
。因此如果条件是true
,就会被渲染。
# 三目运算符
条件渲染的另一种方法是使用JavaScript的条件运算符:condition ? true : false
例如:
render() {
const isLogin = this.state.isLogIn;
return (
<div>
this user is <b>{isLogIn ? 'cuurently' : 'not'}</b>logged in.
</div>
);
}
2
3
4
5
6
7
8
或者:
render() {
const isLogIn = this.state.isLogIn;
return (
<div>
{
isLogIn
? <LogOutBtn />
: <LogInBtn />
}
</div>
):
}
2
3
4
5
6
7
8
9
10
11
12
# 阻止组件渲染
在极少数情况下,你可能希望隐藏组件,即使它已经被其他组件渲染。你可以让render方法字节返回null,而不进行任何渲染。
function WarningBanner(props) {
if (!props.warn) {
return null;
}
return (
<div className="warning">
警告!
</div>
);
}
2
3
4
5
6
7
8
9
10
# React 列表 & Keys
我们可以使用JavaScript的map()方法来创建列表。
当你创建一个元素时,必须包括一个特殊的 key 属性。
实例:
function Numlist(props) {
const nums = props.nums;
const listItems = nums.map((num) => {
// 给每个列表元素分配一个 key 属性
<li key={num.toString()}>{num}</li>
});
return (
<ul>{listItems}</ul>
);
}
const nums = [1, 2, 3, 4, 5];
React.render(
<Numlist nums={nums} />,
document.getElementById('root')
);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Keys
一个元素的 key 最好是这个元素在列表中拥有的一个独一无二的字符串。通常,我们使用来自数据的 id 作为元素的 key。
当元素没有确定的id时,可以使用序列号作为key;
const todoItems = todos.map((todo, index) => {
<li key={index}>
{todo.text}
</li>
});
2
3
4
5
# 用keys提取组件
元素的 key 只有在它和它的兄弟节点对比时才有意义。
function ListItem(props) {
return <li>{props.value}</li>
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) => {
<ListItem key={number.toString} value={number} />
})
return(
<ul>
{listItems}
</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
数组元素中使用的 key 在其兄弟之间应该是独一无二的。然而,它们不需要是全局唯一的。当我们生成两个不同的数组时,我们可以使用相同的键。
# 在JSX中迁入map()
function NumberList(props) {
const numbers =props.numbers;
return (
<ul>
{numbers.map((number) => {
<ListItem key={number.toString} value={number} />
})}
</ul>
);
}
2
3
4
5
6
7
8
9
10
这么做有时可以使你的代码更清晰,但有时这种风格也会被滥用。就像在 JavaScript 中一样,何时需要为了可读性提取出一个变量,这完全取决于你。但请记住,如果一个 map() 嵌套了太多层级,那你就可以提取出组件。
# React 组件 API
React组件API有如下7个方法:
- 设置状态:setState
- 替换状态:replaceState
- 设置属性:setProps
- 替换属性:replaceProps
- 强制更新:forceUpdate
- 获取DOM节点:findDOMNode
- 判断组件挂载状态:isMounted
# 设置状态:setState
setState(object nextState[, function callback])
- nextState,将要设置的新状态,该状态回合当前的state合并
- callback,可选参数,回调函数。该函数会在setState设置成功,且组件重新渲染后调用。 合并nextState和当前state,并重新渲染组件。setState是React事件处理函数中和请求回调函数中触发UI更新的主要方法。
# 关于setSate
- 不能在组件内部通过this.state修改状态,因为该状态会在调用setState后被替换。
- setState()并不会立即改变this.state,而是创建一个即将处理的state。setState()并不一定是同步的,为了提升性能React会在批量执行state和DOM的渲染。
- setState()总是会出发一次组件重绘,除非在shouldComponentUpdate()中实现了一些条件渲染逻辑。
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
clickCount: 0
};
}
handleClick = () => {
this.setState((state) => {
return {
clickCount: state.clickCount + 1
};
})
}
render() {
return (
<h2 onClick={this.handleClick}>点我!点击次数为:{this.state.clickCount}</h2>
);
}
}
ReactDOM.render(
<Counter />,
document.getElementById('root')
);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 替换状态:replaceState
replaceState(object nextState[, function callback])
- nextState,将要设置的行状态,该状态会替换当前的state。
- callback,可选参数,回调函数。该函数会在replaceState设置成功,且组件重新渲染后调用。 repalceState()方法与setState()类似,但是方法只会保留nextState中状态,原state不在nextState中的状态都会被删除。
# 设置属性:setProps
setProps(object nextProps[, function callback])
- nextProps,将要设置的新属性,该状态会和当前的props合并。
- callback,可选参数,回调函数。该函数会在setProps设置成功,且组件重新渲染后调用。
设置组件属性,并重新渲染组件。
props相当于组件的数据流,它总是会从父组件向下传递至所有的子组件中。当和一个外部的JavaScript应用集成时,我们可能会需要向组件传递数据或通知Reac.render()组件需要重新渲染,可以使用serProps()。
更新组件,我可以在节点上再次调用React.render(),也可以通过setProps()方法改变组件属性,出发组件重新渲染。
# 替换属性:replaceProps
replaceProps(object nextProps[, function callback])
- nextProps,将要设置的新属性,该属性会替换当前的props。
- callback,可选参数,回调函数。该函数会在replaceProps设置成功,且组件重新渲染后调用。 replaceProps()方法与setProps类似,但它会删除原有 props。
# 强制更新
forceUpdate([function callback])
- callback,可选参数,回调函数。该函数会在组件render()方法调用后调用。
forceUpdate()方法会使组件调用自身的render()方法从新渲染组件,组件的子组件也会调用自己的render()。但是,组件重新渲染时,依然会读取this.props和this.state,如果状态没有改变,那么React只会更新DOM。
forceUpdate()方法适用于this.props和this.state之外的组件重绘(如:修改了this.state后),通过该方法通知React需要调用render()。
一般来说,应该尽量避免使用forceUpdate(),而仅从this.props和this.state中读取状态并由React触发render()调用。
# 获取DOM节点:findDOMNode
DOMElement findDOMNode()
- 返回值:DOM元素DOMElement 如果组件已经挂载到DOM中,该方法返回对应的本地浏览器DOM元素。当render()返回null或false时,this.findDOMNode()也会返回null。从DOM中取值的时候,该方法很有用,如:获取表单字段的值做一些DOM操作。
# 判断组件挂载状态:isMounted
bool isMounted()
- 返回值: true或false,表示组件是否已挂载到DOM中。 isMounted()方法用于判断组件是否已挂载到DOM中。可以使用该方法保证了setState()和forceUpdate()在异步场景下的调用不会出错。
# React 组件生命周期
React 组件的生命周期,可分为三个状态:
- Mounting(挂载):已插入真实DOM
- Updating(更新):正在被重新渲染
- Unmounting(卸载):已移出真实DOM
# 挂载
当组件实例被创建并插入DOM中时,其生命周期调用顺序如下:
- constructor(): 在React组件挂载之前,会调用它的构造函数。
- getDerivedStateFromProps():在调用render方法之前调用,并且在初始挂载及后续更新时都会被调用。
- render():render()方法是class组件中唯一必须实现的方法。
- componentDidMount():在组件挂载后(插入DOM树中)立即调用。 render方法是class组件中唯一必须实现的方法,其他方法可以根据自己的需要来实现。这些方法的详细说明,可以参考官方文档 (opens new window)。
# 更新
每当组件的state或props发生变化时,组件就会更新。
当组件的props或state发生变化时会触发更新。组件更新的生命周期调用顺序如下:
- getDerivedStateFromProps():在
- shouldComponentUpdate():
- render():
- getSnapshotBeforeUpdate():
- componentDidUpdate(): render方法是class组件中唯一必须实现的方法,其他方法可以根据自己的需要来实现。这些方法的详细说明,可以参考官方文档 (opens new window)。
# 卸载
当组件从DOM中移除时,会调用如下方法:
- componentWillUnmount():在组件卸载及销毁之前直接调用。
# React AJAX
React组件的数据可以通过componentDidMount方法中的Ajax来获取,当从服务端获取数据时,可以将数据存储在state中,再用this.setState方法重新渲染UI。
当使用异步加载数据时,在组件卸载前使用componentWillUnmount来取消未完成的请求。
class UserGist extends React.Component{
constructor(props) {
super(props);
this.state = {
username: '',
lastGistUrl: ''
}
}
conponentDidMount= () => {
this.serverRequest = $.get(this.props.sourcr, (reselt) => {
let lastGist = result[0];
this.setState({
username: lastGist.owner.login,
lastGistUrl:lastGist.html_url
});
})
}
componentWillUnmount() {
this.serverRequest.abort();
}
render() {
return (
<div>
{this.state.username} 用户最新的Gist共享地址:
<a href={this.state.lastGistUrl}>{this.state.lastGistUrl}</a>
</div>
);
}
}
ReactDOM.render(
<UserGist source='https://api.github.com/users/octocat/gists' />,
document.getElementById('root')
);
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
# React Ref
React支持一种非常特殊的属性Ref,你可以用来绑定到render()输出的任何组件上。
# 使用方法
绑定一个ref属性到render的返回值上:
<input ref="myInput" />
在其它代码中,通过this.refs获取支撑实例:
let input = this.refs.myInput;
let inputValue = input.value;
let inputReact = input.getBoundingClientRect();
2
3
# 完整实例:
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}
/>
</div>
);
}
}
ReactDOM.render(
<MyComponent />,
document.getElementById('root')
);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
react 支持三种方式获取 ref,传统的字符串 ref,React.createRef(),回调函数,第一种需要从 refs 上去读取,第二种需要访问 myRef.current,回调函数写法:
<input type="text" ref={(node) => {this.myRef = node}} />