初めて質問させていただきます。
最近,React.jsを勉強し始めたのですが、setStateメソッドの部分で'setState' of nullというエラーが出てしまい、全く先に進めなくなってしまいました。
Reactについて詳しい方にご教授いただければと思います。
開発環境: React.js + Firebase
databaseには、firebaseを使っており、ここからデータを読み込んで、Stateのtweetsに値を格納したいと思っております。格納できれば解決するのですが、どうしてもわかりません。
コードは以下です。
このコードで、componentDidMount()のthis.setStateの部分でエラーが起きてしまいます。
エラー文は以下です。
Uncaught TypeError: Cannot read property 'setState' of null
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta lang="ja">
<link rel="stylesheet" type="text/css" href="css/style.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script src="https://www.gstatic.com/firebasejs/4.6.2/firebase.js"></script>
</head>
<body>
<div id="tweet"></div>
<script type="text/babel">
// Initialize Firebase
var config = {
********非表示にしています******
};
firebase.initializeApp(config);
var firebaseRef = firebase.database().ref();
var tweetsRef = firebaseRef.child("tweets");
// メインツイート画面
class TweetContainer extends React.Component{
constructor(props) {
super(props);
this.state = {tweets: [
{ id: 1, user: 'Tom', text: 'Good morning' },
{ id: 2, user: 'John', text: 'Good afternoon' },
{ id: 3, user: 'Emily', text: 'Good evening' }
]};
console.log("initialized");
this.componentDidMount = this.componentDidMount.bind(this);
}
componentDidMount(){
tweetsRef.on("value", function(snapshot){
this.setState({ //エラー部分
tweets: snapshot.val()
});
});
}
render(){
var tweetItems = this.state.tweets.map(function(tweet){
return (
<TweetItem tweet={tweet} />
);
});
return(
<div className="tweetContainer">
{tweetItems}
<TweetForm />
</div>
);
}
}
//ツイートアイテム
class TweetItem extends React.Component{
constructor(props) {
super(props);
}
render(){
return(
<div className="tweetItem">
<div className="tweet">{this.props.tweet.text}</div>
</div>
);
}
}
//送信フォーム
class TweetForm extends React.Component{
constructor(props) {
super(props);
this._onClick = this._onClick.bind(this);
}
_onClick(e){
tweetsRef.push({
text: ReactDOM.findDOMNode(this.refs.inputValue).value
});
}
render(){
return(
<div className="tweetForm">
<input ref="inputValue" type="text" placeholder="message..." />
<input type="button" value="送信" onClick={this._onClick.bind(this)}/>
</div>
);
}
}
//レンダリング
ReactDOM.render(
<TweetContainer />,
document.getElementById("tweet")
);
</script>
</body>
</html>
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
+3
Javascript/Reactに詳しくないので、命知らずなコメントですが...
最近ちょうど似たような議論を見ました
teratail: Reactにおける関数の渡し方による動作の違い
上記のraccyさんの回答にあるthisの議論が参考になるように思います。
componentDidMount(){
tweetsRef.on("value", function(snapshot){
this.setState({
tweets: snapshot.val()
});
});
}
functionの本体部分でthisを参照する際、この関数が
f.call(eventSource, snapShot)
と呼び出されるのであれば関数内のthisはeventSourceに束縛されますが、この場合はeventSourceがイベント発生元のインスタンス(tweetsRefの値)になるなら、thisは質問者さんが意図したものにならないのではないでしょうか。質問者さんの意図はthisがTweetContainerのインスタンスを表すことですよね?
対処:
class構文をお使いなので(ES2015前提なので)、functionによる無名関数ではなくアロー関数を使えば次のように書けると思います。
componentDidMount() {
tweetsRef.on("value", snapshot => this.setState({
tweets: snapshot.val()
});
}
functionキーワードによる無名関数とアロー関数のthisの違いは次のような単純な例を見ると観察できます。無名関数はcallで起動される際の第一引数の値がthisに束縛されてから実行されますが、アロー関数はアロー関数が生成された時点の文脈でのthisが束縛されたクロージャーの元で実行されるので、callで起動されたとしてもその第一引数の値によりthisが変化することはないのだと思います。
function A() {
this.a = 1;
// 無名関数f本体のthisは関数生成時点のクロージャー内に束縛されて
// おらず、f.call(x)で起動された際のxの値で(起動時に)束縛される。
A.prototype.f = function () { return this.a; };
}
function B() {
this.a = 1;
// アロー関数本体のthisはアロー関数生成時点のクロージャー内に束縛される。
B.prototype.f = () => this.a;
}
let a = new A();
let b = new B();
console.log(a.f.call({a: 2}); // 2
console.log(b.f.call({a: 2}); // 1
おかしな点がありましたら指摘いただけると幸いです。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.31%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2017/11/26 21:56
実は先ほど解決したのですが、まさにおっしゃる通り、thisが親のコンポーネントを指定できていないことが原因でした。ご説明いただいたアロー関数を使うことにより、問題のエラーは起きなくなりました。
componentDidMount(){
tweetsRef.on("value", (snapshot) => {
this.setState({
tweets: snapshot.val()
});
});
}
かなりマイナーな質問にも関わらず、的確な回答をいただきありがとうございました。
自分もまだまだReact.jsを始めたばかりなので、もっと勉強します。
2017/11/26 22:03
2017/11/27 03:47
それまではthisをバインドしなくても勝手に認識してくれてたみたいなんですけど、それが自動じゃ無くなってたみたいです。
React.jsは調べても古い書き方が多くて惑わされるので大変ですね...w