Binding Functions and Enable/Disable State in HTML Buttons with React.js
By using a boolean state value that governs visibility of UI components, React.js's state management can tell a user something’s happening after a button click.
Sep 21, 2020 • 7 Minute Read
Introduction
React.js's state management approach allows systematic implementation of a wide variety of user experiences. One such user experience technique is telling the user that something is happening or is loading in the background when a button is clicked. To achieve this, we'd like to have a boolean state value that governs the visibility of user interface components. That is, if the state value is true then the components are disabled. Otherwise, the components are disabled. That same value can also be used to determine if a section of the app will display a loading message or any visual component for that matter.
Defining the State Value
Supposed you're creating a SearchComponent that contains two major input elements:
- A text input where users can type in a search query
- A submit button that is a call to action to start searching
You'll then control the disabled attribute of these two elements with an isDisabled boolean state.
Establishing State
In your component's constructor, go ahead and define the isDisabled state with an initial value of false. This tells the user that they are free to input stuff in the text input and click the submit button.
import React from 'react';
export default class SearchComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
isDisabled: false
}
}
}
Binding State to UI
Create your component's render() method that returns the form itself with the two UI elements. Copy the following function and paste it within the SearchComponent. In practice, it's usually found as the last method of a React.js component.
render() {
return (
<div>
<input type="text" disabled={this.state.isDisabled} />
<button disabled={this.state.isDisabled}>
Submit Query
</button>
</div>
);
}
Embed the value of this.state.isDisabled as a value of the disabled attribute via JSX as wrapped by {}. Since its initial value is false, users can then proceed to input text in it.
<input type="text" disabled={this.state.isDisabled} />
The same goes for the button for submitting a query.
<button disabled={this.state.isDisabled}>
Submit Query
</button>
Disabling the Interface
The next thing you have to do is to bind a function that will update the state value of a component by calling this.setState(), where this is a reference to the instance of the React.js component. Remember that in React.js, a call to this.setState() will force a re-render of the component with the newly updated values. The function will look like the following:
handleSubmitClicked() {
this.setState({
isDisabled: true
});
}
Binding to an Event
Given your event handler, you'll bind the function to your button's onClick attribute:
<button
disabled={this.state.isDisabled}
onClick={this.handleSubmitClicked.bind(this)}
>
Submit Query
</button>
Invoke the .bind(this) function in order to retain the value for this, which is a reference to the instance of the component. This way (pun not intended), the this inside the logic of handleSubmitClicked() will always retain the value of the component's instance, thus allowing the setState() to be called from it.
Cascading Effect
Since handlSubmitClicked() updates the state of isDisabled to true, it will force a call to the render() method of the component, causing the input elements' disabled attribute to have a value of true. As a result, this will create an effect that tells the user that something is happening after the button was clicked.
Re-enabling the Interface
Once the process is finished, you can tell the user that they're free to input or modify the current text field. Do this by creating another event handler function that does the opposite—that is, it enables the UI components:
enableComponents() {
this.setState({
isDisabled: false
});
}
Simulating a Process
To simulate that "something is happening," add code in the handleSubmitClicked() that waits three seconds before firing a function which in turn will call enableComponents().
handleSubmitClicked() {
this.setState({
isDisabled: true
});
setTimeout(
function() {
this.enableComponents()
}.bind(this),
3000
);
}
Notice that you'll call .bind(this) once again against the function that is the first argument to setTimeout(). This will allow this to retain its value as the reference to the instance of this component, allowing a call to be made to enableComponents(). Normally, this timeout code will be replaced by actual processing of the user input, which will eventually call enableComponents() once done.
Overall Code
Your final component form will look like the following:
import React from 'react';
export default class SearchComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
isDisabled: false
}
}
handleSubmitClicked() {
this.setState({
isDisabled: true
});
setTimeout(
function() {
this.enableComponents()
}.bind(this),
3000
);
}
enableComponents() {
this.setState({
isDisabled: false
});
}
render() {
return (
<div>
<input type="text" disabled={this.state.isDisabled} />
<button
disabled={this.state.isDisabled}
onClick={this.handleSubmitClicked.bind(this)}
>
Submit Query
</button>
</div>
);
}
}
Try this component out and notice that upon clicking Submit Query, both the input and button elements will disable, and after three seconds will be enabled again.
Conclusion
This guide demonstrated a simple way to communicate to a user that something is happening after clicking a button by maintaining a boolean state variable that serves as a value for the visibility of the UI. Try to see if you can incorporate this technique to your own stateful React component by having a single state variable govern the accessibility of your interface. Some suggestions are:
- Using the state variable to make a loading text or visualization appear if the value is true. Otherwise, hide the text or visualization.
- Invoking enableComponents() after a successful AJAX call within handleSubmitClicked()