React.Component
をextendsしたclass内にクリックに反応して叩かれる関数を作りました。
その関数の中でthis.state.xxxx
の値を取得しようとすると、undefinedエラーが発生しました。
connect() {
console.log('connect to the websocket server.')
ws = new WebSocket('ws://localhost:8080/message')
ws.onmessage = function(data) {
const tmpMsgs = this.state.messages
tmpMsgs.unshift(data.data)
this.setState({messages: tmpMsgs})
console.log(data.data)
}
}
Uncaught TypeError: Cannot read property 'messages' of undefined
at WebSocket.ws.onmessage (MessageForm.js:25)
こちらのStackoverflowの記事を参考にlet my = this
を定義し、this
の代わりにlet my = this
のmy
を利用すればエラーは出なくなったのですが、釈然としません。
connect() {
let my = this
console.log('connect to the websocket server.')
ws = new WebSocket('ws://localhost:8080/message')
ws.onmessage = function(data) {
const tmpMsgs = my.state.messages
tmpMsgs.unshift(data.data)
my.setState({messages: tmpMsgs})
console.log(data.data)
}
}
以下のReactの解説ページによると、JSX内でのコールバックを利用するときは、クラスメソッドはバインドされていないから注意しなさい、と書かれています。
You have to be careful about the meaning of this in JSX callbacks. In JavaScript, class methods are not bound by default. If you forget to bind this.handleClick and pass it to onClick, this will be undefined when the function is actually called.
Handling Events
上記のページのサンプルにあるように、
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
constructorでthisとイベントハンドラを結びつけておく必要がありそうです。
そうすれば、イベントが呼ばれる際にthis
がundefined
となることはなくなりました。
constructor (props) {
super(props)
this.connect = this.connect.bind(this)
ニーズがあるかはわかりませんが、作り直したJavaScript全体を貼り直しておきます。
import React from 'react'
let ws = null
class MessageForm extends React.Component {
constructor (props) {
super(props)
this.connect = this.connect.bind(this)
this.sendMessage = this.sendMessage.bind(this)
this.disconnect = this.disconnect.bind(this)
this.state = {
ws: null,
message: '',
messages: []
}
}
messageChanged (e) {
this.setState({message: e.target.value})
}
componentDidMount() {
let ws = new WebSocket('ws://localhost:8080/message')
ws.onmessage = this.handleMessage.bind(this)
this.setState({ws: ws})
console.log('websocket is prepared')
}
componentWillUnmount() {
this.state.ws.close()
}
//https://gist.github.com/voluntas/14303cbf0a1a5fb47ac58c2682bfa877
handleMessage(event) {
let message = event.data
console.log('handle message ->' + message)
this.state.messages.push(message)
this.setState({
messages: this.state.messages
})
console.log('messages -> ' + this.state.messages)
}
connect() {
let ws = new WebSocket('ws://localhost:8080/message')
ws.onmessage = this.handleMessage.bind(this)
this.setState({ws: ws})
console.log('websocket is prepared')
}
sendMessage() {
console.log('send message to websocket server. message -> ' + this.state.message)
this.state.ws.send(this.state.message)
}
disconnect() {
if (this.state.ws != null) {
this.state.ws.close()
}
console.log('disconnected')
}
render() {
return (
<div>
Connect:
<button onClick={e => this.connect()}>Connect </button><br />
Message: <br/>
<input value={this.state.message}
onChange={e => this.messageChanged(e)} /><br />
<button onClick={e => this.sendMessage()}>Send </button><br />
disconnect:
<button onClick={e => this.disconnect()}>Disconnect </button><br />
</div>
)
}
}
export default MessageForm
作り直しの際は以下のGistを参考にさせていただいております。
React コトハジメ