57

I'm getting this error Uncaught TypeError: Cannot read property 'state' of undefined whenever I type anything in the input box of AuthorForm. I'm using React with ES7.

The error occurs on 3rd line of setAuthorState function in ManageAuthorPage. Regardless of that line of code even if I put a console.log(this.state.author) in setAuthorState, it will stop at the console.log and call out the error.

Can't find similar issue for someone else over the internet.

Here is the ManageAuthorPage code:

import React, { Component } from 'react';
import AuthorForm from './authorForm';

class ManageAuthorPage extends Component {
  state = {
    author: { id: '', firstName: '', lastName: '' }
  };

  setAuthorState(event) {
    let field = event.target.name;
    let value = event.target.value;
    this.state.author[field] = value;
    return this.setState({author: this.state.author});
  };

  render() {
    return (
      <AuthorForm
        author={this.state.author}
        onChange={this.setAuthorState}
      />
    );
  }
}

export default ManageAuthorPage 

And here is the AuthorForm code:

import React, { Component } from 'react';

class AuthorForm extends Component {
  render() {
    return (
      <form>
                <h1>Manage Author</h1>
        <label htmlFor="firstName">First Name</label>
                <input type="text"
                    name="firstName"
          className="form-control"
                    placeholder="First Name"
          ref="firstName"
          onChange={this.props.onChange}
                    value={this.props.author.firstName}
          />
        <br />

        <label htmlFor="lastName">Last Name</label>
                <input type="text"
                    name="lastName"
          className="form-control"
                    placeholder="Last Name"
          ref="lastName"
                    onChange={this.props.onChange}
          value={this.props.author.lastName}
                    />

                <input type="submit" value="Save" className="btn btn-default" />
            </form>
    );
  }
}

export default AuthorForm
Khpalwalk
  • 849
  • 1
  • 6
  • 11
  • 1
    You MUST NOT modify state directly. There is `asynchronous` **setState(...)** for this. I am writing about this statement `this.state.author[field] = value;`. – Leonid Dashko Sep 13 '18 at 19:45

2 Answers2

80

You have to bind your event handlers to correct context (this):

onChange={this.setAuthorState.bind(this)}
madox2
  • 39,489
  • 13
  • 88
  • 91
  • 1
    I used your example mate. Thanks. – Khpalwalk Feb 09 '16 at 09:18
  • @Khpalwalk You are welcome :-) – madox2 Feb 09 '16 at 09:19
  • 7
    I find a more eloquent way of doing this is by using arrow functions which will inherently bind this to the function, I find it especially useful when writing react code. So you can use setAuthorState=event=>{...}. No extra lines of code are necessary to bind this to the function. Also, every time this component is clicked it doesn't require the extra costs of binding this. – Brett Reinhard Aug 05 '17 at 06:39
  • @BrettReinhard you are right, currently that's probably the most convenient way. Anyway, JS programmers need to understand the reason of context binding – madox2 Aug 05 '17 at 08:20
  • 3
    I agree, I feel as though 'this' eludes many developers and they don't fully understand what 'this' truly represents. I feel as though in react developers have a better understanding of 'this', now whether that is purely superficial is another thing. For those that don't understand 'this' should take a look at Kyle Simpsons , you don't know js repo on github. His username is getify, and has a few chapters dedicated to 'this' that will help get the ball rolling in understanding what 'this' means in the given context. – Brett Reinhard Aug 07 '17 at 05:40
  • 2
    yes thank u it is working for me. I forgot to to use bind in my code. – Faris Rayhan Mar 12 '18 at 06:59
  • yes thank u it is working for me. I forgot to to use bind in my code. – Faris Rayhan Mar 12 '18 at 06:59
  • do that in your constructor – imran haider Mar 05 '21 at 08:01
58

Make sure you're calling super() as the first thing in your constructor.

You should set this for setAuthorState method

class ManageAuthorPage extends Component {

  state = {
    author: { id: '', firstName: '', lastName: '' }
  };

  constructor(props) {
    super(props);
    this.handleAuthorChange = this.handleAuthorChange.bind(this);
  } 

  handleAuthorChange(event) {
    let {name: fieldName, value} = event.target;

    this.setState({
      [fieldName]: value
    });
  };

  render() {
    return (
      <AuthorForm
        author={this.state.author}
        onChange={this.handleAuthorChange}
      />
    );
  }
}

Another alternative based on arrow function:

class ManageAuthorPage extends Component {

  state = {
    author: { id: '', firstName: '', lastName: '' }
  };

  handleAuthorChange = (event) => {
    const {name: fieldName, value} = event.target;

    this.setState({
      [fieldName]: value
    });
  };

  render() {
    return (
      <AuthorForm
        author={this.state.author}
        onChange={this.handleAuthorChange}
      />
    );
  }
}
Oleksandr T.
  • 69,412
  • 16
  • 152
  • 136