React元件三大屬性state,props,refs

2023-06-28 21:00:26

 

1. React元件定義

1.1 函陣列件(Function Components)

函陣列件是一種簡單的定義元件的方式,通過一個JavaScript函數來定義元件。函數接收一個props物件作為引數,並返回一個React元素作為輸出。

 1     <!-- 準備好一個「容器」 -->
 2     <div id="test"></div>
 3     
 4     <!-- 引入react核心庫 -->
 5     <script type="text/javascript" src="../js/react.development.js"></script>
 6     <!-- 引入react-dom,用於支援react操作DOM -->
 7     <script type="text/javascript" src="../js/react-dom.development.js"></script>
 8     <!-- 引入babel,用於將jsx轉為js -->
 9     <script type="text/javascript" src="../js/babel.min.js"></script>
10 
11     <script type="text/babel">
12         //1.建立函數式元件
13         function MyComponent(){
14             console.log(this); //此處的this是undefined,因為babel編譯後開啟了嚴格模式
15             return <h2>我是用函數定義的元件(適用於【簡單元件】的定義)</h2>
16         }
17         //2.渲染元件到頁面
18         ReactDOM.render(<MyComponent/>,document.getElementById('test'))
19         /* 
20             執行了ReactDOM.render(<MyComponent/>)
21                     1.React解析元件標籤,找到了MyComponent元件。
22                     2.發現元件是使用函數定義的,隨後呼叫該函數,將返回的虛擬DOM轉為真實DOM,隨後呈現在頁面中。
23         */
24     </script>

程式碼的中提到了簡單元件。簡單元件(Simple Components):簡單元件通常是功能較為單一、結構簡單的元件,其主要目的是封裝一部分可複用的UI邏輯。它們通常是函陣列件或者使用ES6箭頭函數定義的函陣列件。簡單元件沒有自己的狀態(state),並且通常依賴於父元件傳遞的props來進行渲染。

1.2 類元件(Class Components)

類元件是使用ES6類語法定義的元件。類元件繼承自React.Component類,並通過render()方法返回一個React元素。類元件可以擁有狀態(state)和生命週期方法。

 

 1     <!-- 準備好一個「容器」 -->
 2     <div id="test"></div>
 3     
 4     <!-- 引入react核心庫 -->
 5     <script type="text/javascript" src="../js/react.development.js"></script>
 6     <!-- 引入react-dom,用於支援react操作DOM -->
 7     <script type="text/javascript" src="../js/react-dom.development.js"></script>
 8     <!-- 引入babel,用於將jsx轉為js -->
 9     <script type="text/javascript" src="../js/babel.min.js"></script>
