0

I have a class component that has a onClick event handler that references an inner ref input. However, in the event handler input is null. I am binding the event handler to this in constructor.

import React, { Component} from 'react';

class MyComponent extends Component {
  constructor(props) {
    super(props);
    this.onClick = this.onClick.bind(this);
  }

  onClick(e) {
    // The following throws "Cannot read property 'click' of undefined"
    this.input.click();
  }

  render() {
    return (
      <div className="container" onClick={this.onClick}>
        <input type="text" ref={input => this.input = input} />
      </div>
    );
  }
}

Why is this.input undefined in my event handler?

EDIT Apparently the code runs fine, just not in my environment. I am using webpack and babel with the env and react presets w/ hot reloading. I am targeting electron.

Full error stack:

my-component.jsx:12 Uncaught TypeError: Cannot read property 'click' of undefined
    at MyComponent.onClick (http://localhost:8080/renderer.js:19224:15)
    at Object.ReactErrorUtils.invokeGuardedCallback (webpack:///./node_modules/react-dom/lib/ReactErrorUtils.js?:69:16)
    at executeDispatch (webpack:///./node_modules/react-dom/lib/EventPluginUtils.js?:85:21)
    at Object.executeDispatchesInOrder (webpack:///./node_modules/react-dom/lib/EventPluginUtils.js?:108:5)
    at executeDispatchesAndRelease (webpack:///./node_modules/react-dom/lib/EventPluginHub.js?:43:22)
    at executeDispatchesAndReleaseTopLevel (webpack:///./node_modules/react-dom/lib/EventPluginHub.js?:54:10)
    at Array.forEach (native)
    at forEachAccumulated (webpack:///./node_modules/react-dom/lib/forEachAccumulated.js?:24:9)
    at Object.processEventQueue (webpack:///./node_modules/react-dom/lib/EventPluginHub.js?:254:7)
    at runEventQueueInBatch (webpack:///./node_modules/react-dom/lib/ReactEventEmitterMixin.js?:17:18)

EDIT

Figured it out see my answer below.

Bob
  • 821
  • 1
  • 17
  • 36
  • There is nothing wrong with this code. It will work. You need to write some more detail. – Prakash Sharma Aug 21 '17 at 17:00
  • i ran your code and it worked... – canaan seaton Aug 21 '17 at 17:04
  • Weird, it works for me when I run it in an online editor but not in my webpack environment. – Bob Aug 21 '17 at 17:18
  • I wasn't able to reproduce the issue you are describing. Are you sure the test case in your question accurately represents the error you are running into? If I recall correctly, attempting to call a method that doesn't exist on an object **does not** throw `Cannot read property 'property' of undefined`; it should throw `object.method is not a function`. – Wing Aug 21 '17 at 17:19
  • [DEMO](http://light-afternoon.surge.sh/) with your code plus style on the container div to make it more visible... check the console to view click event logs... – canaan seaton Aug 21 '17 at 17:22
  • ^ seems to fine, maybe more description on your error – canaan seaton Aug 21 '17 at 17:22
  • I had issues with refs using the same environment as you. What I would up doing was using div id's instead – MatTheWhale Aug 21 '17 at 18:32
  • @MatTheWhale I fixed it if you're interested – Bob Aug 21 '17 at 19:54
  • @Bob Good for you man! Thanks for letting me know, I'll keep this post in mind in the future. In the meantime, make sure you accept your own answer! – MatTheWhale Aug 21 '17 at 19:56

3 Answers3

1

Figured it out, it was a problem with react-hot-loader. Apparently saving the value of this doesn't work in the constructor with react-hot-loader. The fix is to manually enable the transform-es2015-classes plugin in your babelrc.

See https://github.com/gaearon/react-hot-loader/issues/597

Bob
  • 821
  • 1
  • 17
  • 36
0

I think you are trying to get input value. if yes here is the code. There is no this.input.click(); method definition

import React from 'react';

class MyComponent extends React.Component {
   constructor(props) {
    super(props);
    this.onClick = this.onClick.bind(this);
  }

  onClick(e) {
    // The following throws "Cannot read property 'click' of undefined"
    console.log(this.input.value);
  }

  render() {
    return (
      <div className="container">
        <input type="text" ref={input => this.input = input} onChange={this.onClick}/>
      </div>
    );
  }
}

export default MyComponent;
0

Have you tried rewriting your onClick callback as an arrow function? This will also make your code a little smaller:

import React, { Component } from 'react';

class MyComponent extends Component {
  this.input = null;

  onClick = () => {
    this.input && this.input.click();
  }

  render() {
    return (
      <div className="container" onClick={this.onClick}>
        <input type="text" ref={input => this.input = input} />
      </div>
    );
  }
}

Even though it shouldn't matter, I also check if this.input is set - but you can ignore that part (usually).

lumio
  • 7,428
  • 4
  • 40
  • 56
  • Tried this using the transform-class-properties plugin and I get the same result. – Bob Aug 21 '17 at 18:07
  • What happens if you enclose `onClick={ () => console.log( this.onClick ) }` into an arrow function and log it out? – lumio Aug 21 '17 at 18:09