深入解析React資料傳遞之元件內部通訊

前端攻城小牛發表於2018-12-31

這篇文章主要介紹了React資料傳遞之元件內部通訊的方法,寫的十分的全面細緻,具有一定的參考價值,對此有需要的朋友可以參考學習下。如有不足之處,歡迎批評指正。

深入解析React資料傳遞之元件內部通訊

1. 概述

脫離初級前端一段時間後會發現,寫樣式的時間越來越少,處理資料的時間越來越多。處理資料的過程也就是實現業務邏輯的過程,這在專案中無疑是最重要的。 所以學習前端框架,瞭解完基本語法後,接下來就要學習其如何進行資料傳遞。 Angular 設計之初的一大亮點就是實現了資料的雙向繫結,使用 Vue 一段時間後發現,所謂資料的雙向繫結,元件內部唯一的應用場景就是 form 表單(input,textarea,select, radio),而這種場景下的資料雙向繫結,即便框架內部沒有實現,自己實現起來也非常簡單。明白這一點後感覺之前認為 React 沒有實現資料雙向繫結很 low 的想法很幼稚。 對於 React 的資料傳遞,涉及兩方面的內容: 元件內部的資料傳遞,典型的應用場景包括如何實現 form 表單雙向資料繫結、如何繫結事件; 元件間的資料傳遞。 包括父元件往子元件傳遞資料、子元件往父元件傳遞資料以及兄弟元件之間傳遞資料。 本文先討論元件內部的資料傳遞。

2. 元件內部資料傳遞

React 元件內部通訊主要分為兩部分:資料展示與事件處理。 2.1 資料展示 元件內部資料的展示和更新都是通過 state 來實現的,如果要使用 state 必須使用 ES6 的 class 定義元件。資料更新在雙向資料繫結部分探討,這部分僅討論展示初始化資料。 如果你熟悉 Vue,React 的 state 物件相當於 Vue 的 data 物件 下面是一個純展示資料的示例:

class App extends Component {
 constructor(props) {
 super(props);
 
 // 初始化 state
 this.state = {
  inputValue: "test",
 };
 }
 
 render() {
 // 注意,在 react 中,DOM 元素是物件,所以使用‘()'包住 
 return (
  <div className="App">
  <p>{this.state.inputValue}</p>
  </div>
 );
 }
}//歡迎加入前端全棧開發交流圈一起學習交流:619586920
複製程式碼

在通過 class 定義的 React 元件中,除了生命週期鉤子函式, constructor() 和 render() 著兩個方法也是自動執行的,先執行 constructor() ,執行 constructor() 的同時也是再為 render() 渲染 DOM 做資料準備。 實際上 constructor() 函式是元件生命週期中呼叫的第一個函式。

2.2 事件

2.2.1 與 DOM 中事件的異同

在 React 中處理事件和在 DOM 中處理事件類似,有兩點不同: React 中通過駝峰命名法命名事件,而不是全是小寫字母; 在 JSX 中直接傳遞函式作為事件處理程式,而不是字串。 第 2 點不同有坑,後面細說 舉個例子,HTML中的事件:

<button onclick="activateLasers()">
 Activate Lasers
</button>
複製程式碼

React 中的事件:

// 因為 jsx 中'{}'裡面代表函式表示式,
// 所以傳遞給 onClick 的實際是函式 activateLasers 的函式體部分,
// 因此需要指定 this 指向,不然會報錯
<button onClick={activateLasers}>
 Activate Lasers
</button>
//歡迎加入前端全棧開發交流圈一起學習交流:619586920
複製程式碼

2.2.2 存在的坑

直接傳遞 function 作為 event handler 需要指定函式的執行環境,即需要手動繫結 this ,不然會報 this 為 undefined 的錯。見下面的例子:

class App extends Component {
 constructor(props) {
 super(props);
 this.state = {
  isToggleOn: true,
 };
 //歡迎加入前端全棧開發交流圈一起學習交流:619586920
 // 手動繫結 this
 this.handleClick = this.handleClick.bind(this);
 }
 
 handleClick() {
 // 如果不在 constructor() 方法中手動繫結 this,直接將其作為事件處理程式 this 為 undefined
 console.log(this);
 
 this.setState(prevState => ({
  isToggleOn: !prevState.isToggleOn
 }));
 }
 //歡迎加入前端全棧開發交流圈一起學習交流:619586920
 render() {
 return (
  <div className="App">
  <button onClick={this.handleClick}>
   {this.state.isToggleOn ? "on" : "off"}
  </button>
  </div>
 );
 }
}
複製程式碼

