質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Babel

Babelは、JavaScriptの次世代仕様であるECMAScriptのコンパイラ。次世代の標準機能を用いて記述されたコードを、それらの機能に対応していないブラウザでも動作するコードに変換することができます。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

Q&A

解決済

4回答

1374閲覧

Reactにおける関数の渡し方による動作の違い

gesorein

総合スコア101

Babel

Babelは、JavaScriptの次世代仕様であるECMAScriptのコンパイラ。次世代の標準機能を用いて記述されたコードを、それらの機能に対応していないブラウザでも動作するコードに変換することができます。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

1グッド

2クリップ

投稿2017/11/24 03:30

編集2017/11/24 15:43

現在、React.jsを用いて開発を行っております。環境は以下の通りです。
開発環境: Babel + ES6 + React.js + JSX

##聞きたいこと

Reactにおける関数の渡し方による動作の違いについて知りたいです。
たとえば以下のようなコードがあったとします。

JavaScript

1import React from 'react'; 2 3class CountUp extends React.Component { 4 constructor(props) { 5 super(props); 6 this.state = { count: 0 }; 7 } 8 9 handleClick = (e) => { 10 this.setState({ count: this.state.count + 1 }); 11 // 上の setState() の記述は好ましくないようです(詳しくは回答を参照ください) 12 }; 13 14 render() { 15 return( 16 <button onClick={this.handleClick}> 17 {this.state.count} 18 </button> 19 ); 20 } 21}

このコードのonClickの部分を以下のように変更したとします。

JavaScript

1- <button onClick={this.handleClick}> 2+ <button onClick={(e) => this.handleClick(e)}>

ちなみにどちらのコードでも同じ結果になります。

Web上で様々なサンプルコードなどを見ていると、
上の記述も下の記述も両方よく見かけるような気がします。
そのとき、ふとこれらの記述に何か違いがあるのかと疑問に思ったしだいです。

もちろん渡せる引数を変更できるという点で違いがあるかと思いますが、
今回は内部的に何か動作に違いがあるかを知りたいです。よろしくお願いいたします。

KSwordOfHaste👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

上のコードの方がパフォーマンス上優れています。

下の方ではrenderする時に毎回アローファンクションを定義していて、比較されるDOMが別になってしまします。
そのため、毎回DOMの更新が起こってしまうので、パフォーマンスが下がります。

参考
https://qiita.com/cubdesign/items/ee8bff7073ebe1979936 (コメント欄も参照してください)
https://reactjs.org/docs/handling-events.html

投稿2017/11/24 03:51

編集2017/11/24 04:02
sakapun

総合スコア888

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KSwordOfHaste

2017/11/24 04:00 編集

上の方のリンク先'[]'の中も'()'の中もどちらも、404になりました。訂正していただけるとありがたいです。
sakapun

2017/11/24 04:03

リンク修正いたしました
KSwordOfHaste

2017/11/24 04:06

対応いただき、ありがとうございました。
miyabi-sun

2017/11/24 04:19 編集

リンク先読みました。 「これを下位コンポーネントとして別のコンポーネントを呼び出した場合、再レンダリングが発生する可能性がある」と読み取れたので混乱しています。 「毎回」とは書かれていないように思えますが、アロー演算子を使った瞬間、必ずパフォーマンスは下がるのでしょうか?
sakapun

2017/11/24 13:19

もうすでに詳細を調べられたようですが、 (() => {}) === (() => {}) が、Falseとなってしまいますので、アローファンクションをプロパティとして渡した場合にはレンダーされるDOMが異なりますので、必ず差分が検知され再描画が走ってしまうこととなります。 これを防ぐ方法がありまして、プロパティの内容を深くまで操作しないshallowな比較で行うことです。 React15.3ではそれがトップレベルのAPI「Pure Components」として定義されております。 http://jyane.jp/2016/08/12/react-purecomponent.html それを使えば、このような記法の問題を気にすることがなくコードを書けることとなります。 しかし、shallowな比較自体はそれほどコストに優れたわけではないので、逆にパフォーマンスが良くないこともあります。 この例では上のコードであればもう問題とならないのですから、Pure Componentsを使う必要はないでしょう。 実運用レベルで遅くなるのかということも気になるかと思います。 私はどちらかと言うと業務系のアプリを書くことが多いのですが、正直パフォーマンスであまり気になったことは正直ありません。 書きやすさは個人的にもアローファンクションですらっと書きたいと思います。 しかし、原理を理解し、パフォーマンスに問題があるということを把握したあとは、使うべきではないなと思い利用を控えるようにしています
gesorein

2017/11/24 15:07 編集

なるほどPure Componentというものがあるのですね。参考になります。 試しに開発環境で子のコンポーネントをPure Componentとして5000個生成し、 すべての子のコンポーネントに親のコンポーネントから {(e) => this.handleClick(e)} を prop として渡して計測したところ、{this.handleClick} を渡した場合より、 再レンダリングに約2.5倍時間がかかりました。 質問文のコードのように prop で渡さないで onClick に設定しただけの場合は、 2つの再レンダリングにかかる時間はほとんど変わりませんでした。 即時のアロー関数を使って渡す場合は、shouldComponentUpdate() で差分を比較した パフォーマンスチューニングなどの場合に困りそうなので、注意深く利用していこうと思います。 非常に具体的な情報を教えていただき本当にありがとうございます!
guest

0

質問の主題からは外れますが、this.setState({ count: this.state.count + 1 })の部分は公式サイトでも注意喚起されている好ましくない書き方です。this.setStateは非同期で実行されるため、以下のようにthis.stateの値が本当に今現在のstateなのか保証されません。

参考

Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.

https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous

Wrong

handleClick = (e) => { this.setState({ count: this.state.count + 1 }); };

Correct

handleClick = (e) => { this.setState((prevState, props) => ({ count: prevState.count + 1 }); };

投稿2017/11/24 04:05

編集2017/11/24 10:53
HayatoKamono

総合スコア2415

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

gesorein

2017/11/24 06:03

掲示してくださったリンクを拝見しました。 いままでずっと setState() の詳しい仕様を知らずにコードを書いておりました。 これからは公式のドキュメントもきちんと目を通すようにいたします。 それにしてもWeb上のサンプルコードなどを見ると、 質問文のように this.state.count をインクリメントして そのまま SetState() にオブジェクトとして渡していることが多いですよね...。 非常に有益なアドバイスをいただきありがとうございます!
HayatoKamono

2017/11/24 10:39

そうですね。いろんなところでthis.stateを参照してアップデートかけてるコードを見かけます。
guest

0

そもそも、該当のコードはstage 3のClass Fieldsを用いた記法です。ECMAScriptの正式な仕様としては認められていません。stage 3ですので、このまま採用される可能性はありますが、変更される可能性もあることに留意すべきです。

さて、話は置いといて、ECMAScript 2017の仕様でそのまま書いた場合、handleClickは普通のメソッドとして定義することになると思います。

JavaScript

1import React from 'react'; 2 3class CountUp extends React.Component { 4 constructor(props) { 5 super(props); 6 this.state = { count: 0 }; 7 } 8 9 handleClick(e) { 10 this.setState({ count: this.state.count + 1 }); 11 } 12 13 render() { 14 return( 15 <button onClick={this.handleClick}> 16 {this.state.count} 17 </button> 18 ); 19 } 20}

もちろん、これは正常に動きません。なぜなら、onClickにはhandleClickという関数だけを渡しているため、thisが何であるかという情報が欠如されるからです。クリックによってコールバックで呼び出されるときにthisはこのオブジェクトのことではなくなり、this.setState()の呼出しに失敗します。

それを解決する手段の一つとして、{(e) => this.handleClick(e)}があったのです。こちらは=>によってthisが束縛されるため、thisがこのオブジェクトであるところの関数というものをonClickに渡します。ですので、コールバックでもthisが何か別のものになるのを防ぎます。他の解決方法として{this.handleClick.bind(this)}とする方法や、コンストラクタでthis.handleClick = this.handleClick.bind(this);としておくという方法があります。そして、第4の方法として、該当コードであるClass Fieldsを用いた記法が(まだ正式では無いが)できるようになったと言うことです。

つまり、イベントのコールバックされる関数のthisの問題に対する解決方法について、次のいずれかを行えば良いとなります。

  1. アロー関数=>で囲って渡す。{(e) => this.handleClick(e)}
  2. bind(this)して渡す。{this.handleClick.bind(this)}
  3. コンストラクタでbind(this)しておく。this.handleClick = this.handleClick.bind(this);
  4. Class Fieldsでアロー関数を使う。handleClick = (e) => {...};

これはどれか一つだけで十分であり、複数を採用することは意味がありません。どれがいいかは一長一短になります。1.は毎回アロー関数を作るコストがかかります。2.も毎回新しい関数オブジェクトを作っています。3.はメソッド定義とコンストラクタにそのメソッドに対する情報が分散しています。4.はまだstage 3であり、仕様が変わる恐れがあります。

参考: Handling Events - React


[蛇足]

私のお勧めは第5の邪法であるCoffeeScript(2.0.0以上)を使うことです。

CoffeeScript

1import React from 'react' 2 3class CountUp extends React.Component 4 constructor: (props) -> 5 super(props) 6 @state = count: 0 7 8 handleClick: (e) => 9 this.setState count: @state.count + 1 10 11 render: -> 12 <button onClick={@handleClick}> 13 {@state.count} 14 </button>

JSX記法にも対応したCoffeeScriptに死角は無い!

投稿2017/11/24 11:53

編集2017/11/24 11:58
raccy

総合スコア21735

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

gesorein

2017/11/24 12:45

ECMAScriptの仕様について教えていただきありがとうございます。 Class Fieldは this を bind することなく使えるので、stage 3ということを気にせず使っていました。 これからはきちんと検討して使用するか判断するようにします。 また詳細に説明してくださったおかげで this の扱いについて整理できたので良かったです。 CoffeeScript で記述すると非常にスマートに記述できますね!素晴らしい!
guest

0

ベストアンサー

JS的には殆ど同様です。

JavaScript

1var hoge = it => it + 3; 2// 方法1 3console.log(hoge(3)); // 6 4// 方法2 5console.log((it => hoge(it))(3)); // 6

即興のアロー関数を1個用意してネストさせている以上の違いはありません。
他にあげるとすれば関数呼び出しの回数が増えて遅くなるくらいですかね。
JSの関数呼び出しはほぼ無視出来るコストらしいですが……

JSでは関数を作るとthisの指している箇所が変わりますが、
アロー関数はthisを作らないので、質問文の内部で平然とthis.handleClickのプロパティを取りに行って関数を叩いているのが確認できます。


【追記】reactの場合

ただしReactの内部で使う時は話が変わってきます。

Handling Events - Reactによると、
親子関係を結ぶ時は再更新処理が増えるので注意しましょうとのことです。
仕組みはこうです。

JavaScript

1var a = it => it + 3; 2var b = it => it + 3; 3console.log(a == b); // false 4 5var c = {}; 6var d = {}; 7console.log(c == d);

一見全く同じものにしか見えませんが、
Array、Object、Functionは作成する度に別オブジェクトとして見えないIDが割り振られ別個のものとして認識されます。

Reactの仕組み上、renderは何度も叩かれて毎回別の関数が生まれてしまうので、
親のコンポーネントは前回のDOMと今回のDOMが同一であることが分からなくなってしまうようですね。
即興のアロー関数ではなく、何か固定化した関数をはめ込むのがオススメだよと記載されています。

投稿2017/11/24 03:50

編集2017/11/24 04:32
miyabi-sun

総合スコア21158

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

gesorein

2017/11/24 10:59

追記ありがとうございます!非常にわかりやすかったです。 ドキュメントを拝見しましたが、下位コンポーネントに prop としてコールバックを渡すと 余計な再レンダリングが発生する可能性があると書かれているので、 できるだけ this.handleClick をそのまま onClick に渡したほうがいいみたいですね。 ただ「可能性がある」と書かれているので、すべての場合においてパフォーマンスに 影響があるかは気になるところです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問