Search code examples
javascriptreactjsdom-eventsuser-input

Grabbing the value of a form input method for a possible arithmetic operation in ReactJS


This is my first question here after years, so pardon me if I break any forum/platform rule.

I am trying to build a CGPA CALCULATOR so I am having an issue updating a variable on user input change.

I am a beginner so my code and description may be watery. The problem is with my handleChange method I guess, because every time I make an input (I am testing with the courseInput for now), the app crashes with the error:

TypeError: Cannot read property 'setState' of undefined

Someone should please explain to me in details.

I have actually tried a lot Googling but nothing seems wrong with my code.

import React, { Component } from 'react';
  import './App.css';
class App extends Component {

    constructor(props) {
    super(props);
    // this.courseInput = React.createRef();
    this.state = {
      courseInput: [],
        courseCode: '',
        courseUnit: [0],
        courseGrade: [],
      totalPoint: 0,
      totalUnit: 0,
      newCourseInput: <form>
        <input onChange={this.handleChange} type="text" placeholder='COURSE CODE' value={this.courseCode} />
        {/* <input type="number" placeholder='COURSE UNIT' ref={this.courseUnit} value={this.courseUnit} />
        <input type="number" placeholder='COURSE GRADE' ref={this.courseGrade} value={this.courseGrade} /> */}
      </form>
    };
    this.createAnother = this.createAnother.bind(this);
    this.handleChange = this.handleChange.bind(this);
  }

  // THIS createAnother TAKES THE CURRENT STATE OF courseInput AND CONCATENATES IT WITH THE newCourseInput TO MAKE LIST
  createAnother() {
    var courseInput = this.state.courseInput.concat(this.state.newCourseInput)
    this.setState({ courseInput })
  }

  handleChange(event) {
    var updatedCourseCode = event.target.value;
    this.setState({ courseInput: updatedCourseCode }, () => console.log(this.state))

  }

  
  render() {
    // console.log(this);
    // var courseInput = this.state.courseInput;
  return(
    <div>
      <header className="App-header">
        <p>
         THIS IS A CGPA CALCULATOR
        </p>
      </header>
        {/* MAP FUNCTION LOOPS THROUGH THE ARRAY courseInput AND PRINTS OUT THE CODE UNIT AND GRADE IN IN ORDERED LIST */}
        <ol>
        {this.state.courseInput.map((courseInput, index) => 
            <li key={index}>{courseInput}</li>
          
            )}
        </ol>
      
      {/* THIS TRIGGERS AN EVENT HANDLER createAnother LOCATED UP THERE */}
        <button onClick={this.createAnother} >ADD ANOTHER COURSE</button>
      
  </div>
  );
  }
  }
  
  
  export default App;

Solution

  • You should not store jsx elements in your state, but only the necessary data to render these elements later when needed. you also have a mistakes(you tried to assign string to an courseInput whice is array).

    import React, { Component } from "react";
    // import './App.css';
    class App extends Component {
      constructor(props) {
        super(props);
        // this.courseInput = React.createRef();
        this.state = {
          courseInput: [],
          courseCode: "",
          courseUnit: [0],
          courseGrade: [],
          totalPoint: 0,
          totalUnit: 0,
        };
      }
    
      // THIS createAnother TAKES THE CURRENT STATE OF courseInput AND CONCATENATES IT WITH THE newCourseInput TO MAKE LIST
      createAnother = () => {
        var courseInput = this.state.courseInput.concat({
          id: this.state.courseInput.length,
          value: "",
        });
        this.setState({ courseInput });
      };
    
      handleCourseChange = (value, id) => {
        const newCourseInputs = [...this.state.courseInput];
        console.log(newCourseInputs);
        console.log(value, id);
        let courseToChange = newCourseInputs.find((c) => c.id == id);
        courseToChange.value = value;
        this.setState({ courseInput: newCourseInputs });
      };
    
      render() {
        // console.log(this);
        // var courseInput = this.state.courseInput;
        console.log(this.state.courseInput);
        return (
          <div>
            <header className="App-header">
              <p>THIS IS A CGPA CALCULATOR</p>
            </header>
            {/* MAP FUNCTION LOOPS THROUGH THE ARRAY courseInput AND PRINTS OUT THE CODE UNIT AND GRADE IN IN ORDERED LIST */}
            <ol>
              {this.state.courseInput.map((courseInput, index) => (
                <li key={index}>
                  <input
                    onChange={(e) =>
                      this.handleCourseChange(e.target.value, courseInput.id)
                    }
                    type="text"
                    placeholder="COURSE CODE"
                    value={courseInput.value}
                  />
                </li>
              ))}
            </ol>
            {/* THIS TRIGGERS AN EVENT HANDLER createAnother LOCATED UP THERE */}
    
            <button onClick={this.createAnother}>ADD ANOTHER COURSE</button>
          </div>
        );
      }
    }
    
    export default App;
    
    

    this code will probably work as you intended.