19
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

クラス内の関数でthis.state〜プロパティを利用しようとすると、undefinedエラーが出る

Posted at

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 = thismyを利用すればエラーは出なくなったのですが、釈然としません。

  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とイベントハンドラを結びつけておく必要がありそうです。
そうすれば、イベントが呼ばれる際にthisundefinedとなることはなくなりました。

	constructor (props) {
		super(props)
    this.connect = this.connect.bind(this)

ニーズがあるかはわかりませんが、作り直したJavaScript全体を貼り直しておきます。

MessageForm.js
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 コトハジメ

19
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?