TS裝飾器bindThis優(yōu)雅實(shí)現(xiàn)React類組件中this綁定
為什么this會是undefined
初學(xué)React類組件時,最不爽的一點(diǎn)應(yīng)該就是 this
指向問題了吧!初識React的時候,肯定寫過這樣錯誤的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
時,就會如期遇到如下的錯誤...
?? this
丟失了。編譯React類組件時,會將 jsx
轉(zhuǎn)成 React.createElement
,并onClick
事件用對象包裹一層傳參給該函數(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;
寫到這里肯定會讓大家覺得是 React
在埋坑,其實(shí)不然,官方文檔有澄清:
這并不是 React
自身的行為: 這是因?yàn)?函數(shù)在 JS 中就是這么工作的。通常情況下,比如 onClick={this.handleClick}
,你應(yīng)該 bind 這個方法。
經(jīng)受過面向?qū)ο缶幊痰南炊Y,為什么還要在類中手動綁定 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
方法)時,this
變成了 undefined
。寫到這里可能有人會提出疑問,就算不是類的實(shí)例調(diào)用的 logThis
方法,那 this
也應(yīng)該是 window
對象。
沒錯!在非嚴(yán)格模式下,就是 window
對象,但是(知識點(diǎn)) 使用了 ES6 的 class 語法,所有在 class 中聲明的方法都會自動地使用嚴(yán)格模式,故 this
就是 undefined
。
所以,在非React類組件內(nèi),有時候也得手動綁定 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 // 通過 `方法裝飾器` 自動綁定this handleClick() { this.setState({ a: 2 }); } render() { return <div onClick={this.handleClick}>{this.state.a}</div>; } }
對于 方法裝飾器,該函數(shù)對應(yīng)三個入?yún)?,依次?/p>
export function bindThis( target: Object, propertyKey: string, descriptor: PropertyDescriptor, ) { // 如果要返回值,應(yīng)返回一個新的屬性描述器 }
target
對應(yīng)的是類的 prototype
propertyKey
對應(yīng)的是方法名稱,字符串類型,例如 "handleClick"
descriptor
屬性描述器
對于 descriptor
能會比較陌生,當(dāng)前該屬性打印出來的結(jié)果是,
{ value: [Function: handleClick], writable: true, enumerable: false, configurable: true }
參看 MDN 上的 Object.defineProperty,我們發(fā)現(xiàn)對于屬性描述器一共分成兩種,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)前屬性值,對于類中 handleClick
函數(shù),value就是該函數(shù)本身
writable
,表示當(dāng)前屬性值能否被修改
get
,屬性的 getter 函數(shù)。當(dāng)訪問該屬性時,會調(diào)用此函數(shù)。執(zhí)行時不傳入任何參數(shù),但是會傳入 this
對象(由于繼承關(guān)系,這里的this
并不一定是定義該屬性的對象)
set
,屬性的 setter 函數(shù)。當(dāng)屬性值被修改時,會調(diào)用此函數(shù)。該方法接受一個參數(shù)(也就是被賦予的新值),會傳入賦值時的 this
對象。
既然 get
函數(shù)有機(jī)會傳入 this
對象,我們就從這里入手,通過 @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~~~
一個優(yōu)雅又不失功能的 @bindThis 裝飾器就這么愉快地搞定了。
參考
考慮邊界條件更為詳細(xì)的 @bindThis 版本可參考:autobind-decorator
以上就是TS裝飾器bindThis優(yōu)雅實(shí)現(xiàn)React類組件中this綁定的詳細(xì)內(nèi)容,更多關(guān)于React類組件this綁定的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
React?Native?中實(shí)現(xiàn)倒計(jì)時功能
這篇文章主要介紹了React?Native中實(shí)現(xiàn)倒計(jì)時功能示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08React中setState/useState的使用方法詳細(xì)介紹
這篇文章主要介紹了React中setState/useState的使用方法,useState 和 setState 在React開發(fā)過程中 使用很頻繁,但很多人都停留在簡單的使用階段,并沒有正在了解它們的執(zhí)行機(jī)制2023-04-04react.js使用webpack搭配環(huán)境的入門教程
本文主要介紹了react 使用webpack搭配環(huán)境的入門教程,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-08-08