TS裝飾器bindThis優(yōu)雅實現(xiàn)React類組件中this綁定
為什么this會是undefined
初學(xué)React類組件時,最不爽的一點應(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 在埋坑,其實不然,官方文檔有澄清:
這并不是 React 自身的行為: 這是因為 函數(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。寫到這里可能有人會提出疑問,就算不是類的實例調(diào)用的 logThis 方法,那 this 也應(yīng)該是 window 對象。
沒錯!在非嚴(yán)格模式下,就是 window 對象,但是(知識點) 使用了 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)雅實現(xiàn)React類組件中this綁定的詳細(xì)內(nèi)容,更多關(guān)于React類組件this綁定的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
React中setState/useState的使用方法詳細(xì)介紹
這篇文章主要介紹了React中setState/useState的使用方法,useState 和 setState 在React開發(fā)過程中 使用很頻繁,但很多人都停留在簡單的使用階段,并沒有正在了解它們的執(zhí)行機(jī)制2023-04-04
react.js使用webpack搭配環(huán)境的入門教程
本文主要介紹了react 使用webpack搭配環(huán)境的入門教程,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-08-08

