React:「this」はコンポーネント関数内で定義されていません


152
class PlayerControls extends React.Component {
  constructor(props) {
    super(props)

    this.state = {
      loopActive: false,
      shuffleActive: false,
    }
  }

  render() {
    var shuffleClassName = this.state.toggleActive ? "player-control-icon active" : "player-control-icon"

    return (
      <div className="player-controls">
        <FontAwesome
          className="player-control-icon"
          name='refresh'
          onClick={this.onToggleLoop}
          spin={this.state.loopActive}
        />
        <FontAwesome
          className={shuffleClassName}
          name='random'
          onClick={this.onToggleShuffle}
        />
      </div>
    );
  }

  onToggleLoop(event) {
    // "this is undefined??" <--- here
    this.setState({loopActive: !this.state.loopActive})
    this.props.onToggleLoop()
  }

loopActiveトグルで状態を更新したいのですthisが、ハンドラーでオブジェクトが定義されていません。チュートリアルドキュメントによると、thisコンポーネントを参照する必要があります。何か不足していますか?

回答:


211

ES6 React.Componentは、メソッドをそれ自体に自動バインドしません。コンストラクタで自分でバインドする必要があります。このような:

constructor (props){
  super(props);

  this.state = {
      loopActive: false,
      shuffleActive: false,
    };

  this.onToggleLoop = this.onToggleLoop.bind(this);

}

23
() => this.onToggleLooponToggleLoop関数をreactクラスに移動した後でonClickプロパティをに変更した場合も同様に機能します。
2016

71
あなたは本当にすべての反応クラスのすべてのメソッドをバインドする必要がありますか?それは少しおかしくないですか?
Alex L

6
@AlexLメソッドを明示的にバインドせずにそれを行う方法があります。babelを使用する場合、Reactコンポーネントのすべてのメソッドを矢印関数として宣言することができます。ここに例があります:babeljs.io/blog/2015/06/07/react-on-es6-plus
Ivan

7
しかし、thisそもそもなぜ未定義なのでしょうか?私thisはJavaScriptで関数がどのように呼び出されるかに依存することを知っていますが、ここで何が起こっているのですか?
異端者

1
しかし、どのようにこれを行うことができますか?関数はコンストラクタの後まで定義されませんか?私は:(私は、コンストラクタでこれを行うにしようとした場合でも、あなたが私の関数はクラスで定義された「未定義のプロパティを読み取ることができません『バインド』を」取得
レックス

87

いくつかの方法があります。

1つはthis.onToggleLoop = this.onToggleLoop.bind(this);コンストラクタに追加 することです。

もう1つはアロー関数 onToggleLoop = (event) => {...}です。

そして、ありますonClick={this.onToggleLoop.bind(this)}


1
なぜonToogleLoop =()=> {}が機能するのですか?同じ問題が発生し、コンストラクターでバインドしましたが、機能しませんでした。そして、あなたの投稿を見て、私のメソッドを矢印関数の構文に置き換えたところ、機能しました。説明してくれませんか?
Guchelkaben

5
developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…から; アロー関数は独自のthisを作成せず、囲んでいる実行コンテキストのthis値が使用されます。
J.マークスティーブンス

1
インラインを結合することを注意onClickすべてのレンダリング新機能を返却し、新しい値をいじり、小道具のために渡されたようので、それが見えますshouldComponentUpdatePureComponent、S。
忍者観音

24

このように関数を記述します。

onToggleLoop = (event) => {
    this.setState({loopActive: !this.state.loopActive})
    this.props.onToggleLoop()
}

ファットアロー関数

キーワードthisのバインディングは、ファットアロー関数の外側と内側で同じです。これは、呼び出し時にこれを別のオブジェクトにバインドできるfunctionで宣言された関数とは異なります。thisバインディングを維持することは、マッピングのような操作に非常に便利です:this.items.map(x => this.doSomethingWith(x))。


そうするなら、私は得るReferenceError: fields are not currently supported
Pavel Komarov

これは、コンストラクター内でthis.func =()=> {...}と言った場合に機能しますが、これを一種の間抜けなものと見なし、可能であれば避けたいと考えています。
Pavel Komarov

Reactで通常のクラス構文を使用できないほどひどい!
ココドコ

11

私はレンダリング関数で同様のバインドに遭遇しthis、次のようにしてコンテキストを渡しました:

{someList.map(function(listItem) {
  // your code
}, this)}

私も使用しました:

{someList.map((listItem, index) =>
    <div onClick={this.someFunction.bind(this, listItem)} />
)}

それは、不要な機能の多くは、あなたが...、そこにリストがレンダリングされる毎回を作成しているだ
TJクラウダーを

@TJCrowderはい、これらの関数はレンダーが呼び出されるたびに新しく作成されます。関数をクラスメソッドとして作成し、クラスに一度バインドすることをお
勧めし

2

thisつまり、関数の呼び出し方法に依存することに注意してください。つまり、関数がオブジェクトのメソッドとして呼び出されると、関数が呼び出されるオブジェクトにthis設定されます。

thisコンポーネントオブジェクトとしてJSXコンテキストでアクセスできるため、目的のメソッドをメソッドとしてインラインで呼び出すことができますthis

関数/メソッドへの参照を渡すだけの場合、reactはそれを独立した関数として呼び出すようです。

onClick={this.onToggleLoop} // Here you just passing reference, React will invoke it as independent function and this will be undefined

onClick={()=>this.onToggleLoop()} // Here you invoking your desired function as method of this, and this in that function will be set to object from that function is called ie: your component object

1
右、最初の行を使用することもできonClick={this.onToggleLoop}ます。つまり、コンポーネントクラスでフィールド(プロパティ)を定義した場合onToggleLoop = () => /*body using 'this'*/
gvlax

1

バベルを使用している場合は、ES7バインド演算子を使用して「this」をバインドし ますhttps://babeljs.io/docs/en/babel-plugin-transform-function-bind#auto-self-binding

export default class SignupPage extends React.Component {
  constructor(props) {
    super(props);
  }

  handleSubmit(e) {
    e.preventDefault(); 

    const data = { 
      email: this.refs.email.value,
    } 
  }

  render() {

    const {errors} = this.props;

    return (
      <div className="view-container registrations new">
        <main>
          <form id="sign_up_form" onSubmit={::this.handleSubmit}>
            <div className="field">
              <input ref="email" id="user_email" type="email" placeholder="Email"  />
            </div>
            <div className="field">
              <input ref="password" id="user_password" type="new-password" placeholder="Password"  />
            </div>
            <button type="submit">Sign up</button>
          </form>
        </main>
      </div>
    )
  }

}

0

作成したメソッドを、componentDidMount ...のようなライフサイクルメソッドで呼び出す場合は、this.onToggleLoop = this.onToogleLoop.bind(this)およびの矢印機能のみを使用できますonToggleLoop = (event) => {...}

ライフサイクルメソッドが以前に呼び出されているため、コンストラクターでの関数の宣言の通常のアプローチは機能しません。


0

私の場合、これは解決策でした=()=> {}

methodName = (params) => {
//your code here with this.something
}

0

私の場合、forwardRefで参照を受け取ったステートレスコンポーネントの場合、ここで述べられていることを実行する必要がありました。https: //itnext.io/reusing-the-ref-from-forwardref-with-react-hooks-4ce9df693dd

これから(onClickは「これ」に相当するものにアクセスできません)

const Com = forwardRef((props, ref) => {
  return <input ref={ref} onClick={() => {console.log(ref.current} } />
})

これに(それは動作します)

const useCombinedRefs = (...refs) => {
  const targetRef = React.useRef()

  useEffect(() => {
    refs.forEach(ref => {
      if (!ref) return

      if (typeof ref === 'function') ref(targetRef.current)
      else ref.current = targetRef.current
    })
  }, [refs])

  return targetRef
}

const Com = forwardRef((props, ref) => {
  const innerRef = useRef()
  const combinedRef = useCombinedRefs(ref, innerRef)

  return <input ref={combinedRef } onClick={() => {console.log(combinedRef .current} } />
})
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.