4.2 React Component 规格与生命週期(Life Cycle)
前言
经过前面的努力相信目前读者对于用 React 开发一些简单的元件(Component)已经有一定程度的掌握了,现在我们将更细部探讨 React Component 的规格和其生命週期。
React Component 规格
若读者还有印象的话,我们前面介绍 React 特性时有描述 React 的主要撰写方式有两种:一种是使用 ES6 Class,另外一种是 Stateless Components,使用 Functional Component 的写法,单纯渲染 UI。这边再帮大家複习一下上一个章节的简单範例:
使用 ES6 的 Class(可以进行比较複杂的操作和元件生命週期的控制,相对于 stateless components 耗费资源)
// 注意元件开头第一个字母都要大写
class MyComponent extends React.Component {
// render 是 Class based 元件唯一必须的方法(method)
render() {
return (
<div>Hello, {this.props.name}</div>
);
}
}
// PropTypes 验证,若传入的 props type 不符合将会显示错误
MyComponent.propTypes = {
name: React.PropTypes.string,
}
// Prop 预设值,若对应 props 没传入值将会使用 default 值,为每个实例化 Component 共用的值
MyComponent.defaultProps = {
name: '',
}
// 将 <MyComponent /> 元件插入 id 为 app 的 DOM 元素中
ReactDOM.render(<MyComponent name="Mark"/>, document.getElementById('app'));
使用 Functional Component 写法(单纯地 render UI 的 stateless components,没有内部状态、没有实作物件和 ref,没有生命週期函数。若非需要控制生命週期的话建议多使用 stateless components 获得比较好的效能)
// 使用 arrow function 来设计 Functional Component 让 UI 设计更单纯(f(D) => UI),减少副作用(side effect)
const MyComponent = (props) => (
<div>Hello, {props.name}</div>
);
// PropTypes 验证,若传入的 props type 不符合将会显示错误
MyComponent.propTypes = {
name: React.PropTypes.string,
}
// Prop 预设值,若对应 props 没传入值将会使用 default 值
MyComponent.defaultProps = {
name: '',
}
// 将 <MyComponent /> 元件插入 id 为 app 的 DOM 元素中
ReactDOM.render(<MyComponent name="Mark"/>, document.getElementById('app'));
值得留意的是在 ES6 Class 中 render()
是唯一必要的方法(但要注意的是请保持 render()
的纯粹,不要在里面进行 state
修改或是使用非同步方法和浏览器互动,若需非同步互动请于 componentDidMount()
操作),而 Functional Component 目前允许 return null
值。 喔对了,在 ES6 中也不支援 mixins
複用其他元件的方法了。
React Component 生命週期
React Component,就像人会有生老病死一样有生命週期。一般而言 Component 有以下三种生命週期的状态:
- Mounting:已插入真实的 DOM
- Updating:正在被重新渲染
- Unmounting:已移出真实的 DOM
针对 Component 的生命週期状态 React 也有提供对应的处理方法:
- Mounting
- componentWillMount()
- componentDidMount()
- Updating
- componentWillReceiveProps(object nextProps):已载入元件收到新的参数时呼叫
- shouldComponentUpdate(object nextProps, object nextState):元件判断是否重新渲染时呼叫,起始不会呼叫除非呼叫 forceUpdate()
- componentWillUpdate(object nextProps, object nextState)
- componentDidUpdate(object prevProps, object prevState)
- Unmounting
- componentWillUnmount()
很多读者一开始学习 Component 生命週期时会觉得很抽象,所以接下来用一个简单範例让大家感受一下 Component 的生命週期。读者可以发现当一开始载入元件时第一个会触发 console.log('constructor');
,依序执行 componentWillMount
、componentDidMount
,而当点击文字触发 handleClick()
更新 state
时则会依序执行 componentWillUpdate
、componentDidUpdate
:
HTML Markup:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<script src="https://fb.me/react-15.1.0.js"></script>
<script src="https://fb.me/react-dom-15.1.0.js"></script>
<title>Component LifeCycle</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
Component 生命週期展示:
class MyComponent extends React.Component {
constructor(props) {
super(props);
console.log('constructor');
this.handleClick = this.handleClick.bind(this);
this.state = {
name: 'Mark',
}
}
handleClick() {
this.setState({'name': 'Zuck'});
}
componentWillMount() {
console.log('componentWillMount');
}
componentDidMount() {
console.log('componentDidMount');
}
componentWillReceiveProps() {
console.log('componentWillReceiveProps');
}
componentWillUpdate() {
console.log('componentWillUpdate');
}
componentDidUpdate() {
console.log('componentDidUpdate');
}
componentWillUnmount() {
console.log('componentWillUnmount');
}
render() {
return (
<div onClick={this.handleClick}>Hi, {this.state.name}</div>
);
}
}
ReactDOM.render(<MyComponent />, document.getElementById('app'));
其中特殊处理的函数 shouldComponentUpdate
,目前预设 return true
。若你想要优化效能可以自己编写判断方式,若採用 immutable
可以使用 nextProps === this.props
比对是否有变动:
shouldComponentUpdate(nextProps, nextState) {
return nextProps.id !== this.props.id;
}
Ajax 非同步处理
若有需要进行 Ajax 非同步处理,请在 componentDidMount
进行处理。以下透过 jQuery
执行 Ajax
取得 Github API
资料当做範例:
HTML Markup:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<script src="https://fb.me/react-15.1.0.js"></script>
<script src="https://fb.me/react-dom-15.1.0.js"></script>
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
<title>GitHub User</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
app.js
class UserGithub extends React.Component {
constructor(props) {
super(props);
this.state = {
username: '',
githubtUrl: '',
avatarUrl: '',
}
}
componentDidMount() {
$.get(this.props.source, (result) => {
console.log(result);
const data = result;
if (data) {
this.setState({
username: data.name,
githubtUrl: data.html_url,
avatarUrl: data.avatar_url
});
}
});
}
render() {
return (
<div>
<h3>{this.state.username}</h3>
<img src={this.state.avatarUrl} />
<a href={this.state.githubtUrl}>Github Link</a>.
</div>
);
}
}
ReactDOM.render(
<UserGithub source="https://api.github.com/users/torvalds" />,
document.getElementById('app')
);
总结
以上介绍了 React Component 规格与生命週期(Life Cycle)的概念,其中生命週期的概念对于初学者来说可能会比较抽象,建议读者跟着範例动手实作。接下来我们将更进一步介绍 React Router
让读者感受一下单页式应用程式(single page application)的设计方式。