Normandy
Normandy

Reputation: 37

Cannot read property state undefined

I am trying to take input from a user in a form and then submit that information with an alert that shows what the user entered into the form before officially submitting it. However, I cannot figure out how to get solve the issue of state undefined. I initially had the FormCreate and FormDisplay function outside of the class component but still had the same error. After reading for several hours I put them inside the function but still get the same issue.

What am I missing?

'use strict'

let d = React.createElement;



class FormFind extends React.Component {
    constructor(props) {
      super(props);
      this.state= {firstname: "",
        lastname: "",
        email: "" };

this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}




handleChange = (event) => {
this.setState({value: event.target.firstname
});
}

handleSubmit(event) {
alert('Your information is: ' + this.state.value);
event.preventDefault();
}


    render() {


       function FormCreate(props){
        let formMake = React.createElement("label", {key: props.id},React.createElement("br", {},), (React.createElement("input", 
        {type: "text", name: props.item.text, placeholder: props.item.placeholder, size:"auto", required: "required", onChange:(e) => this.setState({value: e.target.value}), 
        value: this.state.value})))

        return formMake
    }

    function FormDisplay(){
        let props = [ {id: 10, text:"fname", placeholder: "Please Enter Your First Name"},
        {id: 11, text: "lname", placeholder: "Please Enter Your Last Name"}, 
        {id: 12, text: "email", placeholder: "Please Enter Your Email"}, 
        {id: 13, text: "Phone", placeholder: "Please Enter Your Phone Number"},
        {id: 14, text: "numtravelers", placeholder: "Please Enter The Number of Children and Adults Going"},
        {id: 15, text: "datetrip", placeholder: "Please Enter Your Date For The Trip"}];  
        var listItems = props.map((item) => FormCreate({key: props.id, item: item})); 

          return(
            React.createElement("div", {}, listItems))
            ;}

            var x = FormDisplay();



      return React.createElement("form", {onSubmit: this.handleSubmit}, (x), React.createElement("input", {type: "submit", value: "Submit"}));

      }
  }

   const domContainer44 = document.getElementById("formfind");

   ReactDOM.render(
     (d(FormFind)),
     domContainer44);```

Upvotes: 0

Views: 496

Answers (3)

Jasper
Jasper

Reputation: 56

import React, { Component, createElement } from 'react' 
const cE = createElement; // you dont need this... the createElement above does the job

class FormFind extends Component {
    constructor(props) {
        super(props);
        this.state = { //intitialized the state, with all properties I plan on having in it 
            firstName: "", 
            lastName: "",
            email: "",
            phone: "",
            numberOfTravelers: "",
            dateTrip: "",
        };
        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
        //there are two functions that I did not bind directly in the constructor
        //formCreate = () => {}
        //formDisplay = () => {}
        //When written in this format they function as if you had bound them in the constructor 
        //as you did handleChange() and handleSubmit()

    }

    handleChange(event) {
        //you had this function creating a new key:value pair with 'value' as the key, not a value from any input 
        //but the string 'value' was being used as the key, and you were giving it a value of undefined because
        //event.target.firstName isnt a part of the input element you just used, instead you would use the name 
        //property of the input element. 
        this.setState({
            [event.target.name]: event.target.value
        });
    }

    handleSubmit(event) {
        event.preventDefault();

        const { firstName, lastName } = this.state
        const fullName = firstName + " " + lastName;

        alert('Your information is: ' + fullName);        
    }

    formCreate = (props) => {
        console.log("PROPS", props)
        let formMake = createElement(
            "label",
            { key: props.key },
            createElement("br", {}),
            (createElement(
                "input",
                {
                    type: "text",
                    name: props.item.text,
                    placeholder: props.item.placeholder,
                    size: "auto",
                    required: "required",
                    onChange: (e) => this.handleChange(e),

                    //You have a handleChange() function that wasnt being used, and
                    //you were trying to setState({}) similar to the way I explained it on the 
                    //handleChangea() function. 

                    // If you would like to use setState()
                    // onChange: (e) => this.setState({ [e.target.name]: e.target.value }),
                    value: this.state.value,
                }
            ))
        )
        return formMake
    }

    formDisplay = () => { 
        let props = [{ id: 10, text: "firstName", placeholder: "Please Enter Your First Name" },
        { id: 11, text: "lastName", placeholder: "Please Enter Your Last Name" },
        { id: 12, text: "email", placeholder: "Please Enter Your Email" },
        { id: 13, text: "phone", placeholder: "Please Enter Your Phone Number" },
        { id: 14, text: "numberOfTravelers", placeholder: "Please Enter The Number of Children and Adults Going" },
        { id: 15, text: "dateTrip", placeholder: "Please Enter Your Date For The Trip" }];
        var listItems = props.map((item) => this.formCreate({ key: item.id, item: item }));

        return (cE("div", {}, listItems));
        //the above cE() is the React.createElement() that we initialized at the top, cE is ambiguous
        //and was used only to press that intializing React.createElement() twice, lines 1 & 2, before using 
        //it is unnecessary and names that are stripped down to far are confusing and bad practice.  

    }
    render() {          

        var x = this.formDisplay();
        return createElement("form", { onSubmit: this.handleSubmit }, (x), createElement("input", { type: "submit", value: "Submit" }));
    }
}

export default FormFind 

I changed a few things around, but this is what you are going for I believe. The only thing you will need to fix is displaying the users information.

Every keystroke from the user causes a redraw of every input element, instead of only the element the user is typing in. It works, but seems inefficient. If it is by a design that I am not seeing, so be it. A simply observation.

Take the time and read through the React documentation, once it clicks React is aweseome. https://reactjs.org/

Check out this information as well, it helped me when I was putting together a form element https://www.taniarascia.com/getting-started-with-react/

Upvotes: 1

Pushkin
Pushkin

Reputation: 3604

Why this.state is undefined?

For that let's look at the render() of yours where it lies, shall we?

render() {
  function FormCreate(props) {
    let formMake = React.createElement(
      "label",
      { key: props.id },
      React.createElement("br", {}),
      React.createElement("input", {
        type: "text",
        name: props.item.text,
        placeholder: props.item.placeholder,
        size: "auto",
        required: "required",
        onChange: e => this.setState({ value: e.target.value }),
        value: this.state.value
      })
    );

    return formMake;
  }
}

Here what you did was, you created a brand new function FormCreate() inside render(). Though it should never be done, we'll assume it can be in our case.

The error was thrown at the line value: this.state.value inside FormCreate(). This is because the this inside FormCreate() is refering to the FormCreate itself and not our React component, which in here is FormFind.

NOTE: FormCreate is different from FormCreate().

Since FormCreate doesn't have state key inside, this.state (or FormCreate.state) is undefined. That is exactly why you were getting the error.

render() {
  function FormCreate(props) {
    // `this` inside this function refers to itself,
    // i.e `this === FormCreate`

    // since state isn't declared in FormCreate, 
    // FormCreate.state is undefined

    let formMake = React.createElement(
      "label",
      { key: props.id },
      React.createElement("br", {}),
      React.createElement("input", {
        // ...
        onChange: e => this.setState({ value: e.target.value }),
        value: this.state.value // or FormCreate.state.value
      })
    );

    return formMake;
  }
}

Solution

Extract FormCreate() as method and put it outside render(). And don't ever forget to return a React Element or null in the render() method.

If you want the full code, ask in the comments, but since you're learning, I want you to try by yourself.

Upvotes: 1

Salmin Skenderovic
Salmin Skenderovic

Reputation: 1720

You shouldn't have the functions declared inside render. Move them inside the class and skip the "function"-prefix so that they belong to your class.

Then change all references to them to this.FormCreate for example.

Besides that I changed the way you set state to the inputs name, like this:

this.setState({
        [e.target.name]: e.target.value
      }),

Here's your code but with the fixes implemented:

'use strict'

let d = React.createElement;

class FormFind extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      fname: "",
      lname: "",
      email: "",
      phone: "",
      numtravelers: "",
      datetrip: ""
    };

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
    
  	this.formContent = this.FormDisplay();
  }


  handleChange = (event) => {
    this.setState({
      [e.target.name]: e.target.value
    });
  }

  handleSubmit(event) {
    alert('Your information is: ' + JSON.stringify(this.state));
    event.preventDefault();
  }

  FormCreate(props) {
    let formMake = React.createElement("label", {
      key: props.id
    }, React.createElement("br", {}, ), (React.createElement("input", {
      type: "text",
      name: props.item.text,
      placeholder: props.item.placeholder,
      size: "auto",
      required: "required",
      onChange: (e) => this.setState({
        [e.target.name]: e.target.value
      }),
      value: this.state.value
    })))

    return formMake
  }

  FormDisplay() {
    let props = [{
        id: 10,
        text: "fname",
        placeholder: "Please Enter Your First Name"
      },
      {
        id: 11,
        text: "lname",
        placeholder: "Please Enter Your Last Name"
      },
      {
        id: 12,
        text: "email",
        placeholder: "Please Enter Your Email"
      },
      {
        id: 13,
        text: "phone",
        placeholder: "Please Enter Your Phone Number"
      },
      {
        id: 14,
        text: "numtravelers",
        placeholder: "Please Enter The Number of Children and Adults Going"
      },
      {
        id: 15,
        text: "datetrip",
        placeholder: "Please Enter Your Date For The Trip"
      }
    ];
    var listItems = props.map((item) => this.FormCreate({
      key: props.id,
      item: item
    }));

    return (
      React.createElement("div", {}, listItems));
  }


  render() {

    return React.createElement("form", {
      onSubmit: this.handleSubmit
    }, (this.formContent), React.createElement("input", {
      type: "submit",
      value: "Submit"
    }));

  }
}

const domContainer44 = document.getElementById("formfind");

ReactDOM.render(
  (d(FormFind)),
  domContainer44);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="formfind"></div>

Upvotes: 1

Related Questions