React: "this" is undefined inside a component function

JavascriptReactjsThis

Javascript Problem Overview


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()
  }

I want to update loopActive state on toggle, but this object is undefined in the handler. According to the tutorial doc, I this should refer to the component. Am I missing something?

Javascript Solutions


Solution 1 - Javascript

ES6 React.Component doesn't auto bind methods to itself. You need to bind them yourself in constructor. Like this:

constructor (props){
  super(props);
  
  this.state = {
      loopActive: false,
      shuffleActive: false,
    };
  
  this.onToggleLoop = this.onToggleLoop.bind(this);

}

Solution 2 - Javascript

There are a couple of ways.

One is to add this.onToggleLoop = this.onToggleLoop.bind(this); in the constructor.

Another is arrow functions onToggleLoop = (event) => {...}.

And then there is onClick={this.onToggleLoop.bind(this)}.

Solution 3 - Javascript

Write your function this way:

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

Fat Arrow Functions

> the binding for the keyword this is the same outside and inside the fat arrow function. This is different than functions declared with function, which can bind this to another object upon invocation. Maintaining the this binding is very convenient for operations like mapping: this.items.map(x => this.doSomethingWith(x)).

Solution 4 - Javascript

I ran into a similar bind in a render function and ended up passing the context of this in the following way:

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

I've also used:

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

Solution 5 - Javascript

You should notice that this depends on how function is invoked ie: when a function is called as a method of an object, its this is set to the object the method is called on.

this is accessible in JSX context as your component object, so you can call your desired method inline as this method.

If you just pass reference to function/method, it seems that react will invoke it as independent function.

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

Solution 6 - Javascript

in my case this was the solution = () => {}

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

Solution 7 - Javascript

If you are using babel, you bind 'this' using ES7 bind operator 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>
    )
  }

}

Solution 8 - Javascript

If you call your created method in the lifecycle methods like componentDidMount... then you can only use the this.onToggleLoop = this.onToogleLoop.bind(this) and the fat arrow function onToggleLoop = (event) => {...}.

The normal approach of the declaration of a function in the constructor wont work because the lifecycle methods are called earlier.

Solution 9 - Javascript

In my case, for a stateless component that received the ref with forwardRef, I had to do what it is said here https://itnext.io/reusing-the-ref-from-forwardref-with-react-hooks-4ce9df693dd

From this (onClick doesn't have access to the equivalent of 'this')

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

To this (it works)

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} } />
})

Solution 10 - Javascript

You can rewrite how your onToggleLoop method is called from your render() method.

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={(event) => this.onToggleLoop(event)}
      spin={this.state.loopActive}
    />       
  </div>
    );
  }

The React documentation shows this pattern in making calls to functions from expressions in attributes.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMaximus SView Question on Stackoverflow
Solution 1 - JavascriptIvanView Answer on Stackoverflow
Solution 2 - JavascriptJ. Mark StevensView Answer on Stackoverflow
Solution 3 - JavascriptShaTinView Answer on Stackoverflow
Solution 4 - JavascriptduhaimeView Answer on Stackoverflow
Solution 5 - JavascriptJakub KutrzebaView Answer on Stackoverflow
Solution 6 - JavascriptAlexView Answer on Stackoverflow
Solution 7 - JavascriptHenry JacobView Answer on Stackoverflow
Solution 8 - JavascriptGuchelkabenView Answer on Stackoverflow
Solution 9 - JavascriptGWorkingView Answer on Stackoverflow
Solution 10 - JavascriptT. WebsterView Answer on Stackoverflow