2.2.3 為什麼會有坑

React 官網 說這個鍋要 JS 原生語法來背,其實不盡然,React 實在 JS 語法早已確定的情況下設計了這樣的事件系統,如果一定要有人站出來背鍋,他們五五分吧。

1, JS原生語法存在的問題

JS語法中有這樣的規則:如果將一個函式的函式體(沒有 () )賦值給另一個變數,函式體內部的 this 指向可能會發生變化。會不會變化取決於函式和被賦值的變數是否處於同一個作用域(相同的執行環境)中,但實際使用中,將一個函式賦值給相同作用域的變數沒有意義,那樣的話直接使用那個函式就好,沒必要在賦值給另一個變數。

this 指向不發生改變的沒有意義的例子(為了方便說明,直接使用 var 操作符):

var fn = function () {
 console.log(this);
};
 
var a = fn;
//歡迎加入前端全棧開發交流圈一起學習交流:619586920 
fn(); // window
a(); // window
this 指向發生改變的例子:
 
var fn = function () {
 console.log(this);
};
 
// 將函式體賦值給一個物件的屬性,函式執行時 this 和定義時指向不同
var o = {
 a: fn,
};
//歡迎加入前端全棧開發交流圈一起學習交流:619586920 
fn(); // window
o.a(); // o,即{a:f}
複製程式碼

如果想要在將函式體賦值另一個變數的同時把原函式的 this 指向也一塊賦值過去,就需要在賦值的過程中進行繫結 this 的操作,如下:

var fn = function () {
 console.log(this);
};
 
// fn 在賦值的同時將內部的 this 打包一塊賦值給了 a
var o = {
 a: fn.bind(this),
};
//歡迎加入前端全棧開發交流圈一起學習交流:619586920 
fn(); // window
o.a(); // window
複製程式碼

通常在將函式體賦值給變數的時候為了避免 this 出錯,都會進行 繫結執行環境的操作 ,典型的例子是 var bindId = document.getElementById.bind(document)

2, JSX 存在的問題

因為 JSX 中 DOM 元素也是物件,給元素的屬性賦值實際是給 DOM 元素物件的屬性賦值,見下:

const element = (
 <button onClick={this.handleClick}>click me</button>
);//歡迎加入前端全棧開發交流圈一起學習交流:619586920
複製程式碼

等同於

const element = {
 type: 'button',
 props: {
 onClick: this.handleClick,
 children: 'click me',
 },
};
複製程式碼

這實際就是 將函式體賦值給一個物件的屬性,函式執行時 this 和定義時指向不同 的場景,和原生語法相同的是 this 指向發生了改變,不同的是原生 JS 中不管怎樣, this 總歸是有個指向的,而 JSX 直接 undefined 。 所以說不繫結 this 報 undefined 的錯不能全怪 JS 原生語法。

3. 雙向資料繫結

通過 state 傳遞資料加上事件處理程式便能實現資料的雙向繫結,其背後的思想是(以 input 為例):初始化時將 state 中預定義的 state a 賦值給 input,當 input 的 value 發生改變時,觸發事件處理程式,將改變後的 value 賦值給狀態 a ,React 監測到 state 改變時重新呼叫 render() 方法,即重新渲染元件,達到雙向繫結的目的。

class App extends Component {
 constructor(props) {
  super(props);
  this.state = {
   inputValue: "test",
  };
  this.changeInput = this.changeInput.bind(this);
 }
 //歡迎加入前端全棧開發交流圈一起學習交流:619586920
 changeInput(e) {
  // 將改變後的 input 值賦值給 inputValue,通過事件物件 $event.target.value 實現
  this.setState({
   inputValue: e.target.value
  });
 }
 
 render() {
  // input 改變時觸發 changeInput
  return (
   <div className="App">
    <input value={this.state.inputValue} onChange={this.changeInput} />
    <p>{this.state.inputValue}</p>
   </div>
  );
 }
}//歡迎加入前端全棧開發交流圈一起學習交流:619586920
複製程式碼

結語

感謝您的觀看,如有不足之處,歡迎批評指正。

本次給大家推薦一個免費的學習群,裡面概括移動應用網站開發,css,html,webpack,vue node angular以及面試資源等。 對web開發技術感興趣的同學,歡迎加入Q群:864305860,不管你是小白還是大牛我都歡迎,還有大牛整理的一套高效率學習路線和教程與您免費分享,同時每天更新視訊資料。 最後,祝大家早日學有所成,拿到滿意offer,快速升職加薪,走上人生巔峰。

相關文章