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

| 勘誤、提問或許願 |