반응 :“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 함수를 반응 클래스로 이동 한 후 onClick 속성을 변경 하면 작동합니다.
Sam

71
모든 반응 클래스의 모든 메소드를 실제로 바인딩해야합니까? 조금 미쳤어?
Alex L

6
@AlexL 메소드를 명시 적으로 바인딩하지 않고 할 수있는 방법이 있습니다. babel을 사용하면 React 컴포넌트의 모든 메소드를 화살표 함수로 선언 할 수 있습니다. 여기에 예가 있습니다 : babeljs.io/blog/2015/06/07/react-on-es6-plus
Ivan

7
그러나 왜 this처음에 정의되지 않은가? 내가 알고있는 this자바 스크립트 함수가 호출 방법에 따라 달라집니다에,하지만 여기에 무슨 일?
maverick

1
하지만 어떻게하면 생성자가 생성 될 때까지 함수가 정의되지 않습니까? 내 함수가 클래스에 정의되어 있어도 생성자에서이 작업을 수행하려고하면 "정의되지 않은 속성 '바인드'를 읽을 수 없습니다"가 표시됩니다. (
rex

86

몇 가지 방법이 있습니다.

하나는 this.onToggleLoop = this.onToggleLoop.bind(this);생성자 를 추가 하는 것입니다.

또 다른 하나는 화살표 기능 onToggleLoop = (event) => {...}입니다.

그리고있다 onClick={this.onToggleLoop.bind(this)}.


1
왜 onToogleLoop = () => {}가 작동합니까? 나는 똑같은 문제를 겪었고 생성자에서 그것을 묶었지만 작동하지 않았다 ... 그리고 지금 당신의 게시물을 보았고 화살표 함수 구문으로 내 메소드를 대체하고 작동합니다. 당신은 나에게 그것을 설명 할 수 있습니까?
Guchelkaben

5
에서 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/... ; 화살표 함수는 자체적으로 이것을 생성하지 않으며, 둘러싸는 실행 컨텍스트의이 값이 사용됩니다.
J. Mark Stevens

1
의 인라인 바인딩 onClick은 렌더마다 새로운 함수를 반환하므로 prop에 대해 새로운 값이 전달 shouldComponentUpdate되어 PureComponents 에서 엉망이 되는 것처럼 보입니다 .
Ninjakannon

24

이 방법으로 함수를 작성하십시오.

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

팻 화살표 기능

키워드의 바인딩은 지방 화살표 함수의 외부와 내부에서 동일합니다. 이것은 함수로 선언 된 함수와 다르며 호출시 이것을 다른 객체에 바인딩 할 수 있습니다. 이 바인딩을 유지하는 것은 매핑과 같은 작업에 매우 편리합니다 : this.items.map (x => this.doSomethingWith (x)).


내가 그렇게하면 얻을 ReferenceError: fields are not currently supported.
Pavel Komarov 2016 년

생성자 내부에서 this.func = () => {...}라고 말하면 작동하지만 구피의 일종으로 간주하고 가능하면 피하고 싶습니다.
Pavel Komarov 2016 년

너무 끔찍해서 React에서 일반 클래스 구문을 사용할 수 없습니다!
Kokodoko

11

렌더링 함수에서 비슷한 바인딩을 실행 this하여 다음과 같은 방식으로 컨텍스트를 전달했습니다 .

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

나는 또한 사용했다 :

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

그것은 당신이 거기에 생성하는 많은 불필요한 기능입니다. 매번 목록이 렌더링 될 때마다 ...
TJ Crowder

@TJCrowder 네, 렌더가 호출 될 때마다이 함수들이 새로 만들어집니다. 그것은 클래스에 일단 결합하고이를 클래스 메소드와 같은 기능을 작성하는 것이 좋습니다,하지만 초보자를위한 바인딩 매뉴얼 문맥은 도움이 될 수있다
duhaime

2

당신은 그주의해야 this객체의 메서드가 그 같이 함수를 호출 할 때 : 기능은 예를 호출하는 방법에 따라 달라 this메소드가 호출 된 객체로 설정됩니다.

thisJSX 컨텍스트에서 컴포넌트 객체로 액세스 할 수 있으므로 원하는 메소드를 메소드로 인라인으로 호출 할 수 있습니다 this.

함수 / 메소드에 대한 참조를 전달하면 반응이 독립적 인 함수로 호출하는 것처럼 보입니다.

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'*/
그렇습니다

1

babel을 사용하는 경우 ES7 바인드 연산자 https://babeljs.io/docs/en/babel-plugin-transform-function-bind#auto-self-binding을 사용하여 'this'를 바인딩합니다.

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)및 fat 화살표 함수 만 사용할 수 있습니다 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은 'this'에 해당하는 액세스 권한이 없습니다)

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.