私はES6で(BabelJSを使って)単純なコンポーネントを書いていますが、関数this.setState
は動作していません。
典型的なエラーには、次のようなものがあります。
未定義のプロパティ 'setState'を読み取れません
または
this.setStateは関数ではありません
なぜなのかご存知ですか?これがコードです:
import React from 'react'
class SomeClass extends React.Component {
constructor(props) {
super(props)
this.state = {inputContent: 'startValue'}
}
sendContent(e) {
console.log('sending input content '+React.findDOMNode(React.refs.someref).value)
}
changeContent(e) {
this.setState({inputContent: e.target.value})
}
render() {
return (
<div>
<h4>The input form is here:</h4>
Title:
<input type="text" ref="someref" value={this.inputContent}
onChange={this.changeContent} />
<button onClick={this.sendContent}>Submit</button>
</div>
)
}
}
export default SomeClass
this.changeContent
は、onChange
propとして渡される前にthis.changeContent.bind(this)
を介してコンポーネントインスタンスにバインドする必要があります。そうしないと、関数本体のthis
変数はコンポーネントインスタンスを参照せず、window
を参照します。 Function :: bind を参照してください。
ES6クラスの代わりにReact.createClass
を使用すると、コンポーネントに定義されているライフサイクル以外のすべてのメソッドは自動的にコンポーネントインスタンスにバインドされます。 自動バインド を参照してください。
関数をバインドすると新しい関数が作成されることに注意してください。 renderで直接バインドすることができます。つまり、コンポーネントがレンダリングされるたびに新しい関数が作成されるか、コンストラクタでバインドされて1回だけ起動されます。
constructor() {
this.changeContent = this.changeContent.bind(this);
}
vs
render() {
return <input onChange={this.changeContent.bind(this)} />;
}
参照はReact.refs
ではなくコンポーネントインスタンスに設定されます。React.refs.someref
をthis.refs.someref
に変更する必要があります。 sendContent
メソッドをコンポーネントインスタンスにバインドして、this
がそれを参照するようにする必要もあります。
Morhausは正しいですが、これはbind
がなくても解決できます。
クラスのプロパティプロポーザル と一緒に arrow function を使用することができます。
class SomeClass extends React.Component {
changeContent = (e) => {
this.setState({inputContent: e.target.value})
}
render() {
return <input type="text" onChange={this.changeContent} />;
}
}
Arrow関数はコンストラクタのスコープ内で宣言されているので、そしてarrow関数は宣言しているスコープからthis
を維持しているので、すべてうまくいきます。ここでの欠点は、これらがプロトタイプ上の関数ではなく、それらがすべて各コンポーネントで再作成されることです。ただし、bind
は同じ結果になるため、これはあまりマイナス面ではありません。
この問題は、React.createClass()
コンポーネント定義構文からES6クラスのReact.Component
を拡張する方法に移行する際に、私たちが最初に経験することの1つです。
これはReact.createClass()
とextends React.Component
のthis
コンテキストの違いが原因です。
React.createClass()
を使用すると自動的にthis
コンテキスト(values)を正しくバインドしますが、ES6クラスを使用する場合はそうではありません。 ES6の方法で(React.Component
を拡張することによって)行う場合、this
コンテキストはデフォルトでnull
です。クラスのプロパティはReactクラス(コンポーネント)インスタンスに自動的にはバインドされません。
私は全部で4つの一般的なアプローチを知っています。
クラスコンストラクタ内で関数をバインドします。 JSXに触れることをまったく避け、各コンポーネントの再レンダリングで新しい関数を作成しない、ベストプラクティスのアプローチとして多くの人に考えられています。
class SomeClass extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log(this); // the React Component instance
}
render() {
return (
<button onClick={this.handleClick}></button>
);
}
}
関数をインラインでバインドします。あなたはまだいくつかのtutorials/articles/etcであちこちで使われているこのアプローチを見つけることができるので、あなたがそれを知っていることは重要です。これは#1と同じ概念ですが、関数をバインドすると、再レンダリングごとに新しい関数が作成されることに注意してください。
class SomeClass extends React.Component {
handleClick() {
console.log(this); // the React Component instance
}
render() {
return (
<button onClick={this.handleClick.bind(this)}></button>
);
}
}
太い矢印機能を使用します。矢印関数まで、すべての新しい関数は独自のthis
値を定義しました。ただし、arrow関数は独自のthis
コンテキストを作成しないため、this
はReactコンポーネントインスタンスの本来の意味を持ちます。したがって、次のことが可能です。
class SomeClass extends React.Component {
handleClick() {
console.log(this); // the React Component instance
}
render() {
return (
<button onClick={ () => this.handleClick() }></button>
);
}
}
または
class SomeClass extends React.Component {
handleClick = () => {
console.log(this); // the React Component instance
}
render() {
return (
<button onClick={this.handleClick}></button>
);
}
}
ユーティリティ関数ライブラリを使用して自動的に関数をバインドします。そこにいくつかのユーティリティライブラリがあり、それは自動的にあなたのために仕事をします。ここでは、いくつか言及するだけで人気のあるものをいくつか紹介します。
Autobind Decorator は、メソッドが分離されている場合でも、クラスのメソッドをthis
の正しいインスタンスにバインドするNPMパッケージです。パッケージは、メソッドの前に@autobind
を使用して、コンポーネントのコンテキストへの正しい参照にthis
をバインドします。
import autobind from 'autobind-decorator';
class SomeClass extends React.Component {
@autobind
handleClick() {
console.log(this); // the React Component instance
}
render() {
return (
<button onClick={this.handleClick}></button>
);
}
}
Autobind Decoratorは、アプローチ#1と同じように、コンポーネントクラス内のすべてのメソッドを一度にバインドできるほど賢いものです。
Class Autobind このバインディングの問題を解決するために広く使用されているもう1つのNPMパッケージです。 Autobind Decoratorとは異なり、decoratorパターンは使用しませんが、実際にはコンストラクタ内でコンポーネントのメソッドを正しい参照に自動的にバインドする関数を使用するだけです。 this
の.
import autobind from 'class-autobind';
class SomeClass extends React.Component {
constructor() {
autobind(this);
// or if you want to bind only only select functions:
// autobind(this, 'handleClick');
}
handleClick() {
console.log(this); // the React Component instance
}
render() {
return (
<button onClick={this.handleClick}></button>
);
}
}
PS:他の非常によく似たライブラリは React Autobind です。
私があなただったら、私はアプローチ#1に固執するでしょう。ただし、クラスコンストラクターで大量のバインドが発生したら、すぐにアプローチ4で説明したヘルパーライブラリのいずれかを探索することをお勧めします。
それはあなたが抱えている問題とは無関係ですが、あなたは refを使いすぎてはいけません 。
あなたの最初の傾向はあなたのアプリで "物事を起こさせる"ために参照を使うことかもしれません。その場合は、少し時間をかけて、コンポーネント階層のどこで状態を所有するのかについて、もっと批判的に考えてください。
同様の目的のために、あなたが必要とするものと同じように、 制御されたコンポーネント を使うことが好ましい方法です。 Component state
を使用することを検討することをお勧めします。だから、あなたは単にこのような値にアクセスすることができます:this.state.inputContent
。
これまでの回答ではソリューションの基本的な概要(つまり、バインディング、矢印関数、これを行うデコレーター)を提供しましたが、実際に説明するwhyこれは必要です。これは混乱の根源であり、不必要な再バインドや他の人がやっていることを盲目的にたどるなどの不必要なステップにつながります。
this
は動的ですこの特定の状況を理解するには、this
がどのように機能するかを簡単に紹介します。ここで重要なことは、this
はランタイムバインディングであり、現在の実行コンテキストに依存するということです。したがって、一般に「コンテキスト」と呼ばれるのはなぜですか。現在の実行コンテキストに関する情報を提供し、バインドする必要があるのは、「コンテキスト」を失うためです。しかし、スニペットの問題を説明しましょう:
const foobar = {
bar: function () {
return this.foo;
},
foo: 3,
};
console.log(foobar.bar()); // 3, all is good!
この例では、予想どおり3
を取得します。しかし、次の例を見てください。
const barFunc = foobar.bar;
console.log(barFunc()); // Uh oh, undefined!
3
はどこに行きましたか?答えは"context"、またはexecuteの機能にあります。関数の呼び出し方法を比較します。
// Example 1
foobar.bar();
// Example 2
const barFunc = foobar.bar;
barFunc();
違いに注意してください。最初の例では、bar
メソッドの正確な場所を指定しています1 foobar
オブジェクトにあります:
foobar.bar();
^^^^^^
ただし、2番目の方法では、メソッドを新しい変数に格納し、その変数を使用して、メソッドが実際に存在する場所を明示せずにメソッドを呼び出します、したがってコンテキストを失います:
barFunc(); // Which object is this function coming from?
そして、そこに問題があります。変数にメソッドを保存すると、そのメソッドの場所(メソッドが実行されているコンテキスト)に関する元の情報が失われます。この情報がないと、実行時に、JavaScriptインタープリターが正しいthis
をバインドする方法がありません。特定のコンテキストがないと、this
は期待どおりに機能しません2。
以下は、this
の問題に悩まされているReactコンポーネント(簡潔にするために短縮された)の例です。
handleClick() {
this.setState(({ clicks }) => ({ // setState is async, use callback to access previous state
clicks: clicks + 1, // increase by 1
}));
}
render() {
return (
<button onClick={this.handleClick}>{this.state.clicks}</button>
);
}
しかし、なぜ、前のセクションはこれにどのように関連していますか?これは、同じ問題の抽象化に苦しんでいるためです。 Reactがイベントハンドラーを処理する :
// Edited to fit answer, React performs other checks internally
// props is the current React component's props, registrationName is the name of the event handle prop, i.e "onClick"
let listener = props[registrationName];
// Later, listener is called
したがって、onClick={this.handleClick}
を実行すると、メソッドthis.handleClick
が最終的に変数listener
に割り当てられます3。しかし、this.handleClick
をlistener
に割り当てたため、handleClick
の発信元を正確に指定しなくなったため、問題が発生しました。 Reactの観点から見ると、listener
は単なるオブジェクトであり、オブジェクト(この場合はReactコンポーネントインスタンス)に関連付けられていません。コンテキストが失われたため、インタープリターはthis
の値を使用してinsidehandleClick
を推測できません。
インタプリタが実行時にthis
値を決定する場合、なぜハンドラをバインドしてdoes work?これは、Function#bind
を使用して、実行時にguaranteethis
値を使用できるためです。これは、関数に内部this
バインディングプロパティを設定することで行われ、this
を推測しないようにします。
this.handleClick = this.handleClick.bind(this);
おそらくコンストラクターでこの行が実行されると、現在のthis
がキャプチャされます(Reactコンポーネントインスタンス)から、まったく新しい関数の内部this
バインディングとして設定され、 Function#bind
。これにより、実行時にthis
が計算されるときに、インタープリターは何も推測しようとせず、指定されたthis
値を使用します。
現在、矢印関数クラスのプロパティは、トランスピレーションに基づいてBabelで機能します。
handleClick = () => { /* Can use this just fine here */ }
になる:
constructor() {
super();
this.handleClick = () => {}
}
そして、これは、矢印関数がnot独自のthisをバインドするが、それらを囲むスコープのthis
を取るという事実のために機能します。この場合、constructor
のthis
は、Reactコンポーネントインスタンスを指し、正しいthis
を提供します。4
1 オブジェクトにバインドされることになっている関数を参照するために「メソッド」を使用し、そうでない場合には「関数」を使用します。
2 2番目のスニペットでは、this
がデフォルトのグローバル実行コンテキスト(strictモードでない場合はwindow
、または特定のコンテキストを介して決定できない場合はundefined
)にデフォルト設定されるため、3ではなくundefinedが記録されます。また、例ではwindow.foo
は存在しないため、未定義になります。
3 イベントキュー内のイベントが実行される方法のうさぎの穴を下る場合、リスナーで invokeGuardedCallback
が呼び出されます。
4 実際にはもっと複雑です。 Reactは、独自に使用するためにリスナーでFunction#apply
を内部的に使用しようとしますが、this
をバインドしないため、矢印関数は機能しません。つまり、矢印関数内のthis
が実際に評価されると、モジュールの現在のコードの各実行コンテキストの各字句環境でthis
が解決されます。最終的にthis
バインディングを持つように解決される実行コンテキストisコンストラクター。現在のReactコンポーネントインスタンスを指すthis
があり、機能することができます。
私のお勧めは、プロパティとして矢印関数を使うことです。
class SomeClass extends React.Component {
handleClick = () => {
console.log(this); // the React Component instance
}
render() {
return (
<button onClick={this.handleClick}></button>
);
}
}
と同様に矢印機能を使用しないでください。
class SomeClass extends React.Component {
handleClick(){
console.log(this); // the React Component instance
}
render() {
return (
<button onClick={()=>{this.handleClick}}></button>
);
}
}
2番目のアプローチでは、実際にはレンダリング呼び出しごとに新しい関数が生成されるため、後でパフォーマンスを気にする場合はReact.PureComponentまたはReact.ComponentをオーバーライドすることができますshouldComponentUpdate(nextProps、nextState)そして小道具が到着したときに浅いチェック
次のようにコンストラクタ内のイベント関数とコンポーネントをバインドする必要があります。
import React from 'react'
class SomeClass extends React.Component {
constructor(props) {
super(props)
this.state = {inputContent: 'startValue'}
this.changeContent = this.changeContent.bind(this);
}
sendContent(e) {
console.log('sending input content '+React.findDOMNode(React.refs.someref).value)
}
changeContent(e) {
this.setState({inputContent: e.target.value})
}
render() {
return (
<div>
<h4>The input form is here:</h4>
Title:
<input type="text" ref="someref" value={this.inputContent}
onChange={this.changeContent} />
<button onClick={this.sendContent}>Submit</button>
</div>
)
}
}
export default SomeClass
ありがとう
あなたはこれらのステップに従ってこれを解決することができます
SendContent関数を次のように変更します。
sendContent(e) {
console.log('sending input content '+this.refs.someref.value)
}
でレンダリング機能を変更
<input type="text" ref="someref" value={this.state.inputContent}
onChange={(event)=>this.changeContent(event)} />
<button onClick={(event)=>this.sendContent(event)}>Submit</button>
クラス内の関数のインスタンスを取得するには、関数をbind
でthis
にする必要があります。例のように
<button onClick={this.sendContent.bind(this)}>Submit</button>
このようにしてthis.state
は有効なオブジェクトになります。
3つの方法でこれに取り組むことができます
1.次のようにコンストラクタ自体にイベント関数をバインドする
import React from 'react'
class SomeClass extends React.Component {
constructor(props) {
super(props)
this.state = {inputContent: 'startValue'}
this.changeContent = this.changeContent.bind(this);
}
sendContent(e) {
console.log('sending input content '+React.findDOMNode(React.refs.someref).value)
}
changeContent(e) {
this.setState({inputContent: e.target.value})
}
render() {
return (
<div>
<h4>The input form is here:</h4>
Title:
<input type="text" ref="someref" value={this.inputContent}
onChange={this.changeContent} />
<button onClick={this.sendContent}>Submit</button>
</div>
)
}
}
export default SomeClass
2.呼び出されたらバインドする
import React from 'react'
class SomeClass extends React.Component {
constructor(props) {
super(props)
this.state = {inputContent: 'startValue'}
}
sendContent(e) {
console.log('sending input content '+React.findDOMNode(React.refs.someref).value)
}
changeContent(e) {
this.setState({inputContent: e.target.value})
}
render() {
return (
<div>
<h4>The input form is here:</h4>
Title:
<input type="text" ref="someref" value={this.inputContent}
onChange={this.changeContent} />
<button onClick={this.sendContent.bind(this)}>Submit</button>
</div>
)
}
}
export default SomeClass
3.矢印機能を使って
import React from 'react'
class SomeClass extends React.Component {
constructor(props) {
super(props)
this.state = {inputContent: 'startValue'}
}
sendContent(e) {
console.log('sending input content '+React.findDOMNode(React.refs.someref).value)
}
changeContent(e) {
this.setState({inputContent: e.target.value})
}
render() {
return (
<div>
<h4>The input form is here:</h4>
Title:
<input type="text" ref="someref" value={this.inputContent}
onChange={this.changeContent} />
<button onClick={()=>this.sendContent()}>Submit</button>
</div>
)
}
}
export default SomeClass
イベントハンドラで状態や小道具を操作するには、関数にバインディングが必要です。
ES5では、イベントハンドラー関数はコンストラクター内でのみバインドし、renderでは直接バインドしないでください。あなたが直接renderでバインディングをするならば、それはあなたのコンポーネントがレンダリングして再レンダリングする度に新しい関数を作成します。だからあなたは常にコンストラクタでそれをバインドするべきです
this.sendContent = this.sendContent.bind(this)
ES6では、矢印機能を使う
あなたが矢印関数を使うとき、あなたは束縛をする必要はなく、あなたはスコープ関連の問題からも遠ざかることができます
sendContent = (event) => {
}
溶液:
bind
name__にメソッド名を付けると、this
name__のコンテキストを維持する太い矢印関数の構文(> {})==を使用できます。import React from 'react'
class SomeClass extends React.Component {
constructor(props) {
super(props)
this.state = {
inputContent: 'startValue'
}
}
sendContent = (e) => {
console.log('sending input content ',this.state.inputContent);
}
changeContent = (e) => {
this.setState({inputContent: e.target.value},()=>{
console.log('STATE:',this.state);
})
}
render() {
return (
<div>
<h4>The input form is here:</h4>
Title:
<input type="text" value={this.state.inputContent}
onChange={this.changeContent} />
<button onClick={this.sendContent}>Submit</button>
</div>
)
}
}
export default SomeClass
その他の解決策:
クラスコンストラクターで関数をバインドします。
JSXテンプレート内の関数を中括弧を使用してバインドする{} {this.methodName.bind(this)}
Alexandre Kirszenbergは正しいですが、注意を払うべきもう一つの重要なことは、あなたがあなたの束縛を置くところです。私は何日もの間状況にとどまっていましたが(おそらく私は初心者だからです)、他の人とは違って、私はバインドについて知っていたので(私はすでにこれを適用しました)エラーバインドの順序が間違っていたことがわかりました。
もう1つの理由は、 "this.state"内で関数を呼び出していたことです。これは、バインド行を超えているためにバインドを認識していませんでした。
以下は私が持っていたものです(ところで、これは私の初めての投稿ですが、私は他に解決策を見つけることができなかったので、私はそれが非常に重要だと思いました):
constructor(props){
super(props);
productArray=//some array
this.state={
// Create an Array which will hold components to be displayed
proListing:productArray.map(product=>{return(<ProRow dele={this.this.popRow()} prodName={product.name} prodPrice={product.price}/>)})
}
this.popRow=this.popRow.bind(this);//This was the Issue, This line //should be kept above "this.state"
ES6を使用しているので、関数は "this"コンテキストに自動的にはバインドされません。手動で関数をコンテキストにバインドする必要があります。
constructor(props) {
super(props);
this.changeContent = this.changeContent.bind(this);
}
バインドをコンストラクタ構文で保持したい場合は、 Proposal-bind-operator を使用して、コードを次のように変換します。
constructor() {
this.changeContent = ::this.changeContent;
}
の代わりに :
constructor() {
this.changeContent = this.changeContent.bind(this);
}
はるかに単純で、bind(this)
やfatArrow
は不要です。
this.changeContent
とonClick={this.sendContent}
は、コンポーネントのインスタンスの this にバインドされていないため、この問題は発生しています。
ES6のarrow関数を使用して周囲のコードと同じ字句スコープを共有し、 this を維持するための別の解決策(constructor()でbind()を使用することに加えて)があります。 render()は次のようになります。
render() {
return (
<input type="text"
onChange={ () => this.changeContent() } />
<button onClick={ () => this.sendContent() }>Submit</button>
)
}
この問題はreact15.0以降に発生します。このイベントハンドラはコンポーネントに自動バインドしませんでした。そのため、イベントハンドラが呼び出されるたびにこれを手動でコンポーネントにバインドする必要があります。
この問題を解決する方法はいくつかあります。しかし、あなたはどの方法が最善か、そしてその理由を知る必要がありますか?一般的に、クラスコンストラクタ内であなたの関数を束縛するか、あるいは矢印関数を使うことをお勧めします。
// method 1: use a arrow function
class ComponentA extends React.Component {
eventHandler = () => {
console.log(this)
}
render() {
return (
<ChildComponent onClick={this.eventHandler} />
);
}
// method 2: Bind your functions in the class constructor.
class ComponentA extends React.Component {
constructor(props) {
super(props);
this.eventHandler = this.eventHandler.bind(this);
}
render() {
return (
<ChildComponent onClick={this.eventHandler} />
);
}
これら2つのメソッドは、コンポーネントが毎回レンダリングされるときに新しい関数を作成することはありません。だから私たちのChildComponentは新しい関数の小道具の変更のためにreRenderしないでしょう、あるいはパフォーマンスの問題を引き起こすかもしれません。
bind(this)
はこの問題を修正できますが、最近ではbind
を使用したくない場合、これを達成するために別の2つの方法を使用できます。
1)従来の方法として、コンストラクターでbind(this)
を使用できるため、関数をJSXコールバックとして使用する場合、this
のコンテキストはクラス自体になります。
class App1 extends React.Component {
constructor(props) {
super(props);
// If we comment out the following line,
// we will get run time error said `this` is undefined.
this.changeColor = this.changeColor.bind(this);
}
changeColor(e) {
e.currentTarget.style.backgroundColor = "#00FF00";
console.log(this.props);
}
render() {
return (
<div>
<button onClick={this.changeColor}> button</button>
</div>
);
}
}
2)関数を矢印関数を持つクラスの属性/フィールドとして定義する場合、bind(this)
を使用する必要はありません。
class App2 extends React.Component {
changeColor = e => {
e.currentTarget.style.backgroundColor = "#00FF00";
console.log(this.props);
};
render() {
return (
<div>
<button onClick={this.changeColor}> button 1</button>
</div>
);
}
}
3)JSXコールバックとして矢印関数を使用する場合、bind(this)
も使用する必要はありません。さらに、パラメーターを渡すことができます。よさそうですね。しかし、その欠点はパフォーマンスの問題です。詳細については ReactJS doco を参照してください。
class App3 extends React.Component {
changeColor(e, colorHex) {
e.currentTarget.style.backgroundColor = colorHex;
console.log(this.props);
}
render() {
return (
<div>
<button onClick={e => this.changeColor(e, "#ff0000")}> button 1</button>
</div>
);
}
}
そして、これらのコードスニペットをデモするために Codepen を作成しました。
関数呼び出しを自分で束縛することを気にしたくない場合は、こんにちは。 'class-autobind'を使用してそれをインポートすることができます
import autobind from 'class-autobind';
class test extends Component {
constructor(props){
super(props);
autobind(this);
}
それが動作しませんので、スーパーコールの前に自動バインドを書かないでください