Если вы новичок в react и используете компоненты на основе классов, вы могли видеть в браузере следующие ошибки:
TypeError: Cannot read properties of undefined (reading 'setState')
TypeError: this.setState is not a function
Задумывались ли вы, почему возникает эта ошибка и как ее исправить? В этой статье мы расскажем об этом.
Воспроизведение ошибки
Рассмотрим следующий код:
import React, { Component } from "react"
import "./App.css"
export default class App extends Component {
constructor(props) {
super(props)
this.state = { counter: 0 }
}
updateCounter() {
this.setState({ counter: this.state.counter + 1 })
}
render() {
return (
<div className="App">
<button onClick={this.updateCounter}>
Clicked {this.state.counter} Times
</button>
</div>
)
}
}
Когда вы быстро просмотрите код, вам покажется, что он в полном порядке. Однако если вы запустите код и нажмете на кнопку, ничего не произойдет. Если вы проверите консоль браузера, то увидите следующую ошибку:
Причина ошибки
Причина возникновения этой ошибки кроется в концепции, называемой Closures в JavaScript. Это означает, что область видимости/контекст функции updateCounter
и класса App
не совпадают.
Это означает, что App
и updateCounter
имеют разные this
.
В нашем случае мы пытаемся получить доступ к this
класса App
из функции, которая имеет свой собственный this
, который в настоящее время undefined
. Отсюда ошибка Cannot read properties of undefined
.
Как исправить ошибку
Есть 2 способа исправить эту проблему:
Использование стрелочных функций ES6
Самый простой способ исправить проблему — преобразовать функцию updateCounter
в стрелочную функцию, как показано ниже:
import React, { Component } from "react"
import "./App.css"
export default class App extends Component {
constructor(props) {
super(props)
this.state = { counter: 0 }
}
updateCounter = () => {
this.setState({ counter: this.state.counter + 1 })
}
render() {
return (
<div className="App">
<button onClick={this.updateCounter}>
Clicked {this.state.counter} Times
</button>
</div>
)
}
}
Если вы протестируете приложение сейчас, вы увидите, что счетчик обновляется при нажатии на кнопку.
Вы можете спросить, как замена функции на функцию со стрелкой неожиданно устранила проблему? Что случилось с this
, которая была undefined
? Ну, стрелочные функции не имеют собственных this
! Они ссылаются на this
лексической среды/контекста, внутри которого они объявлены.
В нашем случае, функция updateCounter
определена внутри класса App
, и она ссылается на this
класса, следовательно, this.setState
работает!
Привязка к функции
Что если вы не хотите использовать стрелочную функцию (ну я не вижу причин, почему вы не должны этого делать!) и исправить код с помощью традиционной функции? Ну, есть способ!
Мы можем связать this
из класса App
, чтобы
import React, { Component } from "react"
import "./App.css"
export default class App extends Component {
constructor(props) {
super(props)
this.state = { counter: 0 }
this.updateCounter = this.updateCounter.bind(this)
}
updateCounter() {
this.setState({ counter: this.state.counter + 1 })
}
render() {
return (
<div className="App">
<button onClick={this.updateCounter}>
Clicked {this.state.counter} Times
</button>
</div>
)
}
}
Синтаксис может выглядеть странно, но все, что он делает, это привязывает this
класса к функции.
Подобные проблемы не возникнут при использовании функциональных компонентов с крючками, поскольку они не используют this
.