10 
11     <script type="text/babel">
12         //1.建立類式元件
13         class MyComponent extends React.Component {
14             render(){
15                 //render是放在哪裡的?—— MyComponent的原型物件上,供範例使用。
16                 //render中的this是誰?—— MyComponent的範例物件 <=> MyComponent元件範例物件。
17                 console.log('render中的this:',this);
18                 return <h2>我是用類定義的元件(適用於【複雜元件】的定義)</h2>
19             }
20         }
21         //2.渲染元件到頁面
22         ReactDOM.render(<MyComponent/>,document.getElementById('test'))
23         /* 
24             執行了ReactDOM.render(<MyComponent/>
25                     1.React解析元件標籤,找到了MyComponent元件。
26                     2.發現元件是使用類定義的,隨後new出來該類的範例,並通過該範例呼叫到原型上的render方法。
27                     3.將render返回的虛擬DOM轉為真實DOM,隨後呈現在頁面中。
28         *
29     </script>

 

程式碼的中提到了複雜元件。複雜元件(Complex Components):複雜元件通常包含更多的邏輯和狀態管理,可能由多個簡單元件組成。它們通常是類元件或使用Hooks和其他高階特性定義的元件。複雜元件可以處理更多的資料和互動,並且在其內部可能包含自己的狀態。

2. 元件三大屬性之state

2.1 state的定義

在類元件中,可以通過在元件的建構函式中初始化this.state來定義初始狀態。state是一個普通的JavaScript物件,其中包含元件的各個狀態屬性及其初始值(可以部分地理解為類似Vue的響應式資料)。狀態屬性可以根據需要定義多個,並且可以通過this.setState()方法來更新狀態,通過更新元件的state可以更新對應的頁面顯示(重新渲染元件)。

 1     <!-- 準備好一個「容器」 -->
 2     <div id="test"></div>
 3     
 4     <!-- 引入react核心庫 -->
 5     <script type="text/javascript" src="../js/react.development.js"></script>
 6     <!-- 引入react-dom,用於支援react操作DOM -->
 7     <script type="text/javascript" src="../js/react-dom.development.js"></script>
 8     <!-- 引入babel,用於將jsx轉為js -->
 9     <script type="text/javascript" src="../js/babel.min.js"></script>
10 
11     <script type="text/babel">
12         //1.建立元件
13         class Weather extends React.Component{
14             
15             //構造器呼叫1次
16             constructor(props){
17                 console.log('constructor');
18                 super(props)
19                 //初始化狀態
20                 this.state = {isHot:false,wind:'微風'}
21                 //解決changeWeather中this指向問題
22                 this.changeWeather = this.changeWeather.bind(this)
23             }
24 
25             //render呼叫1+n次 1是初始化的那次 n是狀態更新的次數
26             render(){
27                 console.log('render');
28                 //讀取狀態
29                 const {isHot,wind} = this.state
30                 return <h1 onClick={this.changeWeather}>今天天氣很{isHot ? '炎熱' : '涼爽'},{wind}</h1>
31             }
32 
33             //changeWeather呼叫幾次? ———— 點幾次調幾次
34             changeWeather(){
35                 //changeWeather放在Weather的原型物件上,供範例使用
36                 //由於changeWeather是作為onClick的回撥,所以不是通過範例呼叫的,是直接呼叫
37                 //類中的方法預設開啟了區域性的嚴格模式,所以changeWeather中的this為undefined
38                 
39                 console.log('changeWeather');
40                 //獲取原來的isHot值
41                 const isHot = this.state.isHot
42                 //嚴重注意:狀態必須通過setState進行更新,且更新是一種合併,不是替換。
43                 this.setState({isHot:!isHot})
44                 console.log(this);
45 
46                 //嚴重注意:狀態(state)不可直接更改,下面這行就是直接更改!!!
47                 //this.state.isHot = !isHot //這是錯誤的寫法
48             }
49         }
50         //2.渲染元件到頁面
51         ReactDOM.render(<Weather/>,document.getElementById('test'))
52                 
53     </script>

 

以上程式碼是state的使用和修改。其中使用了一個類元件Weather,實現了點選切換天氣文字的效果。其中類元件中必須完成的方法是render方法,render方法會在生成元件範例後,讓元件範例去呼叫,所以this會指向元件範例。而類中的自定義方法,changeWeather()則是作為點選事件的回撥,呼叫者並非是元件範例物件,所以直接使用會出現this指向的問題。這裡有2個解決辦法,1. 如上文顯示,利用bind改變函數指向。2.利用ES6的箭頭函數,讓函數沒有this而去使用上一級的this。

這裡的bind和構造器顯得過於複雜冗餘,在類裡面定義state屬性可以不需要構造器直接定義,函數也用箭頭函數寫出。以下是簡寫形式

 1     class Weather extends React.Component{
 2             //初始化狀態
 3             state = {isHot:false,wind:'微風'}
 4 
 5             render(){
 6                 const {isHot,wind} = this.state
 7                 return <h1 onClick={this.changeWeather}>今天天氣很{isHot ? '炎熱' : '涼爽'},{wind}</h1>
 8             }
 9 
10             //自定義方法————要用賦值語句的形式+箭頭函數
11             changeWeather = ()=>{
12                 const isHot = this.state.isHot
13                 this.setState({isHot:!isHot})
14             }
15         }

這裡注意到,對state的操作都必須用setState來完成,不能直接使用賦值語句去修改。直接修改state會導致react無法監測到資料的變化而不能重新渲染檢視。

2.2 setState方法的使用

2.2.1 為什麼要使用setState?

在React中,要通知React重新渲染介面,我們需要使用setState來更新元件的狀態。React並沒有像Vue那樣實現資料劫持或使用類似於Object.defineProperty或Proxy的方式來自動監聽資料的變化。

通過呼叫setState函數,我們顯式地告訴React元件的狀態已經發生了變化,然後React會根據最新的狀態值來重新渲染元件。這是因為React使用了虛擬DOM的概念,它會比較前後兩個虛擬DOM樹的差異,並只更新必要的部分,以提高效能。

使用this.state直接修改狀態的方式是不推薦的,因為React無法追蹤到這種變化。相反,我們應該使用setState函數來更新狀態,這樣React才能捕捉到狀態的變化並進行相應的重新渲染。

總結來說,React確實需要通過setState來通知它資料已經發生了變化,從而觸發重新渲染,而不會自動對資料進行監聽或資料劫持。這種方式可以更好地控制和優化渲染過程,同時提供更好的效能。

2.2.2 引數及用法

setState(stateChange, [callback])------ 物件式的setState 第1個引數stateChange為狀態改變物件(該物件可以體現出狀態的更改) 第2個引數callback是可選的回撥函數, 它在狀態更新完畢、介面也更新後(render呼叫後)才被呼叫。上面所用到的都是隻用了第一個引數。setState修改資料的方式是合併物件而不是替換物件,這個也很好理解,如果不是合併而是替換,每次修改一個屬性值就需要在setState中寫入其他所有屬性值,不符合正常的邏輯。setState對於資料的更新在React事件和元件的生命週期中是非同步的,也就是說無法在setState後馬上獲取新的資料

 1 export class App extends Component {
 2   constructor() {
 3     super()
 4 
 5     this.state = {
 6       message: "Hello World"
 7     }
 8   }
 9   
10   changeText() {
11     // 用法一
12     this.setState({
13       message: "你好啊"
14     })
15     console.log(this.state.message) // Hello World
16   }
17 
18   render() {
19     const { message } = this.state
20     return (
21       <div>
22         <h2>{message}</h2>
23         <button onClick={() => {this.changeText()}}>按鈕</button>
24       </div>
25     )
26   }
27 }

以上程式碼顯示出,列印結果仍然是未修改之前的值。而使用setState的回撥就可以存取修改後的state。

 1 export class App extends Component {
 2   constructor() {
 3     super()
 4 
 5     this.state = {
 6       message: "Hello World"
 7     }
 8   }
 9   
10   changeText() {
11     // 引數二回撥函數可以保證拿到的資料是更新後的
12     this.setState({ message: "你好啊" }, () => {
13       console.log(this.state.message) // 你好啊
14     })
15   }
16 
17   render() {
18     console.log("render函數執行")
19     const { message } = this.state
20     return (
21       <div>
22         <h2>{message}</h2>
23         <button onClick={() => {this.changeText()}}>按鈕</button>
24       </div>
25     )
26   }
27 }

2.2.3 如何使用setState?

  • 物件方式是函數方式的簡寫方式
  • 如果新狀態不依賴於原狀態 ===> 使用物件方式
  • 如果新狀態依賴於原狀態 ===> 使用函數方式
  • 如果需要在setState()後獲取最新的狀態資料, 在第二個callback函數中讀取

2.2.4 為什麼setState要設定成非同步的?

原因一: setState設計為非同步,可以顯著的提升效能

React中的更新機制是基於批次更新的原理。在React中,當觸發多個狀態更新時,React會將這些更新收集起來,並將它們放入一個任務佇列中。然後,在下一個時間段(例如事件迴圈的末尾)React會批次處理任務佇列中的所有更新。用setState會讓render函數重新執行, 如果每次呼叫 setState都進行一次更新,那麼意味著render函數會被頻繁呼叫,介面重新渲染,這樣效率是很低的。

這種批次更新的機制有助於提高效能。通過將多個更新合併為單個更新,可以減少不必要的元件重新渲染次數,以及避免重複計算和佈局操作。同時,這種批次更新機制也可以避免UI閃爍或不一致的問題,因為所有更新都會一起應用於元件。這意味著,即使在一個時間段內多次呼叫setState更新元件狀態,React也會在適當的時機將這些更新合併為單個更新,並在批次處理過程中進行統一的更新操作。這樣,React可以在一個更高效的上下文中處理狀態變化並更新UI。

總結起來,React的更新機制是基於批次更新的原理,它通過將多個更新收集起來,並在適當的時機進行批次處理,以提高效能和避免不必要的重新渲染。這種機制確保了在一個時間段內的多次狀態更新被合併,並在統一的更新過程中應用於元件。

例如下面程式碼, 當點選按鈕時, render函數只會執行一次, 由此可見是有等待多個更新再進行批次處理的

 1 export class App extends Component {
 2   constructor() {
 3     super()
 4 
 5     this.state = {
 6       message: "Hello World"
 7     }
 8   }
 9   
10   changeText() {
11     this.setState({
12       message: "你"
13     })
14     this.setState({
15       message: "你好"
16     })
17     this.setState({
18       message: "你好啊"
19     })
20   }
21 
22   render() {
23     console.log("render函數執行")
24     const { message } = this.state
25     return (
26       <div>
27         <h2>{message}</h2>
28         <button onClick={() => {this.changeText()}}>按鈕</button>
29       </div>
30     )
31   }
32 }

 

原因二: 如果同步更新了state,但是還沒有執行render函數,那麼state和props不能保持同步

在React中,元件的render函數負責根據當前的state和props生成對應的UI。當狀態更新時,React會觸發元件的重新渲染,即執行render函數以生成新的UI。

然而,當我們直接修改state的值而沒有呼叫setState函數時,React並不會立即檢測到狀態的變化,也不會自動觸發重新渲染。這意味著,如果在同步更新state後立即存取props,那麼props的值可能仍然是舊的值,與新的state不同步。

為了確保state和props的同步性,我們應該遵循React的更新機制,即使用setState函數來更新狀態。通過setState,React會在適當的時機檢測到狀態的變化,並在下一次渲染時將新的state與props同步。

例如,在一個元件中,有一個名為message的資料被展示在頁面上,並傳遞給子元件進行展示。然後通過呼叫setState同步地修改了message的值。如果在同步的修改完成後,message的值被改變了,但後續的程式碼中出現了報錯的情況,這時候進行偵錯時會發現頁面中的message值被修改,而傳遞給子元件的message並沒有被修改,導致了元件的state和props中的資料不一致。

3. 元件三大屬性之props

3.1 props定義

Props(屬性)是在React中用於傳遞資料和設定資訊的一種機制。元件可以通過props接收從父元件傳遞下來的資料,這樣可以將資料從一個元件傳遞到另一個元件,實現元件之間的通訊。

Props具有以下主要用途:

  1. 資料傳遞:通過props,父元件可以將資料傳遞給子元件。子元件可以使用這些資料來渲染自己的內容或執行其他操作。
  2. 設定資訊:通過props,可以向元件傳遞設定資訊,以便根據不同的設定渲染不同的內容或實現不同的行為。
  3. 引數設定:通過props,可以向元件傳遞引數,使元件能夠根據傳遞的引數來決定其行為或呈現的樣式。
  4. 動態資料更新:當props的值發生變化時,元件可以根據新的props值進行更新和重新渲染,從而實現動態資料的展示。
  5. Props的使用可以使元件更具靈活性和可重用性,因為元件可以根據傳遞的不同props來呈現不同的結果。同時,通過明確定義props的型別,可以提高程式碼的可讀性和可維護性,並提供型別安全性。

下面程式碼是類元件裡props的基本使用,通過在標籤上寫屬性來完成props的傳值,在元件內部使用this.props接收

 1         class Person extends React.Component{
 2             render(){
 3                 // console.log(this);
 4                 const {name,age,sex} = this.props
 5                 return (
 6                     <ul>
 7                         <li>姓名:{name}</li>
 8                         <li>性別:{sex}</li>
 9                         <li>年齡:{age+1}</li>
10                     </ul>
11                 )
12             }
13         }
14         //渲染元件到頁面
15         ReactDOM.render(<Person name="jerry" age={19}  sex="男"/>,document.getElementById('test1'))
16         ReactDOM.render(<Person name="tom" age={18} sex="女"/>,document.getElementById('test2'))
17 
18         const p = {name:'老劉',age:18,sex:'女'}
19         // console.log('@',...p);
20         // ReactDOM.render(<Person name={p.name} age={p.age} sex={p.sex}/>,document.getElementById('test3'))
21         ReactDOM.render(<Person {...p}/>,document.getElementById('test3'))

在使用JS編寫react程式碼時,我們也可以通過一些方式完成props的型別檢查和一些限制,引入prop-types,用於對元件標籤屬性進行限制。其中propTypes 用於限制props的型別,而defaultProps用於給出一些預設值

 1 <script type="text/javascript" src="../js/prop-types.js"></script>
 2 
 3     <script type="text/babel">
 4         //建立元件
 5         class Person extends React.Component{
 6             render(){
 7                 // console.log(this);
 8                 const {name,age,sex} = this.props
 9                 //props是唯讀的
10                 //this.props.name = 'jack' //此行程式碼會報錯,因為props是唯讀的
11                 return (
12                     <ul>
13                         <li>姓名:{name}</li>
14                         <li>性別:{sex}</li>
15                         <li>年齡:{age+1}</li>
16                     </ul>
17                 )
18             }
19         }
20         //對標籤屬性進行型別、必要性的限制
21         Person.propTypes = {
22             name:PropTypes.string.isRequired, //限制name必傳,且為字串
23             sex:PropTypes.string,//限制sex為字串
24             age:PropTypes.number,//限制age為數值
25             speak:PropTypes.func,//限制speak為函數
26         }
27         //指定預設標籤屬性值
28         Person.defaultProps = {
29             sex:'',//sex預設值為男
30             age:18 //age預設值為18
31         }
32         //渲染元件到頁面
33         ReactDOM.render(<Person name={100} speak={speak}/>,document.getElementById('test1'))
34         ReactDOM.render(<Person name="tom" age={18} sex=""/>,document.getElementById('test2'))
35 
36         const p = {name:'老劉',age:18,sex:''}
37         // console.log('@',...p);
38         // ReactDOM.render(<Person name={p.name} age={p.age} sex={p.sex}/>,document.getElementById('test3'))
39         ReactDOM.render(<Person {...p}/>,document.getElementById('test3'))
40 
41         function speak(){
42             console.log('我說話了');
43         }
44     </script>

以上的props的書寫過於複雜,對於JS的類,可以使用static寫在class內部。props也可以通過建構函式傳參獲取,以下是props的簡寫形式

 1 class Person extends React.Component{
 2 
 3             constructor(props){
 4                 //構造器是否接收props,是否傳遞給super,取決於:是否希望在構造器中通過this存取props
 5                 // console.log(props);
 6                 super(props)
 7                 console.log('constructor',this.props);
 8             }
 9 
10             //對標籤屬性進行型別、必要性的限制
11             static propTypes = {
12                 name:PropTypes.string.isRequired, //限制name必傳,且為字串
13                 sex:PropTypes.string,//限制sex為字串
14                 age:PropTypes.number,//限制age為數值
15             }
16 
17             //指定預設標籤屬性值
18             static defaultProps = {
19                 sex:'男',//sex預設值為男
20                 age:18 //age預設值為18
21             }
22             
23             render(){
24                 // console.log(this);
25                 const {name,age,sex} = this.props
26                 //props是唯讀的
27                 //this.props.name = 'jack' //此行程式碼會報錯,因為props是唯讀的
28                 return (
29                     <ul>
30                         <li>姓名:{name}</li>
31                         <li>性別:{sex}</li>
32                         <li>年齡:{age+1}</li>
33                     </ul>
34                 )
35             }
36         }

除了類元件以外,函陣列件也可以使用props完成基本的傳值

 1 function Person (props){
 2             const {name,age,sex} = props
 3             return (
 4                     <ul>
 5                         <li>姓名:{name}</li>
 6                         <li>性別:{sex}</li>
 7                         <li>年齡:{age}</li>
 8                     </ul>
 9                 )
10         }
11         Person.propTypes = {
12             name:PropTypes.string.isRequired, //限制name必傳,且為字串
13             sex:PropTypes.string,//限制sex為字串
14             age:PropTypes.number,//限制age為數值
15         }
16 
17         //指定預設標籤屬性值
18         Person.defaultProps = {
19             sex:'男',//sex預設值為男
20             age:18 //age預設值為18
21         }
22         //渲染元件到頁面
23         ReactDOM.render(<Person name="jerry"/>,document.getElementById('test1'))

3.2 props是唯讀的

在React中,props是用於從父元件向子元件傳遞資料的一種機制。props被設計為唯讀的,即子元件無法直接修改傳遞給它的props。

這是因為React採用了單向資料流的原則,父元件作為資料的擁有者和管理者,通過props向子元件傳遞資料。子元件只能讀取父元件傳遞的props資料,並基於這些資料進行渲染和互動。

保持props不可修改有以下好處:

  1. 資料單一來源:通過限制props的可修改性,確保資料的源頭始終來自於父元件,避免了資料來源的混亂和不一致。
  2. 可預測性:當props是唯讀的時候,可以更容易地追蹤和理解資料的流動,減少了副作用和意外的資料修改。
  3. 元件隔離性:通過限制子元件對props的修改,增強了元件的獨立性和封裝性,使其更易於測試、維護和複用。

如果子元件需要修改傳遞給它的資料,可以通過回撥函數、狀態管理庫(如Redux或Mobx)等方式,讓父元件來處理資料的修改,並通過props將修改後的資料傳遞給子元件。

4. 元件三大屬性之refs

4.1 refs的定義

Refs(參照)是一種用於存取元件範例或DOM元素的機制。Refs提供了一種方式,讓我們能夠直接存取元件或DOM元素,並對其進行操作。元件內的標籤可以定義ref屬性來標識自己。

Refs的主要用途包括:

  1. 存取元件範例:通過Refs,我們可以獲取到元件的範例,從而可以直接呼叫元件的方法或存取其屬性。
  2. 操作DOM元素:Refs允許我們存取和操作渲染到頁面上的DOM元素。我們可以使用Refs來獲取DOM節點的參照,並對其進行操作,例如修改樣式、新增事件監聽器等。

Refs的使用方法有3種形式:字串形式,回撥函數形式,createRef。

字串形式:直接在標籤裡寫屬性類似於 ref=「名字」的方式,在元件裡使用this.refs獲取

 1 class Demo extends React.Component{
 2             //展示左側輸入框的資料
 3             showData = ()=>{
 4                 const {input1} = this.refs
 5                 alert(input1.value)
 6             }
 7             //展示右側輸入框的資料
 8             showData2 = ()=>{
 9                 const {input2} = this.refs
10                 alert(input2.value)
11             }
12             render(){
13                 return(
14                     <div>
15                         <input ref="input1" type="text" placeholder="點選按鈕提示資料"/>&nbsp;
16                         <button onClick={this.showData}>點我提示左側的資料</button>&nbsp;
17                         <input ref="input2" onBlur={this.showData2} type="text" placeholder="失去焦點提示資料"/>
18                     </div>
19                 )
20             }
21         }

在程式碼中字串形式的ref非常的簡單易懂,但是react官方的檔案中卻已經不推薦使用字串形式的ref了,並且提出了一些可能存在的問題,主要是與效能方面相關,具體的討論區連結如下:https://github.com/facebook/react/pull/8333#issuecomment-271648615

回撥形式:在ref中使用回撥函數來定義ref,把ref繫結到this上。

回撥形式的Refs(回撥Refs)在React中具有以下幾個優點:

    1. 靈活性:回撥Refs允許我們執行自定義邏輯來處理參照的元件範例或DOM元素。通過回撥函數,我們可以在元件掛載或解除安裝時捕獲參照,並將其儲存在合適的變數中。這使得我們可以根據需要靈活地存取和操作參照。

    2. 型別安全:回撥Refs在TypeScript等靜態型別檢查工具中更具型別安全性。通過使用回撥函數引數的型別註解,我們可以準確地指定參照的型別,以便在編譯時捕獲型別錯誤。

    3. 更好的可讀性:通過將回撥函數直接傳遞給ref屬性,程式碼更加清晰易懂。我們可以直接在回撥函數中存取參照,並在適當的時候將其儲存在元件範例上,以便在其他方法中使用。

    4. React生命週期相容性:回撥Refs與React的生命週期方法整合得更好。我們可以在元件的掛載、更新和解除安裝階段使用回撥Refs,以便在正確的時機進行參照的捕獲和釋放。

    5. 相容未來的API改變:回撥Refs是React推薦的方式,它們在React中被廣泛使用,並且在未來的React版本中仍然會得到支援。使用回撥Refs可以更好地保持程式碼的向後相容性,並使您的程式碼能夠跟隨React的演進而進行適應。

 1     class Demo extends React.Component{
 2             //展示左側輸入框的資料
 3             showData = ()=>{
 4                 const {input1} = this
 5                 alert(input1.value)
 6             }
 7             //展示右側輸入框的資料
 8             showData2 = ()=>{
 9                 const {input2} = this
10                 alert(input2.value)
11             }
12             render(){
13                 return(
14                     <div>
15                         <input ref={c => this.input1 = c } type="text" placeholder="點選按鈕提示資料"/>&nbsp;
16                         <button onClick={this.showData}>點我提示左側的資料</button>&nbsp;
17                         <input onBlur={this.showData2} ref={c=> this.input2 = c } type="text" placeholder="失去焦點提示資料"/>&nbsp;
18                     </div>
19                 )
20             }
21         }

 回撥形式的ref在更新過程中它會被執行兩次,第一次傳入引數 null,然後第二次會傳入引數 DOM 元素。這是因為在每次渲染時會建立一個新的函數範例,所以 React 清空舊的 ref 並且設定新的。傳入的null是為了確保清空舊的ref,而第二次傳入新的元素則是設定ref。

以下程式碼可以用於測試ref回撥的執行次數,在每次執行回撥時都會列印結果

 1         class Demo extends React.Component{
 2 
 3             state = {isHot:false}
 4 
 5             showInfo = ()=>{
 6                 const {input1} = this
 7                 alert(input1.value)
 8             }
 9 
10             changeWeather = ()=>{
11                 //獲取原來的狀態
12                 const {isHot} = this.state
13                 //更新狀態
14                 this.setState({isHot:!isHot})
15             }
16 
17 
18 
19             render(){
20                 const {isHot} = this.state
21                 return(
22                     <div>
23                         <h2>今天天氣很{isHot ? '炎熱':'涼爽'}</h2>
24                         <input ref={(c)=>{this.input1 = c;console.log('@',c);}} type="text"/><br/><br/>
25                         <button onClick={this.showInfo}>點我提示輸入的資料</button>
26                         <button onClick={this.changeWeather}>點我切換天氣</button>
27                     </div>
28                 )
29             }
30         }

圖中的列印結果顯示執行了2次回撥

createRef方法:React.createRef方法可以建立ref並且給元件範例使用,這個方法會建立一個容器,該容器存放的是可以被ref所標識的節點,這個容器是專用的。

 

 1         class Demo extends React.Component{
 2             /* 
 3                 React.createRef呼叫後可以返回一個容器,該容器可以儲存被ref所標識的節點,該容器是「專人專用」的
 4              */
 5             myRef = React.createRef()
 6             myRef2 = React.createRef()
 7             //展示左側輸入框的資料
 8             showData = ()=>{
 9                 console.log(this.myRef);
10                 alert(this.myRef.current.value);
11             }
12             //展示右側輸入框的資料
13             showData2 = ()=>{
14                 alert(this.myRef2.current.value);
15             }
16             render(){
17                 return(
18                     <div>
19                         <input ref={this.myRef} type="text" placeholder="點選按鈕提示資料"/>&nbsp;
20                         <button onClick={this.showData}>點我提示左側的資料</button>&nbsp;
21                         <input onBlur={this.showData2} ref={this.myRef2} type="text" placeholder="失去焦點提示資料"/>&nbsp;
22                     </div>
23                 )
24             }
25         }