React 的 Class 組件


寫在前面

在 React 中,定義組件的方式有兩種,一個是 class 類組件,一個是函數組件。class 類組件的實現相比於函數組件要復雜。

1. return React 元素

React 組件必須是返回 React 元素的物件,因此無論是函數組件還是類組件都必須有 return React元素

在 class 類組件的返回 React 元素的位置是在 render() 函數中,也就是說,類組件中必須有一個 render() 函數,在 render() 函數中必須有 return 的值,這個值必須是虛擬 DOM(React 元素)。

class xxx extends React.Component{
      render(){
            return (React組件)
      }
}

無論是函數組件還是類組件,return 的 React 元素的語法必須是由一個標簽包裹起來的所有虛擬 DOM 內容,返回多個獨立的標簽是錯誤的,如下:

render(){
      return(
            <div> first </div>
            <div> second </div>
      )   //會報錯
}

解決此種錯誤的方式有兩種,一種是使用一個 div 標簽將其包裹起來,另外一種方式就是使用 React 提供的 <React.Fragment> 將其包裹起來,但是其渲染到頁面時是不會有 <React.Fragment>的,如下:

render(){
      return(
            <React.Fragment>
                  <div> first </div>
                  <div> second </div>
            </React.Fragment>
      )   
}

上述的 <React.Fragment> 還有一種簡寫方式 <>,如下:

render(){
      return(
            <>
                  <div> first </div>
                  <div> second </div>
            </>
      )   
}

2. 兩種創建 class 組件的方式

在 React 中有兩種創建 class 組件的方式,區分這兩種方式的原因是 js 語法版本的不同。在 ES5 時代,沒有 class 這個概念,因此 React 就提供了創建 class 類組件的函數接口,如下:

import React from 'react'
const A = React.createClass({
      render(){
            return(
                  <div>Hello Word</div>
            )
      }
})
export default A

在新的 ES6 語法出來后,支持了 class。因此,用 ES6 語法定義的類組件的語法如下:

import React from 'react'
class A extends React.Component{
      constructor(props){
            super(props);
      }
      render(){
            return (
                  <div>Hello Word</div>
            )
      }
}
export default A

目前大部分瀏覽器都支持了 ES6 語法,因此都在使用 class 語法定義 React 的類組件,對於一些不支持 ES6 語法的瀏覽器,可以使用 webpack + babel 將 ES6 語法轉為 ES5 語法。

3. props 外部數據

3.1 外部傳入 props

外部數據是怎么傳入到組件的?首先,無論是 React 還是 Vue ,組件的使用都是作為自定義 xml 標簽的方式使用的,如 <Person/>,那么在使用組件時向組件傳遞數據信息就是通過 xml 中的標簽內的屬性值傳遞的,如 <Person name="test" age="{18}">hi</Person>,那么 {name:'test', age: 18, children:'hi'} 就會作為 Person 組件的外部數據對象 props 傳遞給 Person 組件。也就是說 <Person> 組件的 props 對象為:

props = {
      name: 'test',
      age: 18,
      children: 'hi'  //React 中的 props 默認有一個 children 屬性,用於接收組件中間的內容
}

3.2 類內初始化 props

props 在類組件內的初始化是在 constructor 函數中進行的,如下:

constructor(props){
      super(props); // 會將 props 掛在 this 上
}

如果 class 組件中的 constructor 函數中只有上述初始化 props 的三行的形式時,可以省略不寫 constructor,上述三行是默認的。但如果在 constructor 函數中有其他語句時,上述三行就必須寫完整。

Class 組件應該始終使用 props 參數來調用父類的構造函數,因此在類中寫 constructor 時就必須是上述傳參的形式。

constructor(props){
      super(props);
      this.state = {
            n: 0
      }
}

3.3 讀取 props

在 props 初始化完成后,this.props 變量就保存了 props 外部數據對象的地址,在 class 類組件內部就可以通過 this.props 變量訪問外部數據。如下:

render(){
      return(
            <div>
                  姓名是:{this.props.name}
                  年齡為:{this.props.age}
                  <div>
                        {this.props.children}
                  </div>
            </div>
      )
}

3.4 寫 props

組件的 props 外部數據一般是由其父組件的 state 內部數據傳遞過去的,因此在組件內不允許修改 props,想要修改必須通知創建該數據的父元素進行修改。

3.5 props 的作用

1. 接收外部的數據
數據只能讀不能寫,外部數據只能由父組件傳遞

2. 接受外部的函數
在恰當的時機調用該函數,該函數也一般是父組件的函數

4. state 內部數據

4.1 初始化 state 內部數據

每個組件都有自己獨立的內部數據,因此在類組件中,state 內部數據是放在構造函數中作為私有屬性定義的

class Welcome extends React.Component{
  
  /*初始化*/
  constructor(props){
    super(props);
    this.state = {
      n: 0
    }
  }
  /*初始化*/
  
  render(){
    return (
      <div>
      
       /*讀數據*/
      	n: {this.state.n}
      /*讀數據*/
  
      </div>
    )
  }
}

4.2 讀 state(this.state)

讀 state 就是上面的在 render() 函數中使用 this.state 的方式讀取。

4.3 寫 state(this.setState())

React 的寫內部數據 state 就比較繞腦了,我們在前面有介紹,React 是不會像 Vue 一樣對數據進行監聽,並在數據進行變化時刷新視圖的,React 采取的方式是,在數據變化需要更新視圖時,重新調用渲染視圖的 render 函數,將視圖重新渲染。因此需要手動調用 render 函數。

