Introduction

React renders component based on any changes on its state (i.e. data).

Updating State with Form Components

In React, a form input component controlled by it is known as controlled component. These components maintains their own state and update itself based on user input as depicted by the following code:

Code 1 - Controlled Text Input
const MessageView = (props) => {
  return(
    <h1>{props.message}</h1>
  );
}

class App extends React.Component {
  state = {
    message : ""
  };

  handleMsgChange = (event) => {
    this.setState({
      message: event.target.value
    });
  }
  
  render() {
    return(
        <div>
          <input type="text" placeholder="Type Something" value={this.state.message} onChange={this.handleMsgChange}/>
          <MessageView message={this.state.message}/>
      </div>
    );
  }
}

ReactDOM.render(<App />, mountNode);

The App component above has a message attribute in state as seen in the following snippet:

Snippet 1 - App Component State
state = {
  message : ""
};

We can bound the message attribute to the text input via the value attribute and listen to the changes on itself using the onChange attribute which in turn updates the state of the message like the following snippet:

Snippet 2 - Controlled Text Input
<input type="text" placeholder="Type Something" value={this.state.message} onChange={this.handleMsgChange}/>

The handleMsgChange that is bound to text input onChange attribute must update the message state of the React App component and could have the following implementation. This makes the React state as the single source of truth.

Snippet 3 - handleMsgChange Implementation
handleMsgChange = (event) => {
  this.setState({
    message: event.target.value
  });
}

This particular implementation will also trigger the re-render of the MessageView component since it is also dependent to the state of the message as we can see in the following snippet:

Snippet 4 - MessageView listening to the State of the Message
<MessageView message={this.state.message}/>

Updating State that may be Asynchronous

React might batch multiple setState function calls into a single update for performance. Thus the correct way to change the React state is by using the setState function that accepts a function instead of object like the following snippet:

Snippet 5 - Updating the React State
this.setState((prevState, props) => {
   //Do the state update here.
})

Let us try to create a reusable ButtonClear component that will clear value of the message attribute with the following snippet.

Snippet 6 - ButtonClear Component
const ButtonClear = (props) => {
  if (props.visible) {
    return(
        <button onClick={props.handleClear}>Clear</button>
    );
  }
  return(null); //Returns nothing
}

Based on the preceding snippet the ButtonClean component will only display the clear button if we tell it to make it visible via its visible attribute. Moreover, it is also expecting to have a handleClear implementation that will be bound to button's onClick attribute.

The handleClear implementation will be provided by the App component like the following snippet with the recommended usage of the setState() function:

Snippet 7 - App Component handleClear Implementation
handleClear = () => {
  this.setState((prevState, props) => {
    return {message: ""}; //Clears the message
  });
}

Thus we can use the ButtonClear component in the App component like the following snippet:

Snippet 8 - Using the ButtonClear in the App Component
<ButtonClear handleClear={this.handleClear} visible={this.state.message!=""}/>

We also instructed it to make the clear button visible only if the message is not empty.

The updated complete code with ButtonClear component can be seen as follows:

Code 2 - The Complete Code
const MessageView = (props) => {
  return(
    <h1>{props.message}</h1>
  );
}

const ButtonClear = (props) => {
  if (props.visible) {
    return(
        <button onClick={props.handleClear}>Clear</button>
    );
  }
  return(null);
}

class App extends React.Component {
  state = {
    message : ""
  };

  handleMsgChange = (event) => {
    this.setState({
      message: event.target.value
    });
  }
  
  handleClear = () => {
    this.setState((prevState, props) => {
      return {message: ""}; //Clears the message.
    });
  }
  
  render() {
    return(
        <div>
          <input type="text" placeholder="Type Something" value={this.state.message} onChange={this.handleMsgChange}/>
          <ButtonClear handleClear={this.handleClear} visible={this.state.message!=""}/>
          <MessageView message={this.state.message}/>
      </div>
    );
  }
}

ReactDOM.render(<App />, mountNode);

Note: We can run both Code 1 and Code 2 using jsComplete Playground