1

After changing username and password in the inputs, the properties of my state become 'undefined'. I just can't get my head around it: for some reason this.setState doesn't seem to work.

This is the code:

import * as React from 'react';
import { Link, RouteComponentProps } from 'react-router-dom';
import { connect } from 'react-redux';
import { ApplicationState } from '../store';
import * as LogonStore from '../store/LogonStore';

type LogonProps =
    LogonStore.LogonState
    & typeof LogonStore.actionCreators
    & RouteComponentProps<{}>;

class Logon extends React.Component<any, any> {
    public constructor(props) {
        super(props);
        this.state = { un: '', pw: '' };
        this.handleChange = this.handleChange.bind(this);      
    }

    handleChange(event) {
        console.log(event.target.value); // works fine
        this.setState({ un: event.target.un, pw: event.target.pw });
        console.log(this.state); //first  un: '', pw: '' then un: undefined, pw: undefined
    }

    public render() {
        return <div >
            <h1>Logon</h1>
            <label>username</label><input value={this.state.un} onChange={this.handleChange} />
            <label>password</label><input value={this.state.pw} onChange={this.handleChange} />
            <br />         
        </div>;
    }
}


export default connect(
    (state: ApplicationState) => state.logon, // Selects which state properties are merged into the component's props
    LogonStore.actionCreators                 // Selects which action creators are merged into the component's props
)(Logon) as typeof Logon;

I'm missing something fundamental here...

Thanks!

Ole EH Dufour
  • 2,968
  • 4
  • 23
  • 48

3 Answers3

2

You can only handle the change of one input at a time. Here is the problem code:

this.setState({ un: event.target.un, pw: event.target.pw });

When you type in a form field, your handleChange function will get called. The event.target is the HTML input element of the thing you just changed... meaning you cannot update the values for both username and password at the same time like you're trying to do. You can only update values one at a time. In order to facilitate this, you will need to add a "name" property to your input elements:

<input name="un" value={this.state.un} onChange={this.handleChange} />
<input name="pw" value={this.state.pw} onChange={this.handleChange} />

Then update your state like this:

this.setState({ [event.target.name]: event.target.value });
Ryan Wheale
  • 26,022
  • 8
  • 76
  • 96
  • Thanks for the clear explanation. My code wasn't that foolish. – Ole EH Dufour Feb 13 '18 at 16:16
  • Could you have a look here? https://stackoverflow.com/questions/67153957/value-becomes-undefined-because-of-setstate/67154970#67154970 –  Apr 20 '21 at 12:28
2

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state={
      un:'',
      pw:''
    }
  }
  
  handleChange=(event)=> {
    this.setState({[event.target.name]: event.target.value}, 
    () => {
      //updated state
      console.log(this.state)
    });
  }

  render() {
    return <div>
      <h1> Logon </h1> 
      <label> username </label>
      <input name="un" value={this.state.un} onChange={this.handleChange}/>
      <br /> 
      <label> password </label>
      <input name="pw" value={this.state.pw} onChange={this.handleChange}/>
    </div>
  }
}
ReactDOM.render( <App/> , document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

Explanation as asked by OP:

setState is asynchronous. It takes time to mutate the changes. So instead of checking state on next immediate line, you can log or check it as shown in the answer. For more detailed information on this please refer setState()

Dev
  • 3,922
  • 3
  • 24
  • 44
  • 1
    The abundance of copy/paste code out there with no explanation is likely why to OP doesn't understand why their code isn't working. Please try and help the person understand _why_ their code doesn't work. – Ryan Wheale Feb 13 '18 at 16:06
  • 1
    @RyanWheale, sure, I was about to edit the answer after posting the code. But saw you answer and stopped as it had explanation, and I didn't copy and paste. – Dev Feb 13 '18 at 16:09
  • 1
    @OleEHDufour, I have updated the answer, with the link for more info. – Dev Feb 13 '18 at 16:21
  • 1
    I didn't mean to imply that _you_ copied and pasted. You provided a code-only answer. It does no good to only provide code for the OP to copy/paste. Thanks for updating with an explanation. – Ryan Wheale Feb 13 '18 at 18:08
  • Could you have a look here? https://stackoverflow.com/questions/67153957/value-becomes-undefined-because-of-setstate/67154970#67154970 –  Apr 20 '21 at 12:28
0

setState is an asynchronous function. Thus, you cannot access it on the next line, but I assure you it's working. Also, if you really want to log the values from state you can either put the log inside the render method OR use the setState callback, which is the second argument of the setState function, like so:

this.setState(
  { un: event.target.value.un, pw: event.target.value.pw },
  () => console.log(this.state)
);
João Vilaça
  • 601
  • 1
  • 6
  • 13