React Component 规格与生命周期(Life Cycle)

前言

经过前面的努力相信目前读者对于用 React 开发一些简单的组件(Component)已经有一定程度的掌握了,现在我们将更细部探讨 React Component 的规格和其生命周期。

React Component 规格

若读者还有印象的话,我们前面介绍 React 特性时有描述 React 的主要撰写方式有两种:一种是使用 ES6 Class,另外一种是 Stateless Components,使用 Functional Component 的写法,单纯渲染 UI。这边再帮大家复习一下上一个章节的简单范例:

  1. 使用 ES6 的 Class(可以进行比较复杂的操作和组件生命周期的控制,相对于 stateless components 耗费资源)

    1. // 注意组件开头第一个字母都要大写
    2. class MyComponent extends React.Component {
    3. // render 是 Class based 组件唯一必须的方法(method)
    4. render() {
    5. return (
    6. <div>Hello, {this.props.name}</div>
    7. );
    8. }
    9. }
    10. // PropTypes 验证,若传入的 props type 不符合将会显示错误
    11. MyComponent.propTypes = {
    12. name: React.PropTypes.string,
    13. }
    14. // Prop 预设值,若对应 props 没传入值将会使用 default 值,为每个实例化 Component 共用的值
    15. MyComponent.defaultProps = {
    16. name: '',
    17. }
    18. // 将 <MyComponent /> 组件插入 id 为 app 的 DOM 元素中
    19. ReactDOM.render(<MyComponent name="Mark"/>, document.getElementById('app'));
  2. 使用 Functional Component 写法(单纯地 render UI 的 stateless components,没有内部状态、没有实作物件和 ref,没有生命周期函数。若非需要控制生命周期的话建议多使用 stateless components 获得比较好的效能)

    1. // 使用 arrow function 来设计 Functional Component 让 UI 设计更单纯(f(D) => UI),减少副作用(side effect)
    2. const MyComponent = (props) => (
    3. <div>Hello, {props.name}</div>
    4. );
    5. // PropTypes 验证,若传入的 props type 不符合将会显示错误
    6. MyComponent.propTypes = {
    7. name: React.PropTypes.string,
    8. }
    9. // Prop 预设值,若对应 props 没传入值将会使用 default 值
    10. MyComponent.defaultProps = {
    11. name: '',
    12. }
    13. // 将 <MyComponent /> 组件插入 id 为 app 的 DOM 元素中
    14. 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 有以下三种生命周期的状态:

  1. Mounting:已插入真实的 DOM
  2. Updating:正在被重新渲染
  3. Unmounting:已移出真实的 DOM

针对 Component 的生命周期状态 React 也有提供对应的处理方法:

  1. Mounting
    • componentWillMount()
    • componentDidMount()
  2. Updating
    • componentWillReceiveProps(object nextProps):已载入组件收到新的参数时呼叫
    • shouldComponentUpdate(object nextProps, object nextState):组件判断是否重新渲染时呼叫,起始不会呼叫除非呼叫 forceUpdate()
    • componentWillUpdate(object nextProps, object nextState)
    • componentDidUpdate(object prevProps, object prevState)
  3. Unmounting
    • componentWillUnmount()

很多读者一开始学习 Component 生命周期时会觉得很抽象,所以接下来用一个简单范例让大家感受一下 Component 的生命周期。读者可以发现当一开始载入组件时第一个会触发 console.log('constructor');,依序执行 componentWillMountcomponentDidMount ,而当点击文字触发 handleClick() 更新 state 时则会依序执行 componentWillUpdatecomponentDidUpdate

HTML Markup:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <meta name="viewport" content="width=device-width">
  6. <script src="https://fb.me/react-15.1.0.js"></script>
  7. <script src="https://fb.me/react-dom-15.1.0.js"></script>
  8. <title>Component LifeCycle</title>
  9. </head>
  10. <body>
  11. <div id="app"></div>
  12. </body>
  13. </html>

Component 生命周期展示:

  1. class MyComponent extends React.Component {
  2. constructor(props) {
  3. super(props);
  4. console.log('constructor');
  5. this.handleClick = this.handleClick.bind(this);
  6. this.state = {
  7. name: 'Mark',
  8. }
  9. }
  10. handleClick() {
  11. this.setState({'name': 'Zuck'});
  12. }
  13. componentWillMount() {
  14. console.log('componentWillMount');
  15. }
  16. componentDidMount() {
  17. console.log('componentDidMount');
  18. }
  19. componentWillReceiveProps() {
  20. console.log('componentWillReceiveProps');
  21. }
  22. componentWillUpdate() {
  23. console.log('componentWillUpdate');
  24. }
  25. componentDidUpdate() {
  26. console.log('componentDidUpdate');
  27. }
  28. componentWillUnmount() {
  29. console.log('componentWillUnmount');
  30. }
  31. render() {
  32. return (
  33. <div onClick={this.handleClick}>Hi, {this.state.name}</div>
  34. );
  35. }
  36. }
  37. ReactDOM.render(<MyComponent />, document.getElementById('app'));

点击看详细范例

React Component 规格与生命周期

其中特殊处理的函数 shouldComponentUpdate,目前预设 return true。若你想要优化效能可以自己编写判断方式,若采用 immutable 可以使用 nextProps === this.props 比对是否有变动:

  1. shouldComponentUpdate(nextProps, nextState) {
  2. return nextProps.id !== this.props.id;
  3. }

Ajax 非同步处理

若有需要进行 Ajax 非同步处理,请在 componentDidMount 进行处理。以下透过 jQuery 执行 Ajax 取得 Github API 资料当做范例:

HTML Markup:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <meta name="viewport" content="width=device-width">
  6. <script src="https://fb.me/react-15.1.0.js"></script>
  7. <script src="https://fb.me/react-dom-15.1.0.js"></script>
  8. <script src="https://code.jquery.com/jquery-3.1.0.js"></script>
  9. <title>GitHub User</title>
  10. </head>
  11. <body>
  12. <div id="app"></div>
  13. </body>
  14. </html>

app.js

  1. class UserGithub extends React.Component {
  2. constructor(props) {
  3. super(props);
  4. this.state = {
  5. username: '',
  6. githubtUrl: '',
  7. avatarUrl: '',
  8. }
  9. }
  10. componentDidMount() {
  11. $.get(this.props.source, (result) => {
  12. console.log(result);
  13. const data = result;
  14. if (data) {
  15. this.setState({
  16. username: data.name,
  17. githubtUrl: data.html_url,
  18. avatarUrl: data.avatar_url
  19. });
  20. }
  21. });
  22. }
  23. render() {
  24. return (
  25. <div>
  26. <h3>{this.state.username}</h3>
  27. <img src={this.state.avatarUrl} />
  28. <a href={this.state.githubtUrl}>Github Link</a>.
  29. </div>
  30. );
  31. }
  32. }
  33. ReactDOM.render(
  34. <UserGithub source="https://api.github.com/users/torvalds" />,
  35. document.getElementById('app')
  36. );

点击看详细范例

总结

以上介绍了 React Component 规格与生命周期(Life Cycle)的概念,其中生命周期的概念对于初学者来说可能会比较抽象,建议读者跟着范例动手实作。接下来我们将更进一步介绍 React Router 让读者感受一下单页式应用程式(single page application)的设计方式。

延伸阅读

  1. Component Specs and Lifecycle

(image via react-lifecycle

| 勘误、提问或许愿 |