Context
Cho ví dụ sau:
class Counter extends Component {
state = {
count: 0,
}
addOne() {
this.setState({ count: this.state.count + 1 })
}
render() {
const { count } = this.state
return (
<div>
<h1>{count}</h1>
<button onClick={this.addOne}> +1 </button>
</div>
)
}
}Khi chúng ta bấm nút “+1”, đối tượng this trong phương thức addOne sẽ có giá trị là undefined:
addOne() {
console.log(this) // undefined
this.setState({ count: this.state.count + 1 })
}Analogy
Chúng ta triển khai một chương trình có logic tương tự như trong ví dụ trên bằng JS thông thường để hiểu hơn về vấn đề này.
Đầu tiên, ta tạo ra một lớp đối tượng có tên là Counter như sau:
class Counter {
constructor(value) {
this.value = value
}
addOne() {
console.log(this)
this.value += 1
}
onclick() {
handleOnclick(this.addOne)
}
}Sau đó ta tạo ra hàm xử lý sự kiện click:
function handleOnclick(addOne) {
addOne()
}Cuối cùng, ta tạo instance cho Counter và gọi thực hiện phương thức onclick:
const counter = new Counter(0)
counter.onclick()Ở ví dụ này, giá trị của this ở trong phương thức addOne khi được gọi thực hiện cũng là undefined như trong ví dụ trước.
Explain
Ta có thể giải thích như sau:
Mặc dù this ở trong một phương thức sẽ được gắn vào đối tượng gọi thực hiện phương thức, chính là đối tượng counter1.
Nhưng, ở trong trường hợp trên ta lại truyền phương thức addOne vào sự kiện onclick dưới dạng một callback.
Và khi callback này được gọi ở bên trong hàm xử lý của handleOnlick, từ khóa this sẽ không còn tham chiếu đến đối tượng counter nữa. Dẫn đến, giá trị của từ khóa this trở thành undefined.
Solutions
Để this trỏ đến đúng đối tượng, ta có thể thực hiện những cách sau:
Explicit Binding
Sử dụng bind để ràng buộc phương thức với đối tượng được tạo ra từ component ở trong constructor.
constructor(props) {
super(props)
this.addOne = this.addOne.bind(this)
}Callback
Sử dụng arrow function callback:
<button onClick={() => this.addOne()}> +1 </button>Từ khóa this có trong một arrow function sẽ trỏ đến đối tượng gần nhất tính từ phạm vi nó được gọi. Ở đoạn code trên, this sẽ trỏ đến đối tượng được tạo từ component.
Arrow Function
Khai báo phương thức addOne ở dạng arrow function:
addOne = () => {
this.setState({ count: this.state.count + 1 })
}Tương tự cách 2, từ khóa this của addOne sẽ luôn trỏ về instance của Counter.
Resources
Footnotes
-
Xem thêm In a method. ↩