但有了 babel-loader 以后,手動 render 的過程被 babel-loader 做了,babel 提供給 jsx 一些可以自動調用 render 的 API,其中的 寫 state 的 API:setState() 就是一個典型的用在類組件中寫內部數據的接口,調用該接口,會自動 render。

this.setState(???, fn) 這里的第一個參數可以是新的 state 對象,也可以是一個函數。第二個參數是一個回調函數,會在 setState 成功后回調該函數。

this.setState() 函數會自動將新 state 與舊 state 進行 一級合並,因此只需要傳入新 state 中修改的部分屬性即可。

class Welcome extends React.Component{
  
  /*初始化*/
  constructor(props){
    super(props);
    this.state = {
      n: 0,
      m: 0
    }
  }
  /*初始化*/
  
  /*寫state*/
  add(){
    //this.state.n += 1; 該種方式不行,React 不會自動render,必須使用特定的 API
    /*
    this.state.n += 1;
    this.setState(this.state);
    *///該種方式不推薦,最好不要改變以前的對象,當數據改變時產生一個新的對象,
    //最好遵循數據不可變原則,新對象容納新的數據。
    
    this.setState({n: this.state.n+1});  // m 會自動 merge
  }
  /*寫state*/
  
  render(){
    return (
      <div>
      
       /*讀數據*/
      	n: {this.state.n}
       /*讀數據*/
  
       <button onClick={()=>this.add()}>+1</button>			
  
      </div>
    )
  }
}

PS補充:
實際上,setState 函數是一個異步函數,不是語句執行時就立即執行的,因此會受到一些誤解,如:

add(){
  this.setState({n: this.state+1});
  console.log(this.state.n);//得到的不是加一后的值,是舊的state,因為 setState 是異步的
  //不會立馬執行,而是在其所在作用域的語句都執行完后再調用執行,因此是先輸出再調用的
}

異步函數 setState 的調用時間不確定,因此前端大佬們都統一使用了一種新的方式 setState,就是使用函數

add(){
  this.setState(state=>{
    return {n: this.state+1});
  }//此處的state是傳遞過來的舊的state對象,返回一個新的對象
                
  this.setState(state=>{
    const n = state.n + 1;
    console.log(n);//要想輸出是修改后的,就直接先修改,再輸出修改的值,這才是正確的修改得到的值
    //return {n:n};當鍵和值名字一樣時可簡寫
    return {n}
  })
}

這種使用函數的方式是在改變的過程中輸出改變,而不是在異步函數語句后輸出改變,不會混淆,因此,當數據簡單時直接用對象,當復雜時用函數

5. 生命周期鈎子

所謂生命周期鈎子,無論是 react 還是 vue 中的鈎子,是指一些特定的函數,這些特定函數會在特定的時間被自動調用。

5.1 componentWillReceiveProps

該鈎子是在外部屬性 props 里的數據變化時被自動調用的特殊函數,componentWillReceiveProps(newProps, nextContent),第一個參數為新的 props。

該鈎子已經被棄用,更名為 UNSAFE_componentWillReceiveProps。因此平常不用,知道有這個鈎子就行。

5.2 constructor

React 組件在創建的時候會調用 constructor() 函數進行數據的初始化,初始化 props 和 state。這里的初始化 state 的語句必須是 this.state = ... 的形式,不能使用 this.setState() 的形式初始化 state。

在 constructor() 函數中還有一個作用就是用來為函數綁定 this 的。官網的介紹。如下:

constructor(){
      this.onClick = this.onClick.bind(this);
}
onClick(){
      this.setState({
            n: this.state.n + 1
      })
}

constructor() 函數當只有初始化 props 時,可省略不寫。

5.3 render

該鈎子用於將 React 元素渲染到根 DOM 節點中,必須要返回一個 React元素。

render() 里面可以使用 if / else,問號表達式(?:)、若想用 for 循環用 map()。

5.4 shouldComponentUpdate

該鈎子翻譯過來就是,是否更新組件。該函數返回 true 時表示不會阻止 UI 更新,返回 false 表示阻止 UI 更新。

shouldComponentUpdate(newProps, newState)

其作用就是它允許我們手動判斷是否要進行組件 UI 更新,我們可以根據應用場景靈活地設置返回值,以避免不必要的更新。

如果不想使用該鈎子手動監聽判斷是否進行組件 UI 更新,其實可以使用 React 提供了一種它幫我們自動檢查是否需要重新 render 的判斷。在 render 前對新數據和舊數據的每一個屬性都進行對比,若全都相等就不再 render,就將類組件改為繼承 React.PureComponent如下:

class A extends React.PureComponent{
      //....
}

PureComponent 會在 render 之前對比新 state 和舊 state 的每一個 key,以及新 props 和舊 props 的每一個 key。
如果所有 key 的值全都一樣,就不會 render;如果有任何一個 key 的值不同,就會 render。

5.5 componentDidMount

在元素插入到頁面后執行的代碼,這些代碼依賴 DOM,也就是說這些代碼執行的前提條件就是元素必須已經出現在頁面中了才能正確執行。比如想要獲取一個元素的寬度,則必須等到該元素插入到頁面中才能獲取到。

首次渲染會執行此鈎子。

可以在此處發起 AJAX 請求

5.6 componentDidUpdate

該鈎子是用於放置視圖更新后執行的代碼。

首次渲染不會執行此鈎子

componentDidUpdate(preProps, preState)

也可以在此處發起 AJAX 請求

5.7 componentWillUnmount

組件將要被移除頁面然后被銷毀時執行的代碼。

unmout 的組件會被移出頁面,也會在內存中銷毀,因此就不能再次 mount。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM