Why can't I change my input value in React even with the onChange listener

JavascriptReactjsStateOnchange

Javascript Problem Overview


I am quite new to React and after going through some tutorials, I was trying the below code of mine.

I made one component, passed props to it from a store, on componentWillMount I make a new state for component. Rendering is fine till now.

Next I bound my state to value of an input box and I have onChange listener as well. Still, I can't change my values in the field.

Since, I am from Angular background, I am assuming binding input's value to state like below will automatically update the property name in state object. Am I wrong here?

componentWillMount(){
    this.setState({
	    updatable : false,
    	name : this.props.name,
    	status : this.props.status
    });
}

//relevant DOM from component's render function
<input className="form-control" type="text" value={this.state.name} id={'todoName' + this.props.id} onChange={this.onTodoChange.bind(this)}/>

onTodoChange(){
    console.log(this);
    //consoling 'this' here, shows old values only.
    //not sure how and even if I need to update state here.
    // Do I need to pass new state to this function from DOM
    //TODO: send new data to store
}

My onTodoChange function console the value of this which has same value of state as while initializing. How do I go about changing state by typing in input boxes, so that I can send them to the stores?

Javascript Solutions


Solution 1 - Javascript

Unlike in the case of Angular, in React.js you need to update the state manually. You can do something like this:

<input
    id={'todoName' + this.props.id}
    className="form-control"
    type="text"
    value={this.state.name}
    onChange={e => this.onTodoChange(e.target.value)}
/>

And then in the function:

onTodoChange(value){
        this.setState({
             name: value
        });
    }

Also, you can set the initial state in the constructor of the component:

  constructor (props) {
    super(props);
    this.state = {
        updatable: false,
        name: props.name,
        status: props.status
    };
  }

Solution 2 - Javascript

You can do shortcut via inline function if you want to simply change the state variable without declaring a new function at top:

<input type="text" onChange={e => this.setState({ text: e.target.value })}/>

Solution 3 - Javascript

I think it is best way for you.

You should add this: this.onTodoChange = this.onTodoChange.bind(this).

And your function has event param(e), and get value:

componentWillMount(){
        this.setState({
            updatable : false,
            name : this.props.name,
            status : this.props.status
        });
    this.onTodoChange = this.onTodoChange.bind(this)
    }
    
    
<input className="form-control" type="text" value={this.state.name} id={'todoName' + this.props.id} onChange={this.onTodoChange}/>

onTodoChange(e){
         const {name, value} = e.target;
        this.setState({[name]: value});
   
}

Solution 4 - Javascript

If you would like to handle multiple inputs with one handler take a look at my approach where I'm using computed property to get value of the input based on it's name.

import React, { useState } from "react";
import "./style.css";

export default function App() {
  const [state, setState] = useState({
    name: "John Doe",
    email: "[email protected]"
  });

  // We need to spread the previous state and change the one we're targeting, so other data cannot be lost.
  const handleChange = e => {
    setState(prevState => {
      ...prevState,
      [e.target.name]: e.target.value,
    });
  };

  return (
    <div>
      <input
        type="text"
        className="name"
        name="name"
        value={state.name}
        onChange={handleChange}
      />

      <input
        type="text"
        className="email"
        name="email"
        value={state.email}
        onChange={handleChange}
      />
    </div>
  );
}

Solution 5 - Javascript

In React, the component will re-render (or update) only if the state or the prop changes.

In your case you have to update the state immediately after the change so that the component will re-render with the updates state value.

onTodoChange(event) {
        // update the state
    this.setState({name: event.target.value});
}

Solution 6 - Javascript

In react, state will not change until you do it by using this.setState({});. That is why your console message showing old values.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSaurabh TiwariView Question on Stackoverflow
Solution 1 - JavascriptCésar LandesaView Answer on Stackoverflow
Solution 2 - JavascriptWinston James Dale FaleView Answer on Stackoverflow
Solution 3 - Javascriptjoshua.kimView Answer on Stackoverflow
Solution 4 - JavascriptKrzysztof PodmokłyView Answer on Stackoverflow
Solution 5 - JavascriptStateLessView Answer on Stackoverflow
Solution 6 - JavascriptRohan VeerView Answer on Stackoverflow