RadekAdler
RadekAdler

Reputation: 79

'This' is undefined when click event method is launched

I am very confused, because when I fill this.state.seats by values, it correctly render all components in DOM. But when I click on that component (button), it returns back to App.js and shows me:

Uncaught TypeError: Cannot read property 'state' of undefined

even though, the components from this state property are displayed in DOM!

Please, does anyone know what happens?

enter image description here

App.js

import React, { Component } from "react";
import "./App.css";
import Seats from "./Seats";

class App extends Component {
  state = {
    seats: this.initializeSeats()
  };

  initializeSeats() {
    let seats = [];
    let count = 5;
    for (let a = 0; a < count; a++) {
      for (let b = 0; b < count; b++) {
        seats.push({ key: '' + a + b, reserved: false });
      }
    }
    seats.find(s => s.key === '00').reserved = true;
    return seats;
  }

  onClickSeat(e) {
    const seats = [...this.state.seats];
    let seat = seats.find(s => s.key === e.target.value);
    seat.reserved = !seat.reserved;
    console.log(seat.reserved);
    this.setState({ seats: seats });
  }

  render() {
    return (
      <div>
        <h3>Kinosál</h3>
        <Seats
          seats={this.state.seats}
          onClickSeat={this.onClickSeat}
        />
      </div>
    );
  }
}
export default App;

Seats.jsx

import React, { Component } from "react";
import Seat from "./Seat";

class Seats extends Component {
  render() {
    const result = [];
    for (let seat of this.props.seats) {
      if (!seat.reserved) {
        result.push({ key: seat.key, reserved: seat.reserved });
      }
    }
    return (
      <div>
        {result.map(seat => (
          <Seat
            key={seat.key}
            onClick={this.props.onClickSeat}
            seat={seat}
          />
        ))}
      </div>
    );
  }
}
export default Seats;

Seat.jsx

import React, { Component } from "react";
import uuid from 'uuid';

class Seat extends Component {
  render() {
    const { seat, onClick } = this.props;
    return (
      <div>
        <button onClick={onClick} key={uuid.v4()} value={seat.key}>{seat.key}</button>
      </div>
    );
  }
}
export default Seat;

Upvotes: 0

Views: 402

Answers (1)

Matthew Liu
Matthew Liu

Reputation: 319

take a look at https://reactjs.org/docs/handling-events.html

You have to be careful about the meaning of this in JSX callbacks. In JavaScript, class methods are not bound by default. If you forget to bind this.handleClick and pass it to onClick, this will be undefined when the function is actually called.

You have to bind onClickSeat to the App class instance of this you can use the class arrow syntax below to do so.

  onClickSeat = (e) => {
    const seats = [...this.state.seats];
    let seat = seats.find(s => s.key === e.target.value);
    seat.reserved = !seat.reserved;
    console.log(seat.reserved);
    this.setState({ seats: seats });
  }

Once you do that, everything should work! It also explains why you can see the components in the DOM, but onClickSeat has its state undefined (it's because this in onClickSeat is NOT referring to the class instance as you were expecting)

Upvotes: 1

Related Questions