私はたくさんのreact
コードを読んでいて、私には理解できない次のようなものが表示されます。
handleChange = field => e => {
e.preventDefault();
/// Do something here
}
私はたくさんのreact
コードを読んでいて、私には理解できない次のようなものが表示されます。
handleChange = field => e => {
e.preventDefault();
/// Do something here
}
回答:
それはカレー関数です
まず、この関数を2つのパラメーターで調べます…
const add = (x, y) => x + y
add(2, 3) //=> 5
ここでは再びカレーの形で…
const add = x => y => x + y
これは、矢印関数なしの同じ1コードです…
const add = function (x) {
return function (y) {
return x + y
}
}
焦点を合わせる return
それを別の方法で視覚化すると役立つ場合があります。アロー関数はこのように機能することがわかっています。戻り値に特に注意を払いましょう。
const f = someParam => returnValue
私たちのように、add
関数が返す機能を -我々は追加明確にするために括弧を使用することができます。太字のテキストは、私たちの関数の戻り値でありますadd
const add = x => (y => x + y)
つまりadd
、ある数の関数は関数を返します
add(2) // returns (y => 2 + y)
カレー関数の呼び出し
したがって、カレー関数を使用するには、少し異なる方法で呼び出す必要があります…
add(2)(3) // returns 5
これは、最初の(外部)関数呼び出しが2番目の(内部)関数を返すためです。2番目の関数を呼び出して初めて、実際に結果が得られます。これは、呼び出しを2行で区切るとより明白になります…
const add2 = add(2) // returns function(y) { return 2 + y }
add2(3) // returns 5
新しい理解をコードに適用する
OK、それがどのように機能するかを理解したので、コードを見てみましょう
handleChange = field => e => {
e.preventDefault()
/// Do something here
}
まず、矢印関数を使用せずにそれを表現することから始めます…
handleChange = function(field) {
return function(e) {
e.preventDefault()
// Do something here
// return ...
};
};
ただし、アロー関数は字句的にバインドするためthis
、実際には次のようになります…
handleChange = function(field) {
return function(e) {
e.preventDefault()
// Do something here
// return ...
}.bind(this)
}.bind(this)
たぶん今、これが何をしているのかをより明確に見ることができます。handleChange
関数は、指定する関数を作成していますfield
。アプリケーションの状態を更新するには、各入力で独自のリスナーをセットアップする必要があるため、これは便利なReact手法です。このhandleChange
関数を使用することで、change
各フィールドにリスナーを設定することになる重複コードをすべて排除できます。涼しい!
1ここではthis
、元のadd
関数はコンテキストを使用しないため、字句的にバインドする必要はありませんでした。この場合、それを保持することは重要ではありません。
さらに多くの矢
必要に応じて、2つ以上の矢印関数をシーケンスできます-
const three = a => b => c =>
a + b + c
const four = a => b => c => d =>
a + b + c + d
three (1) (2) (3) // 6
four (1) (2) (3) (4) // 10
カリー化された関数は驚くべきことをすることができます。以下では$
、2つのパラメーターを持つカレー関数として定義されていますが、呼び出しサイトでは、任意の数の引数を指定できるように見えます。カリー化はアリティの抽象化です-
const $ = x => k =>
$ (k (x))
const add = x => y =>
x + y
const mult = x => y =>
x * y
$ (1) // 1
(add (2)) // + 2 = 3
(mult (6)) // * 6 = 18
(console.log) // 18
$ (7) // 7
(add (1)) // + 1 = 8
(mult (8)) // * 8 = 64
(mult (2)) // * 2 = 128
(mult (2)) // * 2 = 256
(console.log) // 256
部分適用
部分適用は関連する概念です。関数をカレー形式で定義する必要がないことを除いて、カリーと同様に関数を部分的に適用できます-
const partial = (f, ...a) => (...b) =>
f (...a, ...b)
const add3 = (x, y, z) =>
x + y + z
partial (add3) (1, 2, 3) // 6
partial (add3, 1) (2, 3) // 6
partial (add3, 1, 2) (3) // 6
partial (add3, 1, 2, 3) () // 6
partial (add3, 1, 1, 1, 1) (1, 1, 1, 1, 1) // 3
ここpartial
にあなた自身のブラウザで遊ぶことができる実際のデモがあります-
const partial = (f, ...a) => (...b) =>
f (...a, ...b)
const preventDefault = (f, event) =>
( event .preventDefault ()
, f (event)
)
const logKeypress = event =>
console .log (event.which)
document
.querySelector ('input[name=foo]')
.addEventListener ('keydown', partial (preventDefault, logKeypress))
<input name="foo" placeholder="type here to see ascii codes" size="50">
$
はコンセプトのデモに使用されましたが、好きな名前を付けることができます。偶然ではあるが完全に無関係であり、jQueryなどの一般的なライブラリで使用され$
てい$
ます。これは、関数のライブラリ全体への一種のグローバルエントリポイントです。他にも使われていると思います。もう1つは_
、アンダースコアやロダッシュなどのライブラリで一般化されているものです。あるシンボルが別のシンボルよりも意味がありません。プログラムに意味を割り当てます。それは単に有効なJavaScriptです:D
$
使用方法を確認することで、理解を深めることができます。実装自体について質問している場合$
は、値を受け取っx
て新しい関数を返す関数k => ...
です。返された関数の本体を見ると、関数でなければならないことがk (x)
わかりk
、結果k (x)
がに戻されると$ (...)
、別のが返さk => ...
れます。行き詰まっている、私に知らせてください。
abc(1,2,3)
は、は理想的とは言えませんabc(1)(2)(3)
。コードのロジックについて推論するのは難しく、関数abcを読み取るのも難しく、関数呼び出しを読み取るのも困難です。abcが何をするのかを知るだけで済む前に、abcが返す名前のない関数が何をするのかわからなくなりました。
理解矢印の機能の利用可能な構文は、あなたが提供されている例のように「チェーン」するとき、彼らが導入されているどのような行動の理解が得られます。
複数のパラメーターの有無にかかわらず、ブロック波括弧なしで矢印関数が記述されると、関数の本体を構成する式が暗黙的に返されます。あなたの例では、その式は別の矢印関数です。
No arrow funcs Implicitly return `e=>{…}` Explicitly return `e=>{…}`
---------------------------------------------------------------------------------
function (field) { | field => e => { | field => {
return function (e) { | | return e => {
e.preventDefault() | e.preventDefault() | e.preventDefault()
} | | }
} | } | }
矢印構文を使用して無名関数を作成するもう1つの利点は、それらが定義されているスコープに字句的にバインドされることです。MDNの「矢印機能」から:
これは、あなたの例で特に適切です。 反応応用。@naomikによって尖ったアウトのように、中にあなたが頻繁にアクセスリアクトコンポーネントのメンバ関数を使用してthis
。例えば:
Unbound Explicitly bound Implicitly bound
------------------------------------------------------------------------------
function (field) { | function (field) { | field => e => {
return function (e) { | return function (e) { |
this.setState(...) | this.setState(...) | this.setState(...)
} | }.bind(this) |
} | }.bind(this) | }
このように考えると、矢印が表示されるたびにに置き換えられfunction
ます。function parameters
矢印の前に定義されています。
だからあなたの例では:
field => // function(field){}
e => { e.preventDefault(); } // function(e){e.preventDefault();}
そして一緒に:
function (field) {
return function (e) {
e.preventDefault();
};
}
// Basic syntax:
(param1, param2, paramN) => { statements }
(param1, param2, paramN) => expression
// equivalent to: => { return expression; }
// Parentheses are optional when there's only one argument:
singleParam => { statements }
singleParam => expression
this
。
簡潔でシンプル 🎈
短く書かれた別の関数を返す関数です。
const handleChange = field => e => {
e.preventDefault()
// Do something here
}
// is equal to
function handleChange(field) {
return function(e) {
e.preventDefault()
// Do something here
}
}
人々がそれをする理由❓
カスタマイズ可能な関数を作成する必要があるときに直面しましたか?または、固定パラメーター(引数)を含むコールバック関数を作成する必要がありますが、関数により多くの変数を渡す必要がありますが、グローバル変数は避けますか?あなたの答えが「はい」の場合、それはそれを行う方法です。
たとえばbutton
、withClickコールバックがあります。そしてid
、関数に渡す必要がありonClick
ますが、受け入れるパラメータevent
は1つだけです。次のように内部に追加のパラメータを渡すことはできません。
const handleClick = (event, id) {
event.preventDefault()
// Dispatch some delete action by passing record id
}
効果がないでしょう!
したがって、グローバル変数は悪であるので、グローバル変数なしで独自の変数スコープを持つ他の関数を返す関数を作成します。
関数の下handleClick(props.id)}
が呼び出されて関数が返さid
れ、そのスコープ内にあります!何度押しても、IDは相互に影響したり変更したりしませんが、完全に分離されています。
const handleClick = id => event {
event.preventDefault()
// Dispatch some delete action by passing record id
}
const Confirm = props => (
<div>
<h1>Are you sure to delete?</h1>
<button onClick={handleClick(props.id)}>
Delete
</button>
</div
)
あなたの質問の例は、最初の引数curried function
を使用し、arrow function
を持つの例implicit return
です。
Arrow関数はこれを字句的にバインドします。つまり、独自のthis
引数はありませんが、this
値を囲んでいるスコープから取得します。
上記のコードと同等のものは
const handleChange = (field) {
return function(e) {
e.preventDefault();
/// Do something here
}.bind(this);
}.bind(this);
例についてもう1つ注意する点はhandleChange
、constまたは関数として定義することです。おそらくそれをクラスメソッドの一部として使用していて、class fields syntax
したがって、外部関数を直接バインドする代わりに、クラスコンストラクターでバインドします。
class Something{
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(field) {
return function(e) {
e.preventDefault();
// do something
}
}
}
この例でもう1つ注意する点は、暗黙的な戻りと明示的な戻りの違いです。
const abc = (field) => field * 2;
上記は暗黙の戻りの例です。値フィールドを引数として取り、結果を返しますfield*2
関数を明示的に指定したを
明示的な戻りの場合、値を返すようにメソッドに明示的に指示します
const abc = () => { return field*2; }
矢印関数についてもう1つ注意すべき点は、矢印関数には独自の関数はなくarguments
、親スコープから継承することです。
たとえば、次のようなアロー関数を定義した場合
const handleChange = () => {
console.log(arguments) // would give an error on running since arguments in undefined
}
代替の矢印関数として、使用できる残りのパラメーターを提供します
const handleChange = (...args) => {
console.log(args);
}
それは完全に関連しているわけではないかもしれませんが、言及された質問はcaseを使用する(そして私はこのSOスレッドにぶつかる)ため、ここでは明示的に言及されていない二重矢印関数の1つの重要な側面があります。「最初の」矢印(関数)のみが名前が付けられ(したがって、実行時に「識別可能」)、次の矢印は匿名であり、Reactの観点からは、すべてのレンダリングで「新しい」オブジェクトとしてカウントされます。
したがって、二重矢印関数は、PureComponentを常に再レンダリングします。
例
次のような変更ハンドラーを持つ親コンポーネントがあります。
handleChange = task => event => { ... operations which uses both task and event... };
そして、次のようなレンダーで:
{
tasks.map(task => <MyTask handleChange={this.handleChange(task)}/>
}
次に、handleChangeを入力またはクリックで使用します。そして、これはすべてうまくいき、とても素敵に見えます。しかし、それは、親に再レンダリングを引き起こすすべての変更(完全に無関係な状態変更など)も、PureComponentsであるにもかかわらず、MyTaskのすべてを再レンダリングすることも意味します。
これは、「最外」の矢印とそれをフィードするオブジェクトを渡す、カスタムshouldUpdate関数を書く、または名前付き関数を書く(そしてこれを手動でバインドする...)などの基本に戻るなど、多くの方法で軽減できます。