-1

I want to change the state on click of a button in React. Essentially I am trying to update the value of the name displayed in my div by setting this.state.name equal to whatever is typed in the text-box.

What am I doing wrong here?

I also don't quite understand the difference between onClick={() => this.updateText()} and onClick={this.updateText()} and/or onClick={this.updateText}. Maybe it's related to that?

Form.tsx

import React from 'react';

export default class Form extends React.Component<any, any> {

  constructor(props: any) {
    super(props);

    this.state = {
      title: "Enter a new name here",
      description: "Hola",
      textInput: null,
      name: this.props.name
    };
  }

  updateText() {
    this.setState({
      name: this.state.textInput
    });
  }

  render() {
    return (
      <div className="App">
        <div>{this.props.text} {this.state.name}</div>
        <div>{this.state.age}</div>
        <input type="text" placeholder={this.state.title}>{this.state.textInput}</input>
        <br />
        <button type="submit" onClick={() => this.updateText()}>Submit</button>
      </div>
    );
  }
}

App.tsx

import React from 'react';
import './App.css';
import Form from "./Form";

class App extends React.Component {
  render() {
    return (
      <div className="App">
        <Form text="Hello" age={22} name="Thomas"/> 
      </div>
    );
  }
}

export default App;

this.state.name comes up as empty or null upon typing something and clicking the submit button.

devserkan
  • 16,870
  • 4
  • 31
  • 47
MisterTams
  • 95
  • 2
  • 9
  • 1
    You need to add a change handler to the input itself. The value remains null because you aren’t actually updating state as changes happen. https://reactjs.org/docs/forms.html. Also inputs are not block elements. They don’t have children or equivalent. They are self closing. – Alexander Staroselsky Jun 01 '19 at 23:23

1 Answers1

0

As @Alexander Staroselsky said in his comment, you should use an onChange handler for your input. This way you can update the textInput and set it to your name value.

{this.updateText()} is not a proper way of using the handler. If you use it like that it is invoked on every render but not with the click.

onClick={this.updateText} Here, you can use the function reference and it works. But, either you have to bind it in the constructor or define it as an arrow function. You can see an example below.

onClick={() => this.updateText()} This is an option of using it. You are using here an arrow function for your handler. It works also without binding your handler function for this. But, with this way your handler function is recreated with every render. Avoid this if possible.

class App extends React.Component {
  render() {
    return (
      <div className="App">
        <Form text="Hello" age={22} name="Thomas"/> 
      </div>
    );
  }
}

class Form extends React.Component {

  constructor(props) {
    super(props);

    this.state = {
      title: "Enter a new name here",
      description: "Hola",
      textInput: "",
      name: this.props.name
    };
  }

  updateText = () => {
    this.setState({
      name: this.state.textInput
    });
  }

  handleChange = (e) => this.setState({ textInput: e.target.value})

  render() {
    return (
      <div className="App">
        <div>{this.props.text} {this.state.name}</div>
        <div>{this.state.age}</div>
        <input value={this.state.textInput} onChange={this.handleChange} type="text" placeholder={this.state.title} />
        <br />
        <button type="submit" onClick={this.updateText}>Submit</button>
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root" />
devserkan
  • 16,870
  • 4
  • 31
  • 47
  • That works! What I find interesting is the two different ways to do this. In my opinion I prefer this following approach which is a slight syntactical change compared to you. But at least for me it seems to be a little more concise. I wonder if the performance is the same? – MisterTams Jun 02 '19 at 00:09
  • this.handleChange(e)}/>
    – MisterTams Jun 02 '19 at 00:10
  • updateText() { this.setState({ name: this.state.textInput, textInput: "" }); } handleChange(e: any) { this.setState({ textInput: e.target.value}) } – MisterTams Jun 02 '19 at 00:10
  • @MisterTams, with your way the handler functions will be recreated at every render. You can read [this answer](https://stackoverflow.com/a/52788669/7060441) to see the difference. (By the way, the accepted answer is a misleading one, you can read the comments). But, for small applications (I mean if there aren't so many components which have functions like those) the performance will be nearly the same. – devserkan Jun 02 '19 at 01:22
  • I'm still not quite sure I understand. So your code snippet isn't the correct way to approach this either? Well is it to pass a handler as a prop from the Parent to the Child component? The () => syntax confuses me because I don't come from a React or JavaScript background. – MisterTams Jun 02 '19 at 03:02
  • My example is the right approach as your last code.Using `{() => someFunc()}` causes a recreation in every render since you use an arrow function in the handler.But `{someFunc}` is an example of using by reference and does not trigger a recreation. Your example is also OK since you use functions by their reference. The only difference between your example and mine is you use regular functions and bind them in the constructor and I use arrow functions, so no need to bind them for the `this` context.I know,there are different subjects here. But, after a while, you will understand the difference. – devserkan Jun 02 '19 at 03:31
  • So basically my slight different approach using the arrow function in the handler such as onChange={(e) => this.handleChange(e)} will cause a call to this function any time anything in this component needs re-rendering. Your code in the answer I chose as accepted will do the same result but will not call the onChange or onClick automatically each and every time something in this component needs re-rendering. Therefore, your initial answer does the same effect but is slightly better in performance. Is this correct? I'll read the link you provided as well. – MisterTams Jun 02 '19 at 03:39
  • Not exactly "call" or "invoke" but a "recreation". This does not affect the component itself but triggers an unnecessary rerender for child components. I tried to explain this in the first link I gave in my first comment here. So, besides the word "call", you are thinking right. (By the way, `{someFunc()}` invokes or calls the function immediately but onClick handlers do not work like that. So, you never use the handler like this way.) – devserkan Jun 02 '19 at 04:04
  • 1
    Awesome! I've been reading up on this the last few minutes. You've been a great help! Thanks! – MisterTams Jun 02 '19 at 04:08