Handling Events With Callbacks: Prevent Default Behavior in React

01/30/20191 Min Read — In React.js

The React documentation explains how to handle HTML forms and events. Let's have a look.

Imagine you have an HTML form with an <input> field: the browser will open a new page when the user submits the form.

In React, we often don't want that because we handle our state as part of the main component and we mutate it with setState(). This is a "controlled component".

Here's an example:

App.js

...

class App extends Component {
  state = {
    // some state
    route: "signin"
  };

...

  onRouteChange = (route) => {
    this.setState(() => ({ route: route }));
 }

 ...

 render() {
    return (
    ...
    <Signin onRouteChange={this.onRouteChange} />
    ...

  }
}

The App component passes its method onRouteChange as a prop to the SignIn component.

Signin.js

const Signin = ({ onRouteChange }) => {
  return (
 ...
    <article>
      <main>
        <form className>
          <div className>
            <input
              type="submit"
              value="Sign in"
              onClick={() => onRouteChange("home")}
            />
          </div>
      </main>
    </article>
...
  );
};

When the user clicks on the input field, it fires off the event handler onClick - a Synthetic Event. But with the default behavior, the form tries to connect with a method, i.e. a POST method to send the data to the backend server.

We now have an error: "Form submission canceled because the form is not connected."

You can prevent this HTML behavior with preventDefault() and the docs even have an example.

from the official documentation:

function ActionLink() {
  function handleClick(e) {
    e.preventDefault()
    console.log('The link was clicked.')
  }

  return (
    <a href='#' onClick={handleClick}>
      Click me
    </a>
  )
}

But it doesn't work out of the box when you pass the function (which is an object method of the class App as a React component) as prop to another component (the Signin component).

In your App component, add event as an additional parameter to the method and add preventDefault() in the function body:

App.js

...
  onRouteChange = (event, route) => {
    event.preventDefault();
    this.setState(() => ({ route: route }));
 }
...

Now use the magic of closures and higher order functions and change your Signin onClick handler:

Signin.js

const Signin = ({ onRouteChange }) => {
  return (
 ...
    <article>
      <main>
        <form className>
          <div className>
            <input
              type="submit"
              value="Sign in"
              onClick={(event) => onRouteChange(event, "home")}
            />
          </div>
      </main>
    </article>
...
  );
};

The callback function for onClick now takes the additional parameter event and returns the onRouteChange function. This function takes the event and the route "home". When the callback function executes, onRouteChange will run event.preventDefault().

Some things are complicated in Javascript land.