TS裝飾器bindThis優(yōu)雅實(shí)現(xiàn)React類組件中this綁定
為什么this會(huì)是undefined
初學(xué)React類組件時(shí),最不爽的一點(diǎn)應(yīng)該就是 this
指向問題了吧!初識(shí)React的時(shí)候,肯定寫過這樣錯(cuò)誤的demo。
import React from 'react'; export class ReactTestClass extends React.Component { constructor(props) { super(props); this.state = { a: 1 }; } handleClick() { this.setState({a: 2}) } render() { return <div onClick={this.handleClick}>{this.state.a}</div>; } }
上面的代碼在執(zhí)行 onClick
時(shí),就會(huì)如期遇到如下的錯(cuò)誤...
?? this
丟失了。編譯React類組件時(shí),會(huì)將 jsx
轉(zhuǎn)成 React.createElement
,并onClick
事件用對(duì)象包裹一層傳參給該函數(shù)。
// 編譯后的結(jié)果 class ReactTestClass extends _react.default.Component { constructor(props) { super(props); this.state = { a: 1 }; } handleClick() { this.setState({ a: 2 }); } render() { return /*#__PURE__*/ _react.default.createElement( "div", { onClick: this.handleClick // ? 鬼在這里 }, this.state.a ); } } exports.ReactTestClass = ReactTestClass;
寫到這里肯定會(huì)讓大家覺得是 React
在埋坑,其實(shí)不然,官方文檔有澄清:
這并不是 React
自身的行為: 這是因?yàn)?函數(shù)在 JS 中就是這么工作的。通常情況下,比如 onClick={this.handleClick}
,你應(yīng)該 bind 這個(gè)方法。
經(jīng)受過面向?qū)ο缶幊痰南炊Y,為什么還要在類中手動(dòng)綁定 this
? 我們參考如下代碼
class TestComponent { logThis () { console.log(this); // 這里的 `this` 指向誰? } privateExecute (cb) { cb(); } execute () { this.privateExecute(this.logThis); // 正確的情況應(yīng)該傳入 this.logThis.bind(this) } } const instance = new TestComponent(); instance.execute();
上述代碼如期打印了 undefined
。就是在 privateRender
中執(zhí)行回調(diào)函數(shù)(執(zhí)行的是 logThis
方法)時(shí),this
變成了 undefined
。寫到這里可能有人會(huì)提出疑問,就算不是類的實(shí)例調(diào)用的 logThis
方法,那 this
也應(yīng)該是 window
對(duì)象。
沒錯(cuò)!在非嚴(yán)格模式下,就是 window
對(duì)象,但是(知識(shí)點(diǎn)) 使用了 ES6 的 class 語法,所有在 class 中聲明的方法都會(huì)自動(dòng)地使用嚴(yán)格模式,故 this
就是 undefined
。
所以,在非React類組件內(nèi),有時(shí)候也得手動(dòng)綁定 this
。
優(yōu)雅的@bindThis
使用 .bind(this)
render() { return <div onClick={this.handleClick.bind(this)}>{this.state.a}</div>; }
或箭頭函數(shù)
handleClick = () => { this.setState({a: 2}) }
都可以完美解決,但是早已習(xí)慣面向?qū)ο蠛拖矚g搞事情的我總覺得處理的不夠優(yōu)雅而大方。最終期望綁定this的方式如下,
import React from 'react'; import { bindThis } from './bind-this'; export class ReactTestClass extends React.Component { constructor(props) { super(props); this.state = { a: 1 }; } @bindThis // 通過 `方法裝飾器` 自動(dòng)綁定this handleClick() { this.setState({ a: 2 }); } render() { return <div onClick={this.handleClick}>{this.state.a}</div>; } }
對(duì)于 方法裝飾器,該函數(shù)對(duì)應(yīng)三個(gè)入?yún)?,依次?/p>
export function bindThis( target: Object, propertyKey: string, descriptor: PropertyDescriptor, ) { // 如果要返回值,應(yīng)返回一個(gè)新的屬性描述器 }
target
對(duì)應(yīng)的是類的 prototype
propertyKey
對(duì)應(yīng)的是方法名稱,字符串類型,例如 "handleClick"
descriptor
屬性描述器
對(duì)于 descriptor
能會(huì)比較陌生,當(dāng)前該屬性打印出來的結(jié)果是,
{ value: [Function: handleClick], writable: true, enumerable: false, configurable: true }
參看 MDN 上的 Object.defineProperty,我們發(fā)現(xiàn)對(duì)于屬性描述器一共分成兩種,data descriptor
和 accessor descriptor
,兩者的區(qū)別主要在內(nèi)在屬性字段上:
configurable | enumerable | value | writable | get | set | |
---|---|---|---|---|---|---|
data descriptor | ? | ? | ? | ? | ? | ? |
accessor descriptor | ? | ? | ? | ? | ? | ? |
? 可以存在的屬性,? 不能包含的屬性
其中,
configurable
,表示兩種屬性描述器能否轉(zhuǎn)換、屬性能否被刪除等,默認(rèn) false
enumerable
,表示是否是可枚舉屬性,默認(rèn) false
value
,表示當(dāng)前屬性值,對(duì)于類中 handleClick
函數(shù),value就是該函數(shù)本身
writable
,表示當(dāng)前屬性值能否被修改
get
,屬性的 getter 函數(shù)。當(dāng)訪問該屬性時(shí),會(huì)調(diào)用此函數(shù)。執(zhí)行時(shí)不傳入任何參數(shù),但是會(huì)傳入 this
對(duì)象(由于繼承關(guān)系,這里的this
并不一定是定義該屬性的對(duì)象)
set
,屬性的 setter 函數(shù)。當(dāng)屬性值被修改時(shí),會(huì)調(diào)用此函數(shù)。該方法接受一個(gè)參數(shù)(也就是被賦予的新值),會(huì)傳入賦值時(shí)的 this
對(duì)象。
既然 get
函數(shù)有機(jī)會(huì)傳入 this
對(duì)象,我們就從這里入手,通過 @bindThis 裝飾器給 handleClick
函數(shù)綁定真正的 this
。
export function bindThis( target: Object, propertyKey: string, descriptor: PropertyDescriptor, ) { const fn = descriptor.value; // 先拿到函數(shù)本身 return { configurable: true, get() { const bound = fn.bind(this); // 這里的 this 是當(dāng)前類的示例 return bound; } } }
bingo~~~
一個(gè)優(yōu)雅又不失功能的 @bindThis 裝飾器就這么愉快地搞定了。
參考
考慮邊界條件更為詳細(xì)的 @bindThis 版本可參考:autobind-decorator
以上就是TS裝飾器bindThis優(yōu)雅實(shí)現(xiàn)React類組件中this綁定的詳細(xì)內(nèi)容,更多關(guān)于React類組件this綁定的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
React?Native?中實(shí)現(xiàn)倒計(jì)時(shí)功能
這篇文章主要介紹了React?Native中實(shí)現(xiàn)倒計(jì)時(shí)功能示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08React Native 啟動(dòng)流程詳細(xì)解析
這篇文章主要介紹了React Native 啟動(dòng)流程簡(jiǎn)析,文以 react-native-cli 創(chuàng)建的示例工程(安卓部分)為例,給大家分析 React Native 的啟動(dòng)流程,需要的朋友可以參考下2021-08-08React中setState/useState的使用方法詳細(xì)介紹
這篇文章主要介紹了React中setState/useState的使用方法,useState 和 setState 在React開發(fā)過程中 使用很頻繁,但很多人都停留在簡(jiǎn)單的使用階段,并沒有正在了解它們的執(zhí)行機(jī)制2023-04-04react.js使用webpack搭配環(huán)境的入門教程
本文主要介紹了react 使用webpack搭配環(huán)境的入門教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-08-08如何對(duì)react hooks進(jìn)行單元測(cè)試的方法
這篇文章主要介紹了如何對(duì)react hooks進(jìn)行單元測(cè)試的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08