1
votes

I am learning React and came across this situation. Basically I have a React component Test with a input box and inside it I have another component Hello which print out Hello + all the text user types in . So what I expect is if i type sth then this.text will be updated and then Hello component will re-render. However this is not happening. I realize that I need to set the "text" in this.state then Hello will re-render. My question is why the Hello component is not re-render although its props is changed ? Thanks in advance.

import ReactDOM from "react-dom";
import React, { Component } from "react";

import Hello from "./hello";

class Test extends Component {
  constructor(props) {
    super(props);          
    this.handleChange = this.handleChange.bind(this);  
  }    


  handleChange(e) {     
    this.text = e.target.value;    
    console.log(this.text);   
  }

  render() {
    return (
      <div>
        <h1>Test</h1>
        <form>
          <input value={this.text} onChange={e => this.handleChange(e)} />
          <button>Add</button>          
        </form>
        <Hello name={this.text} />
      </div>
    );
  }
}

and the Hello.js component is

export default ({ name }) => <h1>Hello {name}!</h1>;
4

4 Answers

2
votes

There are 2 problems with your code. First, you need to have state in your constructor. Second , you need to set state with setState provided by React.

import ReactDOM from "react-dom";
import React, { Component } from "react";

import Hello from "./hello";

class Test extends Component {
  constructor(props) {
    super(props); 
    // This is how you set state
    this.state = {  
      text:''
    }         
    this.handleChange = this.handleChange.bind(this);  
  }    


  handleChange(e) {
    // This is how you update state
    this.setState({text: e.target.value})     
    console.log(this.text);   
  }

  render() {
    return (
      <div>
        <h1>Test</h1>
        <form>
          <input value={this.state.text} onChange={e => this.handleChange(e)} />
          <button>Add</button>          
        </form>
        <Hello name={this.state.text} />
      </div>
    );
  }
}

Here is a documentation where you can read more about it.

1
votes

Here you're modifying an instance field (this.text)instead of setting state with setState().

e.g. your code, simplified:

import React, { Component } from "react";

class App extends Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = { text: "" };
  }

  handleChange(e) {
    this.setState({text:e.target.value});
  }

  render() {
    return (
      <div>
        <h1>Test</h1>
        <form>
          <input value={this.state.text} onChange={e => this.handleChange(e)} />
          <button>Add</button>
        </form>
        {this.state.text}
      </div>
    );
  }
}
export default App;

0
votes

You'll need to use setState instead of setting the value to a local var. A render automatically follows a setState.

class App extends React.Component {
  constructor(props) {
    super(props);

    // Add state to the constructor
    this.state = { text: '' };
    this.handleChange = this.handleChange.bind(this);  
  }    


  handleChange(e) {     

    // Set the value to the state text property
    this.setState({ text: e.target.value });
  }

  // Use `this.state.text` instead of this.text
  render() {
    return (
      <div>
        <h1>Test</h1>
        <form>
          <input value={this.state.text} onChange={this.handleChange} />
          <button>Add</button>          
        </form>
        <Hello name={this.state.text} />
      </div>
    );
  }
}


function Hello({ name }) {
  return <h1>Hello {name}!</h1>;
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);
<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"></div>
0
votes

This can be handled by setting up state on your component. You need to fix this in your Test Component.

Set Text Value this.setState({ text: '' })

Get Text value this.state.text

The Reason to use this.handleChange = this.handleChange.bind(this) is to access this.setState within the handleChange.

Read more about state in React.

    import React, { Component } from "react";
    import Hello from "./Hello";

    class Test extends Component {
        constructor(props) {
        super(props);

        this.state = {
          text: ''
        }

        this.handleChange = this.handleChange.bind(this);
      }

      handleChange(e) {
        this.setState({
            text: e.target.value
        });
      }

    render() {
      return (
        <div>
          <h1>Test</h1>
          <form>
            <input value={this.state.text} onChange={e => this.handleChange(e)} />
            <button>Add</button>
          </form>
          <Hello name={this.state.text} />
        </div>
      );
    }
  }

  export default Test;