Chris
Chris

Reputation: 17

TypeError: Cannot read property 'addEventListener' of undefined

I got this Error message: "TypeError: Cannot read property 'addEventListener' of undefined" and I don't know why. I think it has to do with the fact that variable "el" loads the first time with the value of zero but I am not sure and I dont know how to solve that issue.

EDIT: I updated the code with the help of the answers. Now I got a new error message "TypeError: Cannot set property 'value' of undefined" https://jsfiddle.net/Van_Gram/4vofz6jh/7/

import React, { Component } from 'react';
import { connect } from 'react-redux'
import { addToCart } from './actions/cartActions'
import StackGrid from "react-stack-grid"
import Tilt from 'react-tilt'


// normally these would be `import` statements bundled by webpack
const { ReactDOM } = window;

 class Home extends Component{

   constructor(props) {
     super(props);
     this.unlockStartHandler = this.unlockStartHandler.bind(this);
     this.unlockEndHandler = this.unlockEndHandler.bind(this);
     this.animateHandler = this.animateHandler.bind(this);
     this.successHandler = this.successHandler.bind(this);
     this.maxValue = 150;
     this.speed = 12;
     this.currValue = 0;
     this.rafID = null;
   }

   /*componentDidMount() {
     // bind events
    this.inputRange.addEventListener('mousedown', this.unlockStartHandler, false);
    this.inputRange.addEventListener('mousestart', this.unlockStartHandler, false);
    this.inputRange.addEventListener('mouseup', this.unlockEndHandler, false);
    this.inputRange.addEventListener('touchend', this.unlockEndHandler, false);
   }*/

   // listen for unlock
   unlockStartHandler(e) {
     // clear raf if trying again
     window.cancelAnimationFrame(this.rafID);
     // set to desired value
     this.currValue = +e.target.value;
   }

   unlockEndHandler(e) {
       // store current value
       this.currValue = +e.target.value;
       // determine if we have reached success or not
       if(this.currValue >= this.maxValue) {
           this.successHandler();
           this.sendData();
       }
       else {
           this.rafID = window.requestAnimationFrame(this.animateHandler);
       }
     }

     // handle range animation
   animateHandler() {
     // update input range
     this.inputRange.value = this.currValue;
     // determine if we need to continue
     if(this.currValue > -1) {
       window.requestAnimationFrame(this.animateHandler);
     }
     // decrement value
     this.currValue = this.currValue - this.speed;
   }

   // handle successful unlock
   successHandler = (prevState) => {
     // alert('unlocked');
     // reset input range
     this.inputRange.value = 0;
   };

   sendData = () => {
     this.props.parentCallback(null);
   }

   callbackFunction = (childData) => {
     /*this.setState((prevState) => ({
       counter: prevState.counter + 1,
     }));*/
   }

    handleClick = (id)=>{
        this.props.addToCart(id);
    }



    render(){
        let itemList = this.props.items.map(item=>{
            return(
              <Tilt className="Tilt" options={{ max : 10 }, { perspective : 3000 }, {scale : 0.97}} >
                <div className="card" key={item.id}>
                  <div className="card-image">
                    <img src={item.img} alt={item.title}/>
                  </div>

                  <div className="card-content">
                    <h2 className="card-title">{item.title}</h2>
                    <p>{item.desc}</p>
                    <div className="slider">
                      <h3>Swipe um zu bestellen</h3>
                      <input
                        type="range"
                        defaultValue={0}
                        min={0}
                        max={150}
                        className="pullee"
                        /*ref={(el) => { this.inputRange = el; }}*/
                      />
                    </div>
                    <span to="/" className="btn-floating halfway-fab waves-effect waves-light red" onClick={()=>{this.handleClick(item.id)}}><i className="material-icons">add</i></span>
                  </div>
                 </div>
               </Tilt>

            )
        })

        return(
            <StackGrid
              columnWidth="33.3%"
              gutterWidth={20}
              gutterHeight={20}
              duration={500}
              className="stackGrid"
            >
              {itemList}
            </StackGrid>
        )
    }
}
const mapStateToProps = (state)=>{
    return {
      items: state.items
    }
  }
const mapDispatchToProps= (dispatch)=>{

    return{
        addToCart: (id)=>{dispatch(addToCart(id))}
    }
}

export default connect(mapStateToProps,mapDispatchToProps)(Home)

Upvotes: 1

Views: 9775

Answers (1)

Andrew
Andrew

Reputation: 7545

It is a race condition. It is because the ref isn't assigning this.inputRange soon enough before componentDidMount, so it is undefined when you try and add event listeners to the input.

You can try and add conditionals and various strategies, but that isn't the "React" way of doing this. It is no coincidence that the React way is also the most straightforward. Change those event listeners to be a synthetic event that React handles for you, by making it an inline prop inside the input element.

Take out the ref unless you need it for other things and add the event listeners inline.

<input
  onMouseDown={this.unlockStartHandler}
  onMouseStart={this.unlockStartHandler}
  // and so on
/>

React event listener prop names are always the normal JS name with on appended to the front (and retaining camelCase naming).

Upvotes: 2

Related Questions