2

I have a small app with three components

  1. parent => App
  2. Son => Courses
  3. Grandchild => Course

The app state lies in the App component, in the Course component I have an input field with an onChange event that spouse to change the state in the app component, the problem is that every time I type the input changes to a string and I can't keep typing and changing the state.

The values arrive to the parent.

This is my Course code

import React, { Component } from 'react'

class Course extends Component {
    updatedGrade = (e) => {
        this.props.updateCourseGrade(Number(e.target.value), Number(e.target.id));

    };
    render() {
        const {id, courseType, courseName, courseGrade} = this.props.course;
        return (
            <tr key={id}>
                <td>
                    {courseName}
                </td>
                <td> 
                    {(courseType ? 'a' : 'm' )}
                </td>
                <td>
        {(courseGrade !== '' 
            ? courseGrade 
            : <input 
                type="number"
                id={id}
                onChange={this.updatedGrade}
                value={courseGrade}
            /> 
        )}
                </td>
                <td>
                    <button 
                        onClick={this.props.removeCourse.bind(this, id)}
                        style={btnStyle}
                    >
                        remove
                    </button>
                </td>
            </tr>
        )
    }
}

this is my App relevant code:

    class App extends Component {
  state = {
    courses: [
      {
        id: 1,
        courseName: 'bioliogy,
        courseType: false,
        courseHours: 10,
        courseGrade: ''
      },{
        id: 2,
        courseName: 'Mahematics,
        courseType: true,
        courseHours: 20,
        courseGrade: ''
      },{
        id: 3,
        courseName: 'History,
        courseType: false,
        courseHours: 30,
        courseGrade: 50
      }
    ]
  };



  updateCourseGrade(courseGrade, id){

    //const courseGradeNum = Number(courseGrade);
    this.setState({
      courses: this.state.courses.map(course => course.id === id ? {...course, courseGrade } : course)
    })
  console.log('courseGrade ', courseGrade);

Now, when I do this:

 updateCourseGrade(courseGrade, id){

    const courseGradeNum = Number(courseGrade);
    this.setState({
      courses: this.state.courses.map(course => course.id === id ? {...course, courseGradeNum } : course)
    })

The state will get a new value while typing named courseGrade and I don't want this. as well the courseGrade is already defined as a Number in the Course component

What can I do? maybe I shouldn't use value in the course component?

UPDATE

According to Freeman Lambda request, this is the state after I change the value in the input field, the state of courseGrade of the desired course changes. but because the input field disappears I cannot keep typing.

Link to a video that shows what happens https://www.screencast.com/t/Cyz1v6zMWsq

DavSev
  • 1,005
  • 4
  • 22
  • 47

3 Answers3

1

Here:

    {(courseGrade !== '' 
            ? courseGrade 
            : <input 
                type="number"
                id={id}
                onChange={this.updatedGrade}
                value={courseGrade}
            /> 
        )}

You explicitely change the input to a plain string a soon as courseGrade is !== ''

if you want to be able to keep typing you have to stick with an input during typing. If you want the input to disapear after typing you will have to add a button controlling a state that removes the input, for example:

class Course extends Component {
    state = {
        gradeValidated: false,
    }
    updatedGrade = (e) => {
        this.props.updateCourseGrade(Number(e.target.value), Number(e.target.id));

    };
    toggleGradeInput = (e) => {
        this.setState((state) => ({ gradeValidated: !state.gradeValidated }));

    };
    render() {
        const {id, courseType, courseName, courseGrade} = this.props.course;
        return (
            <tr key={id}>
                <td>
                    {courseName}
                </td>
                <td> 
                    {(courseType ? 'a' : 'm' )}
                </td>
                <td>
        {(this.state.gradeValidated 
            ? courseGrade 
            : <input 
                type="number"
                id={id}
                onChange={this.updatedGrade}
                value={courseGrade}
            /> 
        )}
                </td>
                <td>
                    <button 
                        onClick={this.toggleGradeInput}
                        style={btnStyle}
                    >
                        toggle input
                    </button>
                    <button 
                        onClick={this.props.removeCourse.bind(this, id)}
                        style={btnStyle}
                    >
                        remove
                    </button>
                </td>
            </tr>
        )
    }
}
remix23
  • 2,632
  • 2
  • 11
  • 21
1

The problem is here

courseGrade !== '' 
            ? courseGrade 
            : <input 
                type="number"
                id={id}
                onChange={this.updatedGrade}
                value={courseGrade}
            /> 

the first condition is set to true if you set any state instead of '', so my idea is to use a save button for the grate and onChange keep on a local state.

victor zadorozhnyy
  • 889
  • 1
  • 12
  • 25
0

You should use an updater function for this, I think what is happening is that state updates are getting batched.

So instead of

this.setState({
      courses: this.state.courses.map(course => course.id === id ? {...course, courseGradeNum } : course)
    })

try

this.setState( prevState => ({
      courses: prevState.courses.map(course => course.id === id ? {...course, courseGradeNum } : course)
    }))

If this wasn't the problem can you create a code sandbox because there just isn't enough code to understand what is going on. For example, updateCourseGrade is being bound anywhere. don't know if you haven't or if you are just not showing it.

Josh Pittman
  • 7,024
  • 7
  • 38
  